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
 |