mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
550 lines
12 KiB
550 lines
12 KiB
///////////////////////////////////////////////////////////////////////////// |
|
// |
|
// Copyright (c) 2004, Pixar Animation Studios |
|
// |
|
// All rights reserved. |
|
// |
|
// Redistribution and use in source and binary forms, with or without |
|
// modification, are permitted provided that the following conditions are |
|
// met: |
|
// * Redistributions of source code must retain the above copyright |
|
// notice, this list of conditions and the following disclaimer. |
|
// * Redistributions in binary form must reproduce the above |
|
// copyright notice, this list of conditions and the following disclaimer |
|
// in the documentation and/or other materials provided with the |
|
// distribution. |
|
// * Neither the name of Pixar Animation Studios nor the names of |
|
// its contributors may be used to endorse or promote products derived |
|
// from this software without specific prior written permission. |
|
// |
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
// |
|
///////////////////////////////////////////////////////////////////////////// |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// class Pxr24Compressor |
|
// |
|
// This compressor is based on source code that was contributed to |
|
// OpenEXR by Pixar Animation Studios. The compression method was |
|
// developed by Loren Carpenter. |
|
// |
|
// The compressor preprocesses the pixel data to reduce entropy, |
|
// and then calls zlib. |
|
// |
|
// Compression of HALF and UINT channels is lossless, but compressing |
|
// FLOAT channels is lossy: 32-bit floating-point numbers are converted |
|
// to 24 bits by rounding the significand to 15 bits. |
|
// |
|
// When the compressor is invoked, the caller has already arranged |
|
// the pixel data so that the values for each channel appear in a |
|
// contiguous block of memory. The compressor converts the pixel |
|
// values to unsigned integers: For UINT, this is a no-op. HALF |
|
// values are simply re-interpreted as 16-bit integers. FLOAT |
|
// values are converted to 24 bits, and the resulting bit patterns |
|
// are interpreted as integers. The compressor then replaces each |
|
// value with the difference between the value and its left neighbor. |
|
// This turns flat fields in the image into zeroes, and ramps into |
|
// strings of similar values. Next, each difference is split into |
|
// 2, 3 or 4 bytes, and the bytes are transposed so that all the |
|
// most significant bytes end up in a contiguous block, followed |
|
// by the second most significant bytes, and so on. The resulting |
|
// string of bytes is compressed with zlib. |
|
// |
|
//----------------------------------------------------------------------------- |
|
//#define ZLIB_WINAPI |
|
|
|
#include <ImfPxr24Compressor.h> |
|
#include <ImfHeader.h> |
|
#include <ImfChannelList.h> |
|
#include <ImfMisc.h> |
|
#include <ImfCheckedArithmetic.h> |
|
#include <ImathFun.h> |
|
#include <Iex.h> |
|
#include <half.h> |
|
#include <zlib.h> |
|
#include <assert.h> |
|
#include <algorithm> |
|
|
|
using namespace std; |
|
using namespace Imath; |
|
|
|
namespace Imf { |
|
namespace { |
|
|
|
// |
|
// Conversion from 32-bit to 24-bit floating-point numbers. |
|
// Conversion back to 32 bits is simply an 8-bit shift to the left. |
|
// |
|
|
|
inline unsigned int |
|
floatToFloat24 (float f) |
|
{ |
|
union |
|
{ |
|
float f; |
|
unsigned int i; |
|
} u; |
|
|
|
u.f = f; |
|
|
|
// |
|
// Disassemble the 32-bit floating point number, f, |
|
// into sign, s, exponent, e, and significand, m. |
|
// |
|
|
|
unsigned int s = u.i & 0x80000000; |
|
unsigned int e = u.i & 0x7f800000; |
|
unsigned int m = u.i & 0x007fffff; |
|
unsigned int i; |
|
|
|
if (e == 0x7f800000) |
|
{ |
|
if (m) |
|
{ |
|
// |
|
// F is a NAN; we preserve the sign bit and |
|
// the 15 leftmost bits of the significand, |
|
// with one exception: If the 15 leftmost |
|
// bits are all zero, the NAN would turn |
|
// into an infinity, so we have to set at |
|
// least one bit in the significand. |
|
// |
|
|
|
m >>= 8; |
|
i = (e >> 8) | m | (m == 0); |
|
} |
|
else |
|
{ |
|
// |
|
// F is an infinity. |
|
// |
|
|
|
i = e >> 8; |
|
} |
|
} |
|
else |
|
{ |
|
// |
|
// F is finite, round the significand to 15 bits. |
|
// |
|
|
|
i = ((e | m) + (m & 0x00000080)) >> 8; |
|
|
|
if (i >= 0x7f8000) |
|
{ |
|
// |
|
// F was close to FLT_MAX, and the significand was |
|
// rounded up, resulting in an exponent overflow. |
|
// Avoid the overflow by truncating the significand |
|
// instead of rounding it. |
|
// |
|
|
|
i = (e | m) >> 8; |
|
} |
|
} |
|
|
|
return (s >> 8) | i; |
|
} |
|
|
|
|
|
void |
|
notEnoughData () |
|
{ |
|
throw Iex::InputExc ("Error decompressing data " |
|
"(input data are shorter than expected)."); |
|
} |
|
|
|
|
|
void |
|
tooMuchData () |
|
{ |
|
throw Iex::InputExc ("Error decompressing data " |
|
"(input data are longer than expected)."); |
|
} |
|
|
|
} // namespace |
|
|
|
|
|
Pxr24Compressor::Pxr24Compressor (const Header &hdr, |
|
size_t maxScanLineSize, |
|
size_t numScanLines) |
|
: |
|
Compressor (hdr), |
|
_maxScanLineSize (maxScanLineSize), |
|
_numScanLines (numScanLines), |
|
_tmpBuffer (0), |
|
_outBuffer (0), |
|
_channels (hdr.channels()) |
|
{ |
|
size_t maxInBytes = |
|
uiMult (maxScanLineSize, numScanLines); |
|
|
|
size_t maxOutBytes = |
|
uiAdd (uiAdd (maxInBytes, |
|
size_t (ceil (maxInBytes * 0.01))), |
|
size_t (100)); |
|
|
|
_tmpBuffer = new unsigned char [maxInBytes]; |
|
_outBuffer = new char [maxOutBytes]; |
|
|
|
const Box2i &dataWindow = hdr.dataWindow(); |
|
|
|
_minX = dataWindow.min.x; |
|
_maxX = dataWindow.max.x; |
|
_maxY = dataWindow.max.y; |
|
} |
|
|
|
|
|
Pxr24Compressor::~Pxr24Compressor () |
|
{ |
|
delete [] _tmpBuffer; |
|
delete [] _outBuffer; |
|
} |
|
|
|
|
|
int |
|
Pxr24Compressor::numScanLines () const |
|
{ |
|
return _numScanLines; |
|
} |
|
|
|
|
|
Compressor::Format |
|
Pxr24Compressor::format () const |
|
{ |
|
return NATIVE; |
|
} |
|
|
|
|
|
int |
|
Pxr24Compressor::compress (const char *inPtr, |
|
int inSize, |
|
int minY, |
|
const char *&outPtr) |
|
{ |
|
return compress (inPtr, |
|
inSize, |
|
Box2i (V2i (_minX, minY), |
|
V2i (_maxX, minY + _numScanLines - 1)), |
|
outPtr); |
|
} |
|
|
|
|
|
int |
|
Pxr24Compressor::compressTile (const char *inPtr, |
|
int inSize, |
|
Box2i range, |
|
const char *&outPtr) |
|
{ |
|
return compress (inPtr, inSize, range, outPtr); |
|
} |
|
|
|
|
|
int |
|
Pxr24Compressor::uncompress (const char *inPtr, |
|
int inSize, |
|
int minY, |
|
const char *&outPtr) |
|
{ |
|
return uncompress (inPtr, |
|
inSize, |
|
Box2i (V2i (_minX, minY), |
|
V2i (_maxX, minY + _numScanLines - 1)), |
|
outPtr); |
|
} |
|
|
|
|
|
int |
|
Pxr24Compressor::uncompressTile (const char *inPtr, |
|
int inSize, |
|
Box2i range, |
|
const char *&outPtr) |
|
{ |
|
return uncompress (inPtr, inSize, range, outPtr); |
|
} |
|
|
|
|
|
int |
|
Pxr24Compressor::compress (const char *inPtr, |
|
int inSize, |
|
Box2i range, |
|
const char *&outPtr) |
|
{ |
|
if (inSize == 0) |
|
{ |
|
outPtr = _outBuffer; |
|
return 0; |
|
} |
|
|
|
int minX = range.min.x; |
|
int maxX = min (range.max.x, _maxX); |
|
int minY = range.min.y; |
|
int maxY = min (range.max.y, _maxY); |
|
|
|
unsigned char *tmpBufferEnd = _tmpBuffer; |
|
|
|
for (int y = minY; y <= maxY; ++y) |
|
{ |
|
for (ChannelList::ConstIterator i = _channels.begin(); |
|
i != _channels.end(); |
|
++i) |
|
{ |
|
const Channel &c = i.channel(); |
|
|
|
if (modp (y, c.ySampling) != 0) |
|
continue; |
|
|
|
int n = numSamples (c.xSampling, minX, maxX); |
|
|
|
unsigned char *ptr[4]; |
|
unsigned int previousPixel = 0; |
|
|
|
switch (c.type) |
|
{ |
|
case UINT: |
|
|
|
ptr[0] = tmpBufferEnd; |
|
ptr[1] = ptr[0] + n; |
|
ptr[2] = ptr[1] + n; |
|
ptr[3] = ptr[2] + n; |
|
tmpBufferEnd = ptr[3] + n; |
|
|
|
for (int j = 0; j < n; ++j) |
|
{ |
|
unsigned int pixel; |
|
char *pPtr = (char *) &pixel; |
|
|
|
for (int k = 0; k < sizeof (pixel); ++k) |
|
*pPtr++ = *inPtr++; |
|
|
|
unsigned int diff = pixel - previousPixel; |
|
previousPixel = pixel; |
|
|
|
*(ptr[0]++) = diff >> 24; |
|
*(ptr[1]++) = diff >> 16; |
|
*(ptr[2]++) = diff >> 8; |
|
*(ptr[3]++) = diff; |
|
} |
|
|
|
break; |
|
|
|
case HALF: |
|
|
|
ptr[0] = tmpBufferEnd; |
|
ptr[1] = ptr[0] + n; |
|
tmpBufferEnd = ptr[1] + n; |
|
|
|
for (int j = 0; j < n; ++j) |
|
{ |
|
half pixel; |
|
|
|
pixel = *(const half *) inPtr; |
|
inPtr += sizeof (half); |
|
|
|
unsigned int diff = pixel.bits() - previousPixel; |
|
previousPixel = pixel.bits(); |
|
|
|
*(ptr[0]++) = diff >> 8; |
|
*(ptr[1]++) = diff; |
|
} |
|
|
|
break; |
|
|
|
case FLOAT: |
|
|
|
ptr[0] = tmpBufferEnd; |
|
ptr[1] = ptr[0] + n; |
|
ptr[2] = ptr[1] + n; |
|
tmpBufferEnd = ptr[2] + n; |
|
|
|
for (int j = 0; j < n; ++j) |
|
{ |
|
float pixel; |
|
char *pPtr = (char *) &pixel; |
|
|
|
for (int k = 0; k < sizeof (pixel); ++k) |
|
*pPtr++ = *inPtr++; |
|
|
|
unsigned int pixel24 = floatToFloat24 (pixel); |
|
unsigned int diff = pixel24 - previousPixel; |
|
previousPixel = pixel24; |
|
|
|
*(ptr[0]++) = diff >> 16; |
|
*(ptr[1]++) = diff >> 8; |
|
*(ptr[2]++) = diff; |
|
} |
|
|
|
break; |
|
|
|
default: |
|
|
|
assert (false); |
|
} |
|
} |
|
} |
|
|
|
uLongf outSize = int (ceil ((tmpBufferEnd - _tmpBuffer) * 1.01)) + 100; |
|
|
|
if (Z_OK != ::compress ((Bytef *) _outBuffer, |
|
&outSize, |
|
(const Bytef *) _tmpBuffer, |
|
tmpBufferEnd - _tmpBuffer)) |
|
{ |
|
throw Iex::BaseExc ("Data compression (zlib) failed."); |
|
} |
|
|
|
outPtr = _outBuffer; |
|
return outSize; |
|
} |
|
|
|
|
|
int |
|
Pxr24Compressor::uncompress (const char *inPtr, |
|
int inSize, |
|
Box2i range, |
|
const char *&outPtr) |
|
{ |
|
if (inSize == 0) |
|
{ |
|
outPtr = _outBuffer; |
|
return 0; |
|
} |
|
|
|
uLongf tmpSize = _maxScanLineSize * _numScanLines; |
|
|
|
if (Z_OK != ::uncompress ((Bytef *)_tmpBuffer, |
|
&tmpSize, |
|
(const Bytef *) inPtr, |
|
inSize)) |
|
{ |
|
throw Iex::InputExc ("Data decompression (zlib) failed."); |
|
} |
|
|
|
int minX = range.min.x; |
|
int maxX = min (range.max.x, _maxX); |
|
int minY = range.min.y; |
|
int maxY = min (range.max.y, _maxY); |
|
|
|
const unsigned char *tmpBufferEnd = _tmpBuffer; |
|
char *writePtr = _outBuffer; |
|
|
|
for (int y = minY; y <= maxY; ++y) |
|
{ |
|
for (ChannelList::ConstIterator i = _channels.begin(); |
|
i != _channels.end(); |
|
++i) |
|
{ |
|
const Channel &c = i.channel(); |
|
|
|
if (modp (y, c.ySampling) != 0) |
|
continue; |
|
|
|
int n = numSamples (c.xSampling, minX, maxX); |
|
|
|
const unsigned char *ptr[4]; |
|
unsigned int pixel = 0; |
|
|
|
switch (c.type) |
|
{ |
|
case UINT: |
|
|
|
ptr[0] = tmpBufferEnd; |
|
ptr[1] = ptr[0] + n; |
|
ptr[2] = ptr[1] + n; |
|
ptr[3] = ptr[2] + n; |
|
tmpBufferEnd = ptr[3] + n; |
|
|
|
if (tmpBufferEnd - _tmpBuffer > tmpSize) |
|
notEnoughData(); |
|
|
|
for (int j = 0; j < n; ++j) |
|
{ |
|
unsigned int diff = (*(ptr[0]++) << 24) | |
|
(*(ptr[1]++) << 16) | |
|
(*(ptr[2]++) << 8) | |
|
*(ptr[3]++); |
|
|
|
pixel += diff; |
|
|
|
char *pPtr = (char *) &pixel; |
|
|
|
for (int k = 0; k < sizeof (pixel); ++k) |
|
*writePtr++ = *pPtr++; |
|
} |
|
|
|
break; |
|
|
|
case HALF: |
|
|
|
ptr[0] = tmpBufferEnd; |
|
ptr[1] = ptr[0] + n; |
|
tmpBufferEnd = ptr[1] + n; |
|
|
|
if (tmpBufferEnd - _tmpBuffer > tmpSize) |
|
notEnoughData(); |
|
|
|
for (int j = 0; j < n; ++j) |
|
{ |
|
unsigned int diff = (*(ptr[0]++) << 8) | |
|
*(ptr[1]++); |
|
|
|
pixel += diff; |
|
|
|
half * hPtr = (half *) writePtr; |
|
hPtr->setBits ((unsigned short) pixel); |
|
writePtr += sizeof (half); |
|
} |
|
|
|
break; |
|
|
|
case FLOAT: |
|
|
|
ptr[0] = tmpBufferEnd; |
|
ptr[1] = ptr[0] + n; |
|
ptr[2] = ptr[1] + n; |
|
tmpBufferEnd = ptr[2] + n; |
|
|
|
if (tmpBufferEnd - _tmpBuffer > tmpSize) |
|
notEnoughData(); |
|
|
|
for (int j = 0; j < n; ++j) |
|
{ |
|
unsigned int diff = (*(ptr[0]++) << 24) | |
|
(*(ptr[1]++) << 16) | |
|
(*(ptr[2]++) << 8); |
|
pixel += diff; |
|
|
|
char *pPtr = (char *) &pixel; |
|
|
|
for (int k = 0; k < sizeof (pixel); ++k) |
|
*writePtr++ = *pPtr++; |
|
} |
|
|
|
break; |
|
|
|
default: |
|
|
|
assert (false); |
|
} |
|
} |
|
} |
|
|
|
if (tmpBufferEnd - _tmpBuffer < tmpSize) |
|
tooMuchData(); |
|
|
|
outPtr = _outBuffer; |
|
return writePtr - _outBuffer; |
|
} |
|
|
|
} // namespace Imf
|
|
|