123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673 |
- ///////////////////////////////////////////////////////////////////////////
- //
- // Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
- // Digital Ltd. LLC
- //
- // 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 Industrial Light & Magic 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 PizCompressor
- //
- //-----------------------------------------------------------------------------
- #include "ImfPizCompressor.h"
- #include "ImfHeader.h"
- #include "ImfChannelList.h"
- #include "ImfHuf.h"
- #include "ImfWav.h"
- #include "ImfMisc.h"
- #include "ImfCheckedArithmetic.h"
- #include <ImathFun.h>
- #include <ImathBox.h>
- #include <Iex.h>
- #include "ImfIO.h"
- #include "ImfXdr.h"
- #include "ImfAutoArray.h"
- #include <string.h>
- #include <assert.h>
- #include "ImfNamespace.h"
- OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
- using IMATH_NAMESPACE::divp;
- using IMATH_NAMESPACE::modp;
- using IMATH_NAMESPACE::Box2i;
- using IMATH_NAMESPACE::V2i;
- using IEX_NAMESPACE::InputExc;
- namespace {
- //
- // Functions to compress the range of values in the pixel data
- //
- const int USHORT_RANGE = (1 << 16);
- const int BITMAP_SIZE = (USHORT_RANGE >> 3);
- void
- bitmapFromData (const unsigned short data[/*nData*/],
- int nData,
- unsigned char bitmap[BITMAP_SIZE],
- unsigned short &minNonZero,
- unsigned short &maxNonZero)
- {
- for (int i = 0; i < BITMAP_SIZE; ++i)
- bitmap[i] = 0;
- for (int i = 0; i < nData; ++i)
- bitmap[data[i] >> 3] |= (1 << (data[i] & 7));
- bitmap[0] &= ~1; // zero is not explicitly stored in
- // the bitmap; we assume that the
- // data always contain zeroes
- minNonZero = BITMAP_SIZE - 1;
- maxNonZero = 0;
- for (int i = 0; i < BITMAP_SIZE; ++i)
- {
- if (bitmap[i])
- {
- if (minNonZero > i)
- minNonZero = i;
- if (maxNonZero < i)
- maxNonZero = i;
- }
- }
- }
- unsigned short
- forwardLutFromBitmap (const unsigned char bitmap[BITMAP_SIZE],
- unsigned short lut[USHORT_RANGE])
- {
- int k = 0;
- for (int i = 0; i < USHORT_RANGE; ++i)
- {
- if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7))))
- lut[i] = k++;
- else
- lut[i] = 0;
- }
- return k - 1; // maximum value stored in lut[],
- } // i.e. number of ones in bitmap minus 1
- unsigned short
- reverseLutFromBitmap (const unsigned char bitmap[BITMAP_SIZE],
- unsigned short lut[USHORT_RANGE])
- {
- int k = 0;
- for (int i = 0; i < USHORT_RANGE; ++i)
- {
- if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7))))
- lut[k++] = i;
- }
- int n = k - 1;
- while (k < USHORT_RANGE)
- lut[k++] = 0;
- return n; // maximum k where lut[k] is non-zero,
- } // i.e. number of ones in bitmap minus 1
- void
- applyLut (const unsigned short lut[USHORT_RANGE],
- unsigned short data[/*nData*/],
- int nData)
- {
- for (int i = 0; i < nData; ++i)
- data[i] = lut[data[i]];
- }
- } // namespace
- struct PizCompressor::ChannelData
- {
- unsigned short * start;
- unsigned short * end;
- int nx;
- int ny;
- int ys;
- int size;
- };
- PizCompressor::PizCompressor
- (const Header &hdr,
- size_t maxScanLineSize,
- size_t numScanLines)
- :
- Compressor (hdr),
- _maxScanLineSize (maxScanLineSize),
- _format (XDR),
- _numScanLines (numScanLines),
- _tmpBuffer (0),
- _outBuffer (0),
- _numChans (0),
- _channels (hdr.channels()),
- _channelData (0)
- {
- size_t tmpBufferSize =
- uiMult (maxScanLineSize, numScanLines) / 2;
- size_t outBufferSize =
- uiAdd (uiMult (maxScanLineSize, numScanLines),
- size_t (65536 + 8192));
- _tmpBuffer = new unsigned short
- [checkArraySize (tmpBufferSize, sizeof (unsigned short))];
- _outBuffer = new char [outBufferSize];
- const ChannelList &channels = header().channels();
- bool onlyHalfChannels = true;
- for (ChannelList::ConstIterator c = channels.begin();
- c != channels.end();
- ++c)
- {
- _numChans++;
- assert (pixelTypeSize (c.channel().type) % pixelTypeSize (HALF) == 0);
- if (c.channel().type != HALF)
- onlyHalfChannels = false;
- }
- _channelData = new ChannelData[_numChans];
- const Box2i &dataWindow = hdr.dataWindow();
- _minX = dataWindow.min.x;
- _maxX = dataWindow.max.x;
- _maxY = dataWindow.max.y;
- //
- // We can support uncompressed data in the machine's native format
- // if all image channels are of type HALF, and if the Xdr and the
- // native represenations of a half have the same size.
- //
- if (onlyHalfChannels && (sizeof (half) == pixelTypeSize (HALF)))
- _format = NATIVE;
- }
- PizCompressor::~PizCompressor ()
- {
- delete [] _tmpBuffer;
- delete [] _outBuffer;
- delete [] _channelData;
- }
- int
- PizCompressor::numScanLines () const
- {
- return _numScanLines;
- }
- Compressor::Format
- PizCompressor::format () const
- {
- return _format;
- }
- int
- PizCompressor::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
- PizCompressor::compressTile (const char *inPtr,
- int inSize,
- IMATH_NAMESPACE::Box2i range,
- const char *&outPtr)
- {
- return compress (inPtr, inSize, range, outPtr);
- }
- int
- PizCompressor::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
- PizCompressor::uncompressTile (const char *inPtr,
- int inSize,
- IMATH_NAMESPACE::Box2i range,
- const char *&outPtr)
- {
- return uncompress (inPtr, inSize, range, outPtr);
- }
- int
- PizCompressor::compress (const char *inPtr,
- int inSize,
- IMATH_NAMESPACE::Box2i range,
- const char *&outPtr)
- {
- //
- // This is the compress function which is used by both the tiled and
- // scanline compression routines.
- //
- //
- // Special case �- empty input buffer
- //
- if (inSize == 0)
- {
- outPtr = _outBuffer;
- return 0;
- }
- //
- // Rearrange the pixel data so that the wavelet
- // and Huffman encoders can process them easily.
- //
- // The wavelet and Huffman encoders both handle only
- // 16-bit data, so 32-bit data must be split into smaller
- // pieces. We treat each 32-bit channel (UINT, FLOAT) as
- // two interleaved 16-bit channels.
- //
- int minX = range.min.x;
- int maxX = range.max.x;
- int minY = range.min.y;
- int maxY = range.max.y;
-
- if (maxY > _maxY)
- maxY = _maxY;
-
- if (maxX > _maxX)
- maxX = _maxX;
- unsigned short *tmpBufferEnd = _tmpBuffer;
- int i = 0;
- for (ChannelList::ConstIterator c = _channels.begin();
- c != _channels.end();
- ++c, ++i)
- {
- ChannelData &cd = _channelData[i];
- cd.start = tmpBufferEnd;
- cd.end = cd.start;
- cd.nx = numSamples (c.channel().xSampling, minX, maxX);
- cd.ny = numSamples (c.channel().ySampling, minY, maxY);
- cd.ys = c.channel().ySampling;
- cd.size = pixelTypeSize (c.channel().type) / pixelTypeSize (HALF);
- tmpBufferEnd += cd.nx * cd.ny * cd.size;
- }
- if (_format == XDR)
- {
- //
- // Machine-independent (Xdr) data format
- //
- for (int y = minY; y <= maxY; ++y)
- {
- for (int i = 0; i < _numChans; ++i)
- {
- ChannelData &cd = _channelData[i];
- if (modp (y, cd.ys) != 0)
- continue;
- for (int x = cd.nx * cd.size; x > 0; --x)
- {
- Xdr::read <CharPtrIO> (inPtr, *cd.end);
- ++cd.end;
- }
- }
- }
- }
- else
- {
- //
- // Native, machine-dependent data format
- //
- for (int y = minY; y <= maxY; ++y)
- {
- for (int i = 0; i < _numChans; ++i)
- {
- ChannelData &cd = _channelData[i];
- if (modp (y, cd.ys) != 0)
- continue;
- int n = cd.nx * cd.size;
- memcpy (cd.end, inPtr, n * sizeof (unsigned short));
- inPtr += n * sizeof (unsigned short);
- cd.end += n;
- }
- }
- }
- #if defined (DEBUG)
- for (int i = 1; i < _numChans; ++i)
- assert (_channelData[i-1].end == _channelData[i].start);
- assert (_channelData[_numChans-1].end == tmpBufferEnd);
- #endif
- //
- // Compress the range of the pixel data
- //
- AutoArray <unsigned char, BITMAP_SIZE> bitmap;
- unsigned short minNonZero;
- unsigned short maxNonZero;
- bitmapFromData (_tmpBuffer,
- tmpBufferEnd - _tmpBuffer,
- bitmap,
- minNonZero, maxNonZero);
- AutoArray <unsigned short, USHORT_RANGE> lut;
- unsigned short maxValue = forwardLutFromBitmap (bitmap, lut);
- applyLut (lut, _tmpBuffer, tmpBufferEnd - _tmpBuffer);
- //
- // Store range compression info in _outBuffer
- //
- char *buf = _outBuffer;
- Xdr::write <CharPtrIO> (buf, minNonZero);
- Xdr::write <CharPtrIO> (buf, maxNonZero);
- if (minNonZero <= maxNonZero)
- {
- Xdr::write <CharPtrIO> (buf, (char *) &bitmap[0] + minNonZero,
- maxNonZero - minNonZero + 1);
- }
- //
- // Apply wavelet encoding
- //
- for (int i = 0; i < _numChans; ++i)
- {
- ChannelData &cd = _channelData[i];
- for (int j = 0; j < cd.size; ++j)
- {
- wav2Encode (cd.start + j,
- cd.nx, cd.size,
- cd.ny, cd.nx * cd.size,
- maxValue);
- }
- }
- //
- // Apply Huffman encoding; append the result to _outBuffer
- //
- char *lengthPtr = buf;
- Xdr::write <CharPtrIO> (buf, int(0));
- int length = hufCompress (_tmpBuffer, tmpBufferEnd - _tmpBuffer, buf);
- Xdr::write <CharPtrIO> (lengthPtr, length);
- outPtr = _outBuffer;
- return buf - _outBuffer + length;
- }
- int
- PizCompressor::uncompress (const char *inPtr,
- int inSize,
- IMATH_NAMESPACE::Box2i range,
- const char *&outPtr)
- {
- //
- // This is the cunompress function which is used by both the tiled and
- // scanline decompression routines.
- //
-
- //
- // Special case - empty input buffer
- //
- if (inSize == 0)
- {
- outPtr = _outBuffer;
- return 0;
- }
- //
- // Determine the layout of the compressed pixel data
- //
- int minX = range.min.x;
- int maxX = range.max.x;
- int minY = range.min.y;
- int maxY = range.max.y;
-
- if (maxY > _maxY)
- maxY = _maxY;
-
- if (maxX > _maxX)
- maxX = _maxX;
- unsigned short *tmpBufferEnd = _tmpBuffer;
- int i = 0;
- for (ChannelList::ConstIterator c = _channels.begin();
- c != _channels.end();
- ++c, ++i)
- {
- ChannelData &cd = _channelData[i];
- cd.start = tmpBufferEnd;
- cd.end = cd.start;
- cd.nx = numSamples (c.channel().xSampling, minX, maxX);
- cd.ny = numSamples (c.channel().ySampling, minY, maxY);
- cd.ys = c.channel().ySampling;
- cd.size = pixelTypeSize (c.channel().type) / pixelTypeSize (HALF);
- tmpBufferEnd += cd.nx * cd.ny * cd.size;
- }
- //
- // Read range compression data
- //
- unsigned short minNonZero;
- unsigned short maxNonZero;
- AutoArray <unsigned char, BITMAP_SIZE> bitmap;
- memset (bitmap, 0, sizeof (unsigned char) * BITMAP_SIZE);
- Xdr::read <CharPtrIO> (inPtr, minNonZero);
- Xdr::read <CharPtrIO> (inPtr, maxNonZero);
- if (maxNonZero >= BITMAP_SIZE)
- {
- throw InputExc ("Error in header for PIZ-compressed data "
- "(invalid bitmap size).");
- }
- if (minNonZero <= maxNonZero)
- {
- Xdr::read <CharPtrIO> (inPtr, (char *) &bitmap[0] + minNonZero,
- maxNonZero - minNonZero + 1);
- }
- AutoArray <unsigned short, USHORT_RANGE> lut;
- unsigned short maxValue = reverseLutFromBitmap (bitmap, lut);
- //
- // Huffman decoding
- //
- int length;
- Xdr::read <CharPtrIO> (inPtr, length);
- if (length > inSize)
- {
- throw InputExc ("Error in header for PIZ-compressed data "
- "(invalid array length).");
- }
- hufUncompress (inPtr, length, _tmpBuffer, tmpBufferEnd - _tmpBuffer);
- //
- // Wavelet decoding
- //
- for (int i = 0; i < _numChans; ++i)
- {
- ChannelData &cd = _channelData[i];
- for (int j = 0; j < cd.size; ++j)
- {
- wav2Decode (cd.start + j,
- cd.nx, cd.size,
- cd.ny, cd.nx * cd.size,
- maxValue);
- }
- }
- //
- // Expand the pixel data to their original range
- //
- applyLut (lut, _tmpBuffer, tmpBufferEnd - _tmpBuffer);
-
- //
- // Rearrange the pixel data into the format expected by the caller.
- //
- char *outEnd = _outBuffer;
- if (_format == XDR)
- {
- //
- // Machine-independent (Xdr) data format
- //
- for (int y = minY; y <= maxY; ++y)
- {
- for (int i = 0; i < _numChans; ++i)
- {
- ChannelData &cd = _channelData[i];
- if (modp (y, cd.ys) != 0)
- continue;
- for (int x = cd.nx * cd.size; x > 0; --x)
- {
- Xdr::write <CharPtrIO> (outEnd, *cd.end);
- ++cd.end;
- }
- }
- }
- }
- else
- {
- //
- // Native, machine-dependent data format
- //
- for (int y = minY; y <= maxY; ++y)
- {
- for (int i = 0; i < _numChans; ++i)
- {
- ChannelData &cd = _channelData[i];
- if (modp (y, cd.ys) != 0)
- continue;
- int n = cd.nx * cd.size;
- memcpy (outEnd, cd.end, n * sizeof (unsigned short));
- outEnd += n * sizeof (unsigned short);
- cd.end += n;
- }
- }
- }
- #if defined (DEBUG)
- for (int i = 1; i < _numChans; ++i)
- assert (_channelData[i-1].end == _channelData[i].start);
- assert (_channelData[_numChans-1].end == tmpBufferEnd);
- #endif
- outPtr = _outBuffer;
- return outEnd - _outBuffer;
- }
- OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
|