551 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			551 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /////////////////////////////////////////////////////////////////////////////
 | |
| //
 | |
| // 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
 | 
