/*****************************************************************************/ // Copyright 2006-2007 Adobe Systems Incorporated // All Rights Reserved. // // NOTICE: Adobe permits you to use, modify, and distribute this file in // accordance with the terms of the Adobe license agreement accompanying it. /*****************************************************************************/ /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_lossless_jpeg.cpp#2 $ */ /* $DateTime: 2012/06/01 07:28:57 $ */ /* $Change: 832715 $ */ /* $Author: tknoll $ */ /*****************************************************************************/ // Lossless JPEG code adapted from: /* Copyright (C) 1991, 1992, Thomas G. Lane. * Part of the Independent JPEG Group's software. * See the file Copyright for more details. * * Copyright (c) 1993 Brian C. Smith, The Regents of the University * of California * All rights reserved. * * Copyright (c) 1994 Kongji Huang and Brian C. Smith. * Cornell University * All rights reserved. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose, without fee, and without written agreement is * hereby granted, provided that the above copyright notice and the following * two paragraphs appear in all copies of this software. * * IN NO EVENT SHALL CORNELL UNIVERSITY BE LIABLE TO ANY PARTY FOR * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF CORNELL * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * CORNELL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND CORNELL UNIVERSITY HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ /*****************************************************************************/ #include "dng_lossless_jpeg.h" #include "dng_assertions.h" #include "dng_exceptions.h" #include "dng_memory.h" #include "dng_stream.h" #include "dng_tag_codes.h" /*****************************************************************************/ // This module contains routines that should be as fast as possible, even // at the expense of slight code size increases. #include "dng_fast_module.h" /*****************************************************************************/ // The qSupportCanon_sRAW stuff not actually required for DNG support, but // only included to allow this code to be used on Canon sRAW files. #ifndef qSupportCanon_sRAW #define qSupportCanon_sRAW 1 #endif // The qSupportHasselblad_3FR stuff not actually required for DNG support, but // only included to allow this code to be used on Hasselblad 3FR files. #ifndef qSupportHasselblad_3FR #define qSupportHasselblad_3FR 1 #endif /*****************************************************************************/ /* * One of the following structures is created for each huffman coding * table. We use the same structure for encoding and decoding, so there * may be some extra fields for encoding that aren't used in the decoding * and vice-versa. */ struct HuffmanTable { /* * These two fields directly represent the contents of a JPEG DHT * marker */ uint8 bits[17]; uint8 huffval[256]; /* * The remaining fields are computed from the above to allow more * efficient coding and decoding. These fields should be considered * private to the Huffman compression & decompression modules. */ uint16 mincode[17]; int32 maxcode[18]; int16 valptr[17]; int32 numbits[256]; int32 value[256]; uint16 ehufco[256]; int8 ehufsi[256]; }; /*****************************************************************************/ // Computes the derived fields in the Huffman table structure. static void FixHuffTbl (HuffmanTable *htbl) { int32 l; int32 i; const uint32 bitMask [] = { 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff, 0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff, 0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff, 0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff, 0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff, 0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff, 0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f, 0x0000000f, 0x00000007, 0x00000003, 0x00000001 }; // Figure C.1: make table of Huffman code length for each symbol // Note that this is in code-length order. int8 huffsize [257]; int32 p = 0; for (l = 1; l <= 16; l++) { for (i = 1; i <= (int32) htbl->bits [l]; i++) huffsize [p++] = (int8) l; } huffsize [p] = 0; int32 lastp = p; // Figure C.2: generate the codes themselves // Note that this is in code-length order. uint16 huffcode [257]; uint16 code = 0; int32 si = huffsize [0]; p = 0; while (huffsize [p]) { while (((int32) huffsize [p]) == si) { huffcode [p++] = code; code++; } code <<= 1; si++; } // Figure C.3: generate encoding tables // These are code and size indexed by symbol value // Set any codeless symbols to have code length 0; this allows // EmitBits to detect any attempt to emit such symbols. memset (htbl->ehufsi, 0, sizeof (htbl->ehufsi)); for (p = 0; p < lastp; p++) { htbl->ehufco [htbl->huffval [p]] = huffcode [p]; htbl->ehufsi [htbl->huffval [p]] = huffsize [p]; } // Figure F.15: generate decoding tables p = 0; for (l = 1; l <= 16; l++) { if (htbl->bits [l]) { htbl->valptr [l] = (int16) p; htbl->mincode [l] = huffcode [p]; p += htbl->bits [l]; htbl->maxcode [l] = huffcode [p - 1]; } else { htbl->maxcode [l] = -1; } } // We put in this value to ensure HuffDecode terminates. htbl->maxcode[17] = 0xFFFFFL; // Build the numbits, value lookup tables. // These table allow us to gather 8 bits from the bits stream, // and immediately lookup the size and value of the huffman codes. // If size is zero, it means that more than 8 bits are in the huffman // code (this happens about 3-4% of the time). memset (htbl->numbits, 0, sizeof (htbl->numbits)); for (p = 0; p < lastp; p++) { int32 size = huffsize [p]; if (size <= 8) { int32 value = htbl->huffval [p]; code = huffcode [p]; int32 ll = code << (8 -size); int32 ul = (size < 8 ? ll | bitMask [24 + size] : ll); if (ul >= static_cast<int32>(sizeof(htbl->numbits) / sizeof(htbl->numbits[0])) || ul >= static_cast<int32>(sizeof(htbl->value) / sizeof(htbl->value[0]))) { ThrowBadFormat (); } for (i = ll; i <= ul; i++) { htbl->numbits [i] = size; htbl->value [i] = value; } } } } /*****************************************************************************/ /* * The following structure stores basic information about one component. */ struct JpegComponentInfo { /* * These values are fixed over the whole image. * They are read from the SOF marker. */ int16 componentId; /* identifier for this component (0..255) */ int16 componentIndex; /* its index in SOF or cPtr->compInfo[] */ /* * Downsampling is not normally used in lossless JPEG, although * it is permitted by the JPEG standard (DIS). We set all sampling * factors to 1 in this program. */ int16 hSampFactor; /* horizontal sampling factor */ int16 vSampFactor; /* vertical sampling factor */ /* * Huffman table selector (0..3). The value may vary * between scans. It is read from the SOS marker. */ int16 dcTblNo; }; /* * One of the following structures is used to pass around the * decompression information. */ struct DecompressInfo { /* * Image width, height, and image data precision (bits/sample) * These fields are set by ReadFileHeader or ReadScanHeader */ int32 imageWidth; int32 imageHeight; int32 dataPrecision; /* * compInfo[i] describes component that appears i'th in SOF * numComponents is the # of color components in JPEG image. */ JpegComponentInfo *compInfo; int16 numComponents; /* * *curCompInfo[i] describes component that appears i'th in SOS. * compsInScan is the # of color components in current scan. */ JpegComponentInfo *curCompInfo[4]; int16 compsInScan; /* * MCUmembership[i] indexes the i'th component of MCU into the * curCompInfo array. */ int16 MCUmembership[10]; /* * ptrs to Huffman coding tables, or NULL if not defined */ HuffmanTable *dcHuffTblPtrs[4]; /* * prediction selection value (PSV) and point transform parameter (Pt) */ int32 Ss; int32 Pt; /* * In lossless JPEG, restart interval shall be an integer * multiple of the number of MCU in a MCU row. */ int32 restartInterval;/* MCUs per restart interval, 0 = no restart */ int32 restartInRows; /*if > 0, MCU rows per restart interval; 0 = no restart*/ /* * these fields are private data for the entropy decoder */ int32 restartRowsToGo; /* MCUs rows left in this restart interval */ int16 nextRestartNum; /* # of next RSTn marker (0..7) */ }; /*****************************************************************************/ // An MCU (minimum coding unit) is an array of samples. typedef uint16 ComponentType; // the type of image components typedef ComponentType *MCU; // MCU - array of samples /*****************************************************************************/ class dng_lossless_decoder { private: dng_stream *fStream; // Input data. dng_spooler *fSpooler; // Output data. bool fBug16; // Decode data with the "16-bit" bug. dng_memory_data huffmanBuffer [4]; dng_memory_data compInfoBuffer; DecompressInfo info; dng_memory_data mcuBuffer1; dng_memory_data mcuBuffer2; dng_memory_data mcuBuffer3; dng_memory_data mcuBuffer4; MCU *mcuROW1; MCU *mcuROW2; uint64 getBuffer; // current bit-extraction buffer int32 bitsLeft; // # of unused bits in it #if qSupportHasselblad_3FR bool fHasselblad3FR; #endif public: dng_lossless_decoder (dng_stream *stream, dng_spooler *spooler, bool bug16); void StartRead (uint32 &imageWidth, uint32 &imageHeight, uint32 &imageChannels); void FinishRead (); private: uint8 GetJpegChar () { return fStream->Get_uint8 (); } void UnGetJpegChar () { fStream->SetReadPosition (fStream->Position () - 1); } uint16 Get2bytes (); void SkipVariable (); void GetDht (); void GetDri (); void GetApp0 (); void GetSof (int32 code); void GetSos (); void GetSoi (); int32 NextMarker (); JpegMarker ProcessTables (); void ReadFileHeader (); int32 ReadScanHeader (); void DecoderStructInit (); void HuffDecoderInit (); void ProcessRestart (); int32 QuickPredict (int32 col, int32 curComp, MCU *curRowBuf, MCU *prevRowBuf); void FillBitBuffer (int32 nbits); int32 show_bits8 (); void flush_bits (int32 nbits); int32 get_bits (int32 nbits); int32 get_bit (); int32 HuffDecode (HuffmanTable *htbl); void HuffExtend (int32 &x, int32 s); void PmPutRow (MCU *buf, int32 numComp, int32 numCol, int32 row); void DecodeFirstRow (MCU *curRowBuf); void DecodeImage (); // Hidden copy constructor and assignment operator. dng_lossless_decoder (const dng_lossless_decoder &decoder); dng_lossless_decoder & operator= (const dng_lossless_decoder &decoder); }; /*****************************************************************************/ dng_lossless_decoder::dng_lossless_decoder (dng_stream *stream, dng_spooler *spooler, bool bug16) : fStream (stream ) , fSpooler (spooler) , fBug16 (bug16 ) , compInfoBuffer () , info () , mcuBuffer1 () , mcuBuffer2 () , mcuBuffer3 () , mcuBuffer4 () , mcuROW1 (NULL) , mcuROW2 (NULL) , getBuffer (0) , bitsLeft (0) #if qSupportHasselblad_3FR , fHasselblad3FR (false) #endif { memset (&info, 0, sizeof (info)); } /*****************************************************************************/ uint16 dng_lossless_decoder::Get2bytes () { uint16 a = GetJpegChar (); return (uint16) ((a << 8) + GetJpegChar ()); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * SkipVariable -- * * Skip over an unknown or uninteresting variable-length marker * * Results: * None. * * Side effects: * Bitstream is parsed over marker. * * *-------------------------------------------------------------- */ void dng_lossless_decoder::SkipVariable () { uint32 length = Get2bytes () - 2; fStream->Skip (length); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * GetDht -- * * Process a DHT marker * * Results: * None * * Side effects: * A huffman table is read. * Exits on error. * *-------------------------------------------------------------- */ void dng_lossless_decoder::GetDht () { int32 length = Get2bytes () - 2; while (length > 0) { int32 index = GetJpegChar (); if (index < 0 || index >= 4) { ThrowBadFormat (); } HuffmanTable *&htblptr = info.dcHuffTblPtrs [index]; if (htblptr == NULL) { huffmanBuffer [index] . Allocate (sizeof (HuffmanTable)); htblptr = (HuffmanTable *) huffmanBuffer [index] . Buffer (); } htblptr->bits [0] = 0; int32 count = 0; for (int32 i = 1; i <= 16; i++) { htblptr->bits [i] = GetJpegChar (); count += htblptr->bits [i]; } if (count > 256) { ThrowBadFormat (); } for (int32 j = 0; j < count; j++) { htblptr->huffval [j] = GetJpegChar (); } length -= 1 + 16 + count; } } /*****************************************************************************/ /* *-------------------------------------------------------------- * * GetDri -- * * Process a DRI marker * * Results: * None * * Side effects: * Exits on error. * Bitstream is parsed. * *-------------------------------------------------------------- */ void dng_lossless_decoder::GetDri () { if (Get2bytes () != 4) { ThrowBadFormat (); } info.restartInterval = Get2bytes (); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * GetApp0 -- * * Process an APP0 marker. * * Results: * None * * Side effects: * Bitstream is parsed * *-------------------------------------------------------------- */ void dng_lossless_decoder::GetApp0 () { SkipVariable (); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * GetSof -- * * Process a SOFn marker * * Results: * None. * * Side effects: * Bitstream is parsed * Exits on error * info structure is filled in * *-------------------------------------------------------------- */ void dng_lossless_decoder::GetSof (int32 /*code*/) { int32 length = Get2bytes (); info.dataPrecision = GetJpegChar (); info.imageHeight = Get2bytes (); info.imageWidth = Get2bytes (); info.numComponents = GetJpegChar (); // We don't support files in which the image height is initially // specified as 0 and is later redefined by DNL. As long as we // have to check that, might as well have a general sanity check. if ((info.imageHeight <= 0) || (info.imageWidth <= 0) || (info.numComponents <= 0)) { ThrowBadFormat (); } // Lossless JPEG specifies data precision to be from 2 to 16 bits/sample. const int32 MinPrecisionBits = 2; const int32 MaxPrecisionBits = 16; if ((info.dataPrecision < MinPrecisionBits) || (info.dataPrecision > MaxPrecisionBits)) { ThrowBadFormat (); } // Check length of tag. if (length != (info.numComponents * 3 + 8)) { ThrowBadFormat (); } // Allocate per component info. // We can cast info.numComponents to a uint32 because the check above // guarantees that it cannot be negative. compInfoBuffer.Allocate (static_cast<uint32> (info.numComponents), sizeof (JpegComponentInfo)); info.compInfo = (JpegComponentInfo *) compInfoBuffer.Buffer (); // Read in the per compent info. for (int32 ci = 0; ci < info.numComponents; ci++) { JpegComponentInfo *compptr = &info.compInfo [ci]; compptr->componentIndex = (int16) ci; compptr->componentId = GetJpegChar (); int32 c = GetJpegChar (); compptr->hSampFactor = (int16) ((c >> 4) & 15); compptr->vSampFactor = (int16) ((c ) & 15); (void) GetJpegChar (); /* skip Tq */ } } /*****************************************************************************/ /* *-------------------------------------------------------------- * * GetSos -- * * Process a SOS marker * * Results: * None. * * Side effects: * Bitstream is parsed. * Exits on error. * *-------------------------------------------------------------- */ void dng_lossless_decoder::GetSos () { int32 length = Get2bytes (); // Get the number of image components. int32 n = GetJpegChar (); info.compsInScan = (int16) n; // Check length. length -= 3; if (length != (n * 2 + 3) || n < 1 || n > 4) { ThrowBadFormat (); } // Find index and huffman table for each component. for (int32 i = 0; i < n; i++) { int32 cc = GetJpegChar (); int32 c = GetJpegChar (); int32 ci; for (ci = 0; ci < info.numComponents; ci++) { if (cc == info.compInfo[ci].componentId) { break; } } if (ci >= info.numComponents) { ThrowBadFormat (); } JpegComponentInfo *compptr = &info.compInfo [ci]; info.curCompInfo [i] = compptr; compptr->dcTblNo = (int16) ((c >> 4) & 15); } // Get the PSV, skip Se, and get the point transform parameter. info.Ss = GetJpegChar (); (void) GetJpegChar (); info.Pt = GetJpegChar () & 0x0F; } /*****************************************************************************/ /* *-------------------------------------------------------------- * * GetSoi -- * * Process an SOI marker * * Results: * None. * * Side effects: * Bitstream is parsed. * Exits on error. * *-------------------------------------------------------------- */ void dng_lossless_decoder::GetSoi () { // Reset all parameters that are defined to be reset by SOI info.restartInterval = 0; } /*****************************************************************************/ /* *-------------------------------------------------------------- * * NextMarker -- * * Find the next JPEG marker Note that the output might not * be a valid marker code but it will never be 0 or FF * * Results: * The marker found. * * Side effects: * Bitstream is parsed. * *-------------------------------------------------------------- */ int32 dng_lossless_decoder::NextMarker () { int32 c; do { // skip any non-FF bytes do { c = GetJpegChar (); } while (c != 0xFF); // skip any duplicate FFs, since extra FFs are legal do { c = GetJpegChar(); } while (c == 0xFF); } while (c == 0); // repeat if it was a stuffed FF/00 return c; } /*****************************************************************************/ /* *-------------------------------------------------------------- * * ProcessTables -- * * Scan and process JPEG markers that can appear in any order * Return when an SOI, EOI, SOFn, or SOS is found * * Results: * The marker found. * * Side effects: * Bitstream is parsed. * *-------------------------------------------------------------- */ JpegMarker dng_lossless_decoder::ProcessTables () { while (true) { int32 c = NextMarker (); switch (c) { case M_SOF0: case M_SOF1: case M_SOF2: case M_SOF3: case M_SOF5: case M_SOF6: case M_SOF7: case M_JPG: case M_SOF9: case M_SOF10: case M_SOF11: case M_SOF13: case M_SOF14: case M_SOF15: case M_SOI: case M_EOI: case M_SOS: return (JpegMarker) c; case M_DHT: GetDht (); break; case M_DQT: break; case M_DRI: GetDri (); break; case M_APP0: GetApp0 (); break; case M_RST0: // these are all parameterless case M_RST1: case M_RST2: case M_RST3: case M_RST4: case M_RST5: case M_RST6: case M_RST7: case M_TEM: break; default: // must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn SkipVariable (); break; } } return M_ERROR; } /*****************************************************************************/ /* *-------------------------------------------------------------- * * ReadFileHeader -- * * Initialize and read the stream header (everything through * the SOF marker). * * Results: * None * * Side effects: * Exit on error. * *-------------------------------------------------------------- */ void dng_lossless_decoder::ReadFileHeader () { // Demand an SOI marker at the start of the stream --- otherwise it's // probably not a JPEG stream at all. int32 c = GetJpegChar (); int32 c2 = GetJpegChar (); if ((c != 0xFF) || (c2 != M_SOI)) { ThrowBadFormat (); } // OK, process SOI GetSoi (); // Process markers until SOF c = ProcessTables (); switch (c) { case M_SOF0: case M_SOF1: case M_SOF3: GetSof (c); break; default: ThrowBadFormat (); break; } } /*****************************************************************************/ /* *-------------------------------------------------------------- * * ReadScanHeader -- * * Read the start of a scan (everything through the SOS marker). * * Results: * 1 if find SOS, 0 if find EOI * * Side effects: * Bitstream is parsed, may exit on errors. * *-------------------------------------------------------------- */ int32 dng_lossless_decoder::ReadScanHeader () { // Process markers until SOS or EOI int32 c = ProcessTables (); switch (c) { case M_SOS: GetSos (); return 1; case M_EOI: return 0; default: ThrowBadFormat (); break; } return 0; } /*****************************************************************************/ /* *-------------------------------------------------------------- * * DecoderStructInit -- * * Initalize the rest of the fields in the decompression * structure. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ void dng_lossless_decoder::DecoderStructInit () { int32 ci; #if qSupportCanon_sRAW bool canon_sRAW = (info.numComponents == 3) && (info.compInfo [0].hSampFactor == 2) && (info.compInfo [1].hSampFactor == 1) && (info.compInfo [2].hSampFactor == 1) && (info.compInfo [0].vSampFactor == 1) && (info.compInfo [1].vSampFactor == 1) && (info.compInfo [2].vSampFactor == 1) && (info.dataPrecision == 15) && (info.Ss == 1) && ((info.imageWidth & 1) == 0); bool canon_sRAW2 = (info.numComponents == 3) && (info.compInfo [0].hSampFactor == 2) && (info.compInfo [1].hSampFactor == 1) && (info.compInfo [2].hSampFactor == 1) && (info.compInfo [0].vSampFactor == 2) && (info.compInfo [1].vSampFactor == 1) && (info.compInfo [2].vSampFactor == 1) && (info.dataPrecision == 15) && (info.Ss == 1) && ((info.imageWidth & 1) == 0) && ((info.imageHeight & 1) == 0); if (!canon_sRAW && !canon_sRAW2) #endif { // Check sampling factor validity. for (ci = 0; ci < info.numComponents; ci++) { JpegComponentInfo *compPtr = &info.compInfo [ci]; if (compPtr->hSampFactor != 1 || compPtr->vSampFactor != 1) { ThrowBadFormat (); } } } // Prepare array describing MCU composition. if (info.compsInScan < 0 || info.compsInScan > 4) { ThrowBadFormat (); } for (ci = 0; ci < info.compsInScan; ci++) { info.MCUmembership [ci] = (int16) ci; } // Initialize mucROW1 and mcuROW2 which buffer two rows of // pixels for predictor calculation. // This multiplication cannot overflow because info.compsInScan is // guaranteed to be between 0 and 4 inclusive (see checks above). int32 mcuSize = info.compsInScan * (uint32) sizeof (ComponentType); mcuBuffer1.Allocate (info.imageWidth, sizeof (MCU)); mcuBuffer2.Allocate (info.imageWidth, sizeof (MCU)); mcuROW1 = (MCU *) mcuBuffer1.Buffer (); mcuROW2 = (MCU *) mcuBuffer2.Buffer (); mcuBuffer3.Allocate (info.imageWidth, mcuSize); mcuBuffer4.Allocate (info.imageWidth, mcuSize); mcuROW1 [0] = (ComponentType *) mcuBuffer3.Buffer (); mcuROW2 [0] = (ComponentType *) mcuBuffer4.Buffer (); for (int32 j = 1; j < info.imageWidth; j++) { mcuROW1 [j] = mcuROW1 [j - 1] + info.compsInScan; mcuROW2 [j] = mcuROW2 [j - 1] + info.compsInScan; } } /*****************************************************************************/ /* *-------------------------------------------------------------- * * HuffDecoderInit -- * * Initialize for a Huffman-compressed scan. * This is invoked after reading the SOS marker. * * Results: * None * * Side effects: * None. * *-------------------------------------------------------------- */ void dng_lossless_decoder::HuffDecoderInit () { // Initialize bit parser state getBuffer = 0; bitsLeft = 0; // Prepare Huffman tables. for (int16 ci = 0; ci < info.compsInScan; ci++) { JpegComponentInfo *compptr = info.curCompInfo [ci]; // Make sure requested tables are present if (compptr->dcTblNo < 0 || compptr->dcTblNo > 3) { ThrowBadFormat (); } if (info.dcHuffTblPtrs [compptr->dcTblNo] == NULL) { ThrowBadFormat (); } // Compute derived values for Huffman tables. // We may do this more than once for same table, but it's not a // big deal FixHuffTbl (info.dcHuffTblPtrs [compptr->dcTblNo]); } // Initialize restart stuff info.restartInRows = info.restartInterval / info.imageWidth; info.restartRowsToGo = info.restartInRows; info.nextRestartNum = 0; } /*****************************************************************************/ /* *-------------------------------------------------------------- * * ProcessRestart -- * * Check for a restart marker & resynchronize decoder. * * Results: * None. * * Side effects: * BitStream is parsed, bit buffer is reset, etc. * *-------------------------------------------------------------- */ void dng_lossless_decoder::ProcessRestart () { // Throw away and unused odd bits in the bit buffer. fStream->SetReadPosition (fStream->Position () - bitsLeft / 8); bitsLeft = 0; getBuffer = 0; // Scan for next JPEG marker int32 c; do { // skip any non-FF bytes do { c = GetJpegChar (); } while (c != 0xFF); // skip any duplicate FFs do { c = GetJpegChar (); } while (c == 0xFF); } while (c == 0); // repeat if it was a stuffed FF/00 // Verify correct restart code. if (c != (M_RST0 + info.nextRestartNum)) { ThrowBadFormat (); } // Update restart state. info.restartRowsToGo = info.restartInRows; info.nextRestartNum = (info.nextRestartNum + 1) & 7; } /*****************************************************************************/ /* *-------------------------------------------------------------- * * QuickPredict -- * * Calculate the predictor for sample curRowBuf[col][curComp]. * It does not handle the special cases at image edges, such * as first row and first column of a scan. We put the special * case checkings outside so that the computations in main * loop can be simpler. This has enhenced the performance * significantly. * * Results: * predictor is passed out. * * Side effects: * None. * *-------------------------------------------------------------- */ inline int32 dng_lossless_decoder::QuickPredict (int32 col, int32 curComp, MCU *curRowBuf, MCU *prevRowBuf) { int32 diag = prevRowBuf [col - 1] [curComp]; int32 upper = prevRowBuf [col ] [curComp]; int32 left = curRowBuf [col - 1] [curComp]; switch (info.Ss) { case 0: return 0; case 1: return left; case 2: return upper; case 3: return diag; case 4: return left + upper - diag; case 5: return left + ((upper - diag) >> 1); case 6: return upper + ((left - diag) >> 1); case 7: return (left + upper) >> 1; default: { ThrowBadFormat (); return 0; } } } /*****************************************************************************/ /* *-------------------------------------------------------------- * * FillBitBuffer -- * * Load up the bit buffer with at least nbits * Process any stuffed bytes at this time. * * Results: * None * * Side effects: * The bitwise global variables are updated. * *-------------------------------------------------------------- */ inline void dng_lossless_decoder::FillBitBuffer (int32 nbits) { const int32 kMinGetBits = sizeof (uint32) * 8 - 7; #if qSupportHasselblad_3FR if (fHasselblad3FR) { while (bitsLeft < kMinGetBits) { int32 c0 = GetJpegChar (); int32 c1 = GetJpegChar (); int32 c2 = GetJpegChar (); int32 c3 = GetJpegChar (); getBuffer = (getBuffer << 8) | c3; getBuffer = (getBuffer << 8) | c2; getBuffer = (getBuffer << 8) | c1; getBuffer = (getBuffer << 8) | c0; bitsLeft += 32; } return; } #endif while (bitsLeft < kMinGetBits) { int32 c = GetJpegChar (); // If it's 0xFF, check and discard stuffed zero byte if (c == 0xFF) { int32 c2 = GetJpegChar (); if (c2 != 0) { // Oops, it's actually a marker indicating end of // compressed data. Better put it back for use later. UnGetJpegChar (); UnGetJpegChar (); // There should be enough bits still left in the data // segment; if so, just break out of the while loop. if (bitsLeft >= nbits) break; // Uh-oh. Corrupted data: stuff zeroes into the data // stream, since this sometimes occurs when we are on the // last show_bits8 during decoding of the Huffman // segment. c = 0; } } getBuffer = (getBuffer << 8) | c; bitsLeft += 8; } } /*****************************************************************************/ inline int32 dng_lossless_decoder::show_bits8 () { if (bitsLeft < 8) FillBitBuffer (8); return (int32) ((getBuffer >> (bitsLeft - 8)) & 0xff); } /*****************************************************************************/ inline void dng_lossless_decoder::flush_bits (int32 nbits) { bitsLeft -= nbits; } /*****************************************************************************/ inline int32 dng_lossless_decoder::get_bits (int32 nbits) { if (nbits > 16) { ThrowBadFormat (); } if (bitsLeft < nbits) FillBitBuffer (nbits); return (int32) ((getBuffer >> (bitsLeft -= nbits)) & (0x0FFFF >> (16 - nbits))); } /*****************************************************************************/ inline int32 dng_lossless_decoder::get_bit () { if (!bitsLeft) FillBitBuffer (1); return (int32) ((getBuffer >> (--bitsLeft)) & 1); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * HuffDecode -- * * Taken from Figure F.16: extract next coded symbol from * input stream. This should becode a macro. * * Results: * Next coded symbol * * Side effects: * Bitstream is parsed. * *-------------------------------------------------------------- */ inline int32 dng_lossless_decoder::HuffDecode (HuffmanTable *htbl) { // If the huffman code is less than 8 bits, we can use the fast // table lookup to get its value. It's more than 8 bits about // 3-4% of the time. int32 code = show_bits8 (); if (htbl->numbits [code]) { flush_bits (htbl->numbits [code]); return htbl->value [code]; } else { flush_bits (8); int32 l = 8; while (code > htbl->maxcode [l]) { code = (code << 1) | get_bit (); l++; } // With garbage input we may reach the sentinel value l = 17. if (l > 16) { return 0; // fake a zero as the safest result } else { return htbl->huffval [htbl->valptr [l] + ((int32) (code - htbl->mincode [l]))]; } } } /*****************************************************************************/ /* *-------------------------------------------------------------- * * HuffExtend -- * * Code and table for Figure F.12: extend sign bit * * Results: * The extended value. * * Side effects: * None. * *-------------------------------------------------------------- */ inline void dng_lossless_decoder::HuffExtend (int32 &x, int32 s) { if (x < (0x08000 >> (16 - s))) { x += -(1 << s) + 1; } } /*****************************************************************************/ // Called from DecodeImage () to write one row. void dng_lossless_decoder::PmPutRow (MCU *buf, int32 numComp, int32 numCol, int32 /* row */) { uint16 *sPtr = &buf [0] [0]; uint32 pixels = numCol * numComp; fSpooler->Spool (sPtr, pixels * (uint32) sizeof (uint16)); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * DecodeFirstRow -- * * Decode the first raster line of samples at the start of * the scan and at the beginning of each restart interval. * This includes modifying the component value so the real * value, not the difference is returned. * * Results: * None. * * Side effects: * Bitstream is parsed. * *-------------------------------------------------------------- */ void dng_lossless_decoder::DecodeFirstRow (MCU *curRowBuf) { int32 compsInScan = info.compsInScan; // Process the first column in the row. for (int32 curComp = 0; curComp < compsInScan; curComp++) { int32 ci = info.MCUmembership [curComp]; JpegComponentInfo *compptr = info.curCompInfo [ci]; HuffmanTable *dctbl = info.dcHuffTblPtrs [compptr->dcTblNo]; // Section F.2.2.1: decode the difference int32 d = 0; int32 s = HuffDecode (dctbl); if (s) { if (s == 16 && !fBug16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } // Add the predictor to the difference. int32 Pr = info.dataPrecision; int32 Pt = info.Pt; curRowBuf [0] [curComp] = (ComponentType) (d + (1 << (Pr-Pt-1))); } // Process the rest of the row. int32 numCOL = info.imageWidth; for (int32 col = 1; col < numCOL; col++) { for (int32 curComp = 0; curComp < compsInScan; curComp++) { int32 ci = info.MCUmembership [curComp]; JpegComponentInfo *compptr = info.curCompInfo [ci]; HuffmanTable *dctbl = info.dcHuffTblPtrs [compptr->dcTblNo]; // Section F.2.2.1: decode the difference int32 d = 0; int32 s = HuffDecode (dctbl); if (s) { if (s == 16 && !fBug16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } // Add the predictor to the difference. curRowBuf [col] [curComp] = (ComponentType) (d + curRowBuf [col-1] [curComp]); } } // Update the restart counter if (info.restartInRows) { info.restartRowsToGo--; } } /*****************************************************************************/ /* *-------------------------------------------------------------- * * DecodeImage -- * * Decode the input stream. This includes modifying * the component value so the real value, not the * difference is returned. * * Results: * None. * * Side effects: * Bitstream is parsed. * *-------------------------------------------------------------- */ void dng_lossless_decoder::DecodeImage () { #define swap(type,a,b) {type c; c=(a); (a)=(b); (b)=c;} int32 numCOL = info.imageWidth; int32 numROW = info.imageHeight; int32 compsInScan = info.compsInScan; // Precompute the decoding table for each table. HuffmanTable *ht [4]; for (int32 curComp = 0; curComp < compsInScan; curComp++) { int32 ci = info.MCUmembership [curComp]; JpegComponentInfo *compptr = info.curCompInfo [ci]; ht [curComp] = info.dcHuffTblPtrs [compptr->dcTblNo]; } MCU *prevRowBuf = mcuROW1; MCU *curRowBuf = mcuROW2; #if qSupportCanon_sRAW // Canon sRAW support if (info.compInfo [0].hSampFactor == 2 && info.compInfo [0].vSampFactor == 1) { for (int32 row = 0; row < numROW; row++) { // Initialize predictors. int32 p0; int32 p1; int32 p2; if (row == 0) { p0 = 1 << 14; p1 = 1 << 14; p2 = 1 << 14; } else { p0 = prevRowBuf [0] [0]; p1 = prevRowBuf [0] [1]; p2 = prevRowBuf [0] [2]; } for (int32 col = 0; col < numCOL; col += 2) { // Read first luminance component. { int32 d = 0; int32 s = HuffDecode (ht [0]); if (s) { if (s == 16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } p0 += d; curRowBuf [col] [0] = (ComponentType) p0; } // Read second luminance component. { int32 d = 0; int32 s = HuffDecode (ht [0]); if (s) { if (s == 16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } p0 += d; curRowBuf [col + 1] [0] = (ComponentType) p0; } // Read first chroma component. { int32 d = 0; int32 s = HuffDecode (ht [1]); if (s) { if (s == 16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } p1 += d; curRowBuf [col ] [1] = (ComponentType) p1; curRowBuf [col + 1] [1] = (ComponentType) p1; } // Read second chroma component. { int32 d = 0; int32 s = HuffDecode (ht [2]); if (s) { if (s == 16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } p2 += d; curRowBuf [col ] [2] = (ComponentType) p2; curRowBuf [col + 1] [2] = (ComponentType) p2; } } PmPutRow (curRowBuf, compsInScan, numCOL, row); swap (MCU *, prevRowBuf, curRowBuf); } return; } if (info.compInfo [0].hSampFactor == 2 && info.compInfo [0].vSampFactor == 2) { for (int32 row = 0; row < numROW; row += 2) { // Initialize predictors. int32 p0; int32 p1; int32 p2; if (row == 0) { p0 = 1 << 14; p1 = 1 << 14; p2 = 1 << 14; } else { p0 = prevRowBuf [0] [0]; p1 = prevRowBuf [0] [1]; p2 = prevRowBuf [0] [2]; } for (int32 col = 0; col < numCOL; col += 2) { // Read first luminance component. { int32 d = 0; int32 s = HuffDecode (ht [0]); if (s) { if (s == 16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } p0 += d; prevRowBuf [col] [0] = (ComponentType) p0; } // Read second luminance component. { int32 d = 0; int32 s = HuffDecode (ht [0]); if (s) { if (s == 16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } p0 += d; prevRowBuf [col + 1] [0] = (ComponentType) p0; } // Read third luminance component. { int32 d = 0; int32 s = HuffDecode (ht [0]); if (s) { if (s == 16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } p0 += d; curRowBuf [col] [0] = (ComponentType) p0; } // Read fourth luminance component. { int32 d = 0; int32 s = HuffDecode (ht [0]); if (s) { if (s == 16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } p0 += d; curRowBuf [col + 1] [0] = (ComponentType) p0; } // Read first chroma component. { int32 d = 0; int32 s = HuffDecode (ht [1]); if (s) { if (s == 16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } p1 += d; prevRowBuf [col ] [1] = (ComponentType) p1; prevRowBuf [col + 1] [1] = (ComponentType) p1; curRowBuf [col ] [1] = (ComponentType) p1; curRowBuf [col + 1] [1] = (ComponentType) p1; } // Read second chroma component. { int32 d = 0; int32 s = HuffDecode (ht [2]); if (s) { if (s == 16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } p2 += d; prevRowBuf [col ] [2] = (ComponentType) p2; prevRowBuf [col + 1] [2] = (ComponentType) p2; curRowBuf [col ] [2] = (ComponentType) p2; curRowBuf [col + 1] [2] = (ComponentType) p2; } } PmPutRow (prevRowBuf, compsInScan, numCOL, row); PmPutRow (curRowBuf, compsInScan, numCOL, row); } return; } #endif #if qSupportHasselblad_3FR if (info.Ss == 8) { fHasselblad3FR = true; for (int32 row = 0; row < numROW; row++) { int32 p0 = 32768; int32 p1 = 32768; for (int32 col = 0; col < numCOL; col += 2) { int32 s0 = HuffDecode (ht [0]); int32 s1 = HuffDecode (ht [0]); if (s0) { int32 d = get_bits (s0); if (s0 == 16) { d = -32768; } else { HuffExtend (d, s0); } p0 += d; } if (s1) { int32 d = get_bits (s1); if (s1 == 16) { d = -32768; } else { HuffExtend (d, s1); } p1 += d; } curRowBuf [col ] [0] = (ComponentType) p0; curRowBuf [col + 1] [0] = (ComponentType) p1; } PmPutRow (curRowBuf, compsInScan, numCOL, row); } return; } #endif // Decode the first row of image. Output the row and // turn this row into a previous row for later predictor // calculation. DecodeFirstRow (mcuROW1); PmPutRow (mcuROW1, compsInScan, numCOL, 0); // Process each row. for (int32 row = 1; row < numROW; row++) { // Account for restart interval, process restart marker if needed. if (info.restartInRows) { if (info.restartRowsToGo == 0) { ProcessRestart (); // Reset predictors at restart. DecodeFirstRow (curRowBuf); PmPutRow (curRowBuf, compsInScan, numCOL, row); swap (MCU *, prevRowBuf, curRowBuf); continue; } info.restartRowsToGo--; } // The upper neighbors are predictors for the first column. for (int32 curComp = 0; curComp < compsInScan; curComp++) { // Section F.2.2.1: decode the difference int32 d = 0; int32 s = HuffDecode (ht [curComp]); if (s) { if (s == 16 && !fBug16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } // First column of row above is predictor for first column. curRowBuf [0] [curComp] = (ComponentType) (d + prevRowBuf [0] [curComp]); } // For the rest of the column on this row, predictor // calculations are based on PSV. if (compsInScan == 2 && info.Ss == 1) { // This is the combination used by both the Canon and Kodak raw formats. // Unrolling the general case logic results in a significant speed increase. uint16 *dPtr = &curRowBuf [1] [0]; int32 prev0 = dPtr [-2]; int32 prev1 = dPtr [-1]; for (int32 col = 1; col < numCOL; col++) { int32 s = HuffDecode (ht [0]); if (s) { int32 d; if (s == 16 && !fBug16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } prev0 += d; } s = HuffDecode (ht [1]); if (s) { int32 d; if (s == 16 && !fBug16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } prev1 += d; } dPtr [0] = (uint16) prev0; dPtr [1] = (uint16) prev1; dPtr += 2; } } else { for (int32 col = 1; col < numCOL; col++) { for (int32 curComp = 0; curComp < compsInScan; curComp++) { // Section F.2.2.1: decode the difference int32 d = 0; int32 s = HuffDecode (ht [curComp]); if (s) { if (s == 16 && !fBug16) { d = -32768; } else { d = get_bits (s); HuffExtend (d, s); } } // Predict the pixel value. int32 predictor = QuickPredict (col, curComp, curRowBuf, prevRowBuf); // Save the difference. curRowBuf [col] [curComp] = (ComponentType) (d + predictor); } } } PmPutRow (curRowBuf, compsInScan, numCOL, row); swap (MCU *, prevRowBuf, curRowBuf); } #undef swap } /*****************************************************************************/ void dng_lossless_decoder::StartRead (uint32 &imageWidth, uint32 &imageHeight, uint32 &imageChannels) { ReadFileHeader (); ReadScanHeader (); DecoderStructInit (); HuffDecoderInit (); imageWidth = info.imageWidth; imageHeight = info.imageHeight; imageChannels = info.compsInScan; } /*****************************************************************************/ void dng_lossless_decoder::FinishRead () { DecodeImage (); } /*****************************************************************************/ void DecodeLosslessJPEG (dng_stream &stream, dng_spooler &spooler, uint32 minDecodedSize, uint32 maxDecodedSize, bool bug16) { dng_lossless_decoder decoder (&stream, &spooler, bug16); uint32 imageWidth; uint32 imageHeight; uint32 imageChannels; decoder.StartRead (imageWidth, imageHeight, imageChannels); uint32 decodedSize = imageWidth * imageHeight * imageChannels * (uint32) sizeof (uint16); if (decodedSize < minDecodedSize || decodedSize > maxDecodedSize) { ThrowBadFormat (); } decoder.FinishRead (); } /*****************************************************************************/ class dng_lossless_encoder { private: const uint16 *fSrcData; uint32 fSrcRows; uint32 fSrcCols; uint32 fSrcChannels; uint32 fSrcBitDepth; int32 fSrcRowStep; int32 fSrcColStep; dng_stream &fStream; HuffmanTable huffTable [4]; uint32 freqCount [4] [257]; // Current bit-accumulation buffer int32 huffPutBuffer; int32 huffPutBits; // Lookup table for number of bits in an 8 bit value. int numBitsTable [256]; public: dng_lossless_encoder (const uint16 *srcData, uint32 srcRows, uint32 srcCols, uint32 srcChannels, uint32 srcBitDepth, int32 srcRowStep, int32 srcColStep, dng_stream &stream); void Encode (); private: void EmitByte (uint8 value); void EmitBits (int code, int size); void FlushBits (); void CountOneDiff (int diff, uint32 *countTable); void EncodeOneDiff (int diff, HuffmanTable *dctbl); void FreqCountSet (); void HuffEncode (); void GenHuffCoding (HuffmanTable *htbl, uint32 *freq); void HuffOptimize (); void EmitMarker (JpegMarker mark); void Emit2bytes (int value); void EmitDht (int index); void EmitSof (JpegMarker code); void EmitSos (); void WriteFileHeader (); void WriteScanHeader (); void WriteFileTrailer (); }; /*****************************************************************************/ dng_lossless_encoder::dng_lossless_encoder (const uint16 *srcData, uint32 srcRows, uint32 srcCols, uint32 srcChannels, uint32 srcBitDepth, int32 srcRowStep, int32 srcColStep, dng_stream &stream) : fSrcData (srcData ) , fSrcRows (srcRows ) , fSrcCols (srcCols ) , fSrcChannels (srcChannels) , fSrcBitDepth (srcBitDepth) , fSrcRowStep (srcRowStep ) , fSrcColStep (srcColStep ) , fStream (stream ) , huffPutBuffer (0) , huffPutBits (0) { // Initialize number of bits lookup table. numBitsTable [0] = 0; for (int i = 1; i < 256; i++) { int temp = i; int nbits = 1; while (temp >>= 1) { nbits++; } numBitsTable [i] = nbits; } } /*****************************************************************************/ inline void dng_lossless_encoder::EmitByte (uint8 value) { fStream.Put_uint8 (value); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * EmitBits -- * * Code for outputting bits to the file * * Only the right 24 bits of huffPutBuffer are used; the valid * bits are left-justified in this part. At most 16 bits can be * passed to EmitBits in one call, and we never retain more than 7 * bits in huffPutBuffer between calls, so 24 bits are * sufficient. * * Results: * None. * * Side effects: * huffPutBuffer and huffPutBits are updated. * *-------------------------------------------------------------- */ inline void dng_lossless_encoder::EmitBits (int code, int size) { DNG_ASSERT (size != 0, "Bad Huffman table entry"); int putBits = size; int putBuffer = code; putBits += huffPutBits; putBuffer <<= 24 - putBits; putBuffer |= huffPutBuffer; while (putBits >= 8) { uint8 c = (uint8) (putBuffer >> 16); // Output whole bytes we've accumulated with byte stuffing EmitByte (c); if (c == 0xFF) { EmitByte (0); } putBuffer <<= 8; putBits -= 8; } huffPutBuffer = putBuffer; huffPutBits = putBits; } /*****************************************************************************/ /* *-------------------------------------------------------------- * * FlushBits -- * * Flush any remaining bits in the bit buffer. Used before emitting * a marker. * * Results: * None. * * Side effects: * huffPutBuffer and huffPutBits are reset * *-------------------------------------------------------------- */ void dng_lossless_encoder::FlushBits () { // The first call forces output of any partial bytes. EmitBits (0x007F, 7); // We can then zero the buffer. huffPutBuffer = 0; huffPutBits = 0; } /*****************************************************************************/ /* *-------------------------------------------------------------- * * CountOneDiff -- * * Count the difference value in countTable. * * Results: * diff is counted in countTable. * * Side effects: * None. * *-------------------------------------------------------------- */ inline void dng_lossless_encoder::CountOneDiff (int diff, uint32 *countTable) { // Encode the DC coefficient difference per section F.1.2.1 int temp = diff; if (temp < 0) { temp = -temp; } // Find the number of bits needed for the magnitude of the coefficient int nbits = temp >= 256 ? numBitsTable [temp >> 8 ] + 8 : numBitsTable [temp & 0xFF]; // Update count for this bit length countTable [nbits] ++; } /*****************************************************************************/ /* *-------------------------------------------------------------- * * EncodeOneDiff -- * * Encode a single difference value. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ inline void dng_lossless_encoder::EncodeOneDiff (int diff, HuffmanTable *dctbl) { // Encode the DC coefficient difference per section F.1.2.1 int temp = diff; int temp2 = diff; if (temp < 0) { temp = -temp; // For a negative input, want temp2 = bitwise complement of // abs (input). This code assumes we are on a two's complement // machine. temp2--; } // Find the number of bits needed for the magnitude of the coefficient int nbits = temp >= 256 ? numBitsTable [temp >> 8 ] + 8 : numBitsTable [temp & 0xFF]; // Emit the Huffman-coded symbol for the number of bits EmitBits (dctbl->ehufco [nbits], dctbl->ehufsi [nbits]); // Emit that number of bits of the value, if positive, // or the complement of its magnitude, if negative. // If the number of bits is 16, there is only one possible difference // value (-32786), so the lossless JPEG spec says not to output anything // in that case. So we only need to output the diference value if // the number of bits is between 1 and 15. if (nbits & 15) { EmitBits (temp2 & (0x0FFFF >> (16 - nbits)), nbits); } } /*****************************************************************************/ /* *-------------------------------------------------------------- * * FreqCountSet -- * * Count the times each category symbol occurs in this image. * * Results: * None. * * Side effects: * The freqCount has counted all category * symbols appeared in the image. * *-------------------------------------------------------------- */ void dng_lossless_encoder::FreqCountSet () { memset (freqCount, 0, sizeof (freqCount)); DNG_ASSERT ((int32)fSrcRows >= 0, "dng_lossless_encoder::FreqCountSet: fSrcRpws too large."); for (int32 row = 0; row < (int32)fSrcRows; row++) { const uint16 *sPtr = fSrcData + row * fSrcRowStep; // Initialize predictors for this row. int32 predictor [4]; for (int32 channel = 0; channel < (int32)fSrcChannels; channel++) { if (row == 0) predictor [channel] = 1 << (fSrcBitDepth - 1); else predictor [channel] = sPtr [channel - fSrcRowStep]; } // Unroll most common case of two channels if (fSrcChannels == 2) { int32 pred0 = predictor [0]; int32 pred1 = predictor [1]; uint32 srcCols = fSrcCols; int32 srcColStep = fSrcColStep; for (uint32 col = 0; col < srcCols; col++) { int32 pixel0 = sPtr [0]; int32 pixel1 = sPtr [1]; int16 diff0 = (int16) (pixel0 - pred0); int16 diff1 = (int16) (pixel1 - pred1); CountOneDiff (diff0, freqCount [0]); CountOneDiff (diff1, freqCount [1]); pred0 = pixel0; pred1 = pixel1; sPtr += srcColStep; } } // General case. else { for (uint32 col = 0; col < fSrcCols; col++) { for (uint32 channel = 0; channel < fSrcChannels; channel++) { int32 pixel = sPtr [channel]; int16 diff = (int16) (pixel - predictor [channel]); CountOneDiff (diff, freqCount [channel]); predictor [channel] = pixel; } sPtr += fSrcColStep; } } } } /*****************************************************************************/ /* *-------------------------------------------------------------- * * HuffEncode -- * * Encode and output Huffman-compressed image data. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ void dng_lossless_encoder::HuffEncode () { DNG_ASSERT ((int32)fSrcRows >= 0, "dng_lossless_encoder::HuffEncode: fSrcRows too large."); for (int32 row = 0; row < (int32)fSrcRows; row++) { const uint16 *sPtr = fSrcData + row * fSrcRowStep; // Initialize predictors for this row. int32 predictor [4]; for (int32 channel = 0; channel < (int32)fSrcChannels; channel++) { if (row == 0) predictor [channel] = 1 << (fSrcBitDepth - 1); else predictor [channel] = sPtr [channel - fSrcRowStep]; } // Unroll most common case of two channels if (fSrcChannels == 2) { int32 pred0 = predictor [0]; int32 pred1 = predictor [1]; uint32 srcCols = fSrcCols; int32 srcColStep = fSrcColStep; for (uint32 col = 0; col < srcCols; col++) { int32 pixel0 = sPtr [0]; int32 pixel1 = sPtr [1]; int16 diff0 = (int16) (pixel0 - pred0); int16 diff1 = (int16) (pixel1 - pred1); EncodeOneDiff (diff0, &huffTable [0]); EncodeOneDiff (diff1, &huffTable [1]); pred0 = pixel0; pred1 = pixel1; sPtr += srcColStep; } } // General case. else { for (uint32 col = 0; col < fSrcCols; col++) { for (uint32 channel = 0; channel < fSrcChannels; channel++) { int32 pixel = sPtr [channel]; int16 diff = (int16) (pixel - predictor [channel]); EncodeOneDiff (diff, &huffTable [channel]); predictor [channel] = pixel; } sPtr += fSrcColStep; } } } FlushBits (); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * GenHuffCoding -- * * Generate the optimal coding for the given counts. * This algorithm is explained in section K.2 of the * JPEG standard. * * Results: * htbl->bits and htbl->huffval are constructed. * * Side effects: * None. * *-------------------------------------------------------------- */ void dng_lossless_encoder::GenHuffCoding (HuffmanTable *htbl, uint32 *freq) { int i; int j; const int MAX_CLEN = 32; // assumed maximum initial code length uint8 bits [MAX_CLEN + 1]; // bits [k] = # of symbols with code length k short codesize [257]; // codesize [k] = code length of symbol k short others [257]; // next symbol in current branch of tree memset (bits , 0, sizeof (bits )); memset (codesize, 0, sizeof (codesize)); for (i = 0; i < 257; i++) others [i] = -1; // init links to empty // Including the pseudo-symbol 256 in the Huffman procedure guarantees // that no real symbol is given code-value of all ones, because 256 // will be placed in the largest codeword category. freq [256] = 1; // make sure there is a nonzero count // Huffman's basic algorithm to assign optimal code lengths to symbols while (true) { // Find the smallest nonzero frequency, set c1 = its symbol. // In case of ties, take the larger symbol number. int c1 = -1; uint32 v = 0xFFFFFFFF; for (i = 0; i <= 256; i++) { if (freq [i] && freq [i] <= v) { v = freq [i]; c1 = i; } } // Find the next smallest nonzero frequency, set c2 = its symbol. // In case of ties, take the larger symbol number. int c2 = -1; v = 0xFFFFFFFF; for (i = 0; i <= 256; i++) { if (freq [i] && freq [i] <= v && i != c1) { v = freq [i]; c2 = i; } } // Done if we've merged everything into one frequency. if (c2 < 0) break; // Else merge the two counts/trees. freq [c1] += freq [c2]; freq [c2] = 0; // Increment the codesize of everything in c1's tree branch. codesize [c1] ++; while (others [c1] >= 0) { c1 = others [c1]; codesize [c1] ++; } // chain c2 onto c1's tree branch others [c1] = (short) c2; // Increment the codesize of everything in c2's tree branch. codesize [c2] ++; while (others [c2] >= 0) { c2 = others [c2]; codesize [c2] ++; } } // Now count the number of symbols of each code length. for (i = 0; i <= 256; i++) { if (codesize [i]) { // The JPEG standard seems to think that this can't happen, // but I'm paranoid... if (codesize [i] > MAX_CLEN) { DNG_REPORT ("Huffman code size table overflow"); ThrowProgramError (); } bits [codesize [i]]++; } } // JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure // Huffman procedure assigned any such lengths, we must adjust the coding. // Here is what the JPEG spec says about how this next bit works: // Since symbols are paired for the longest Huffman code, the symbols are // removed from this length category two at a time. The prefix for the pair // (which is one bit shorter) is allocated to one of the pair; then, // skipping the BITS entry for that prefix length, a code word from the next // shortest nonzero BITS entry is converted into a prefix for two code words // one bit longer. for (i = MAX_CLEN; i > 16; i--) { while (bits [i] > 0) { // Kludge: I have never been able to test this logic, and there // are comments on the web that this encoder has bugs with 16-bit // data, so just throw an error if we get here and revert to a // default table. - tknoll 12/1/03. DNG_REPORT ("Info: Optimal huffman table bigger than 16 bits"); ThrowProgramError (); // Original logic: j = i - 2; // find length of new prefix to be used while (bits [j] == 0) j--; bits [i ] -= 2; // remove two symbols bits [i - 1] ++; // one goes in this length bits [j + 1] += 2; // two new symbols in this length bits [j ] --; // symbol of this length is now a prefix } } // Remove the count for the pseudo-symbol 256 from // the largest codelength. while (bits [i] == 0) // find largest codelength still in use i--; bits [i] --; // Return final symbol counts (only for lengths 0..16). memcpy (htbl->bits, bits, sizeof (htbl->bits)); // Return a list of the symbols sorted by code length. // It's not real clear to me why we don't need to consider the codelength // changes made above, but the JPEG spec seems to think this works. int p = 0; for (i = 1; i <= MAX_CLEN; i++) { for (j = 0; j <= 255; j++) { if (codesize [j] == i) { htbl->huffval [p] = (uint8) j; p++; } } } } /*****************************************************************************/ /* *-------------------------------------------------------------- * * HuffOptimize -- * * Find the best coding parameters for a Huffman-coded scan. * When called, the scan data has already been converted to * a sequence of MCU groups of source image samples, which * are stored in a "big" array, mcuTable. * * It counts the times each category symbol occurs. Based on * this counting, optimal Huffman tables are built. Then it * uses this optimal Huffman table and counting table to find * the best PSV. * * Results: * Optimal Huffman tables are retured in cPtr->dcHuffTblPtrs[tbl]. * Best PSV is retured in cPtr->Ss. * * Side effects: * None. * *-------------------------------------------------------------- */ void dng_lossless_encoder::HuffOptimize () { // Collect the frequency counts. FreqCountSet (); // Generate Huffman encoding tables. for (uint32 channel = 0; channel < fSrcChannels; channel++) { try { GenHuffCoding (&huffTable [channel], freqCount [channel]); } catch (...) { DNG_REPORT ("Info: Reverting to default huffman table"); for (uint32 j = 0; j <= 256; j++) { freqCount [channel] [j] = (j <= 16 ? 1 : 0); } GenHuffCoding (&huffTable [channel], freqCount [channel]); } FixHuffTbl (&huffTable [channel]); } } /*****************************************************************************/ /* *-------------------------------------------------------------- * * EmitMarker -- * * Emit a marker code into the output stream. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ void dng_lossless_encoder::EmitMarker (JpegMarker mark) { EmitByte (0xFF); EmitByte ((uint8) mark); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * Emit2bytes -- * * Emit a 2-byte integer; these are always MSB first in JPEG * files * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ void dng_lossless_encoder::Emit2bytes (int value) { EmitByte ((value >> 8) & 0xFF); EmitByte (value & 0xFF); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * EmitDht -- * * Emit a DHT marker, follwed by the huffman data. * * Results: * None * * Side effects: * None * *-------------------------------------------------------------- */ void dng_lossless_encoder::EmitDht (int index) { int i; HuffmanTable *htbl = &huffTable [index]; EmitMarker (M_DHT); int length = 0; for (i = 1; i <= 16; i++) length += htbl->bits [i]; Emit2bytes (length + 2 + 1 + 16); EmitByte ((uint8) index); for (i = 1; i <= 16; i++) EmitByte (htbl->bits [i]); for (i = 0; i < length; i++) EmitByte (htbl->huffval [i]); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * EmitSof -- * * Emit a SOF marker plus data. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ void dng_lossless_encoder::EmitSof (JpegMarker code) { EmitMarker (code); Emit2bytes (3 * fSrcChannels + 2 + 5 + 1); // length EmitByte ((uint8) fSrcBitDepth); Emit2bytes (fSrcRows); Emit2bytes (fSrcCols); EmitByte ((uint8) fSrcChannels); for (uint32 i = 0; i < fSrcChannels; i++) { EmitByte ((uint8) i); EmitByte ((uint8) ((1 << 4) + 1)); // Not subsampled. EmitByte (0); // Tq shall be 0 for lossless. } } /*****************************************************************************/ /* *-------------------------------------------------------------- * * EmitSos -- * * Emit a SOS marker plus data. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ void dng_lossless_encoder::EmitSos () { EmitMarker (M_SOS); Emit2bytes (2 * fSrcChannels + 2 + 1 + 3); // length EmitByte ((uint8) fSrcChannels); // Ns for (uint32 i = 0; i < fSrcChannels; i++) { // Cs,Td,Ta EmitByte ((uint8) i); EmitByte ((uint8) (i << 4)); } EmitByte (1); // PSV - hardcoded - tknoll EmitByte (0); // Spectral selection end - Se EmitByte (0); // The point transform parameter } /*****************************************************************************/ /* *-------------------------------------------------------------- * * WriteFileHeader -- * * Write the file header. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ void dng_lossless_encoder::WriteFileHeader () { EmitMarker (M_SOI); // first the SOI EmitSof (M_SOF3); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * WriteScanHeader -- * * Write the start of a scan (everything through the SOS marker). * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ void dng_lossless_encoder::WriteScanHeader () { // Emit Huffman tables. for (uint32 i = 0; i < fSrcChannels; i++) { EmitDht (i); } EmitSos (); } /*****************************************************************************/ /* *-------------------------------------------------------------- * * WriteFileTrailer -- * * Write the End of image marker at the end of a JPEG file. * * Results: * None. * * Side effects: * None. * *-------------------------------------------------------------- */ void dng_lossless_encoder::WriteFileTrailer () { EmitMarker (M_EOI); } /*****************************************************************************/ void dng_lossless_encoder::Encode () { DNG_ASSERT (fSrcChannels <= 4, "Too many components in scan"); // Count the times each difference category occurs. // Construct the optimal Huffman table. HuffOptimize (); // Write the frame and scan headers. WriteFileHeader (); WriteScanHeader (); // Encode the image. HuffEncode (); // Clean up everything. WriteFileTrailer (); } /*****************************************************************************/ void EncodeLosslessJPEG (const uint16 *srcData, uint32 srcRows, uint32 srcCols, uint32 srcChannels, uint32 srcBitDepth, int32 srcRowStep, int32 srcColStep, dng_stream &stream) { dng_lossless_encoder encoder (srcData, srcRows, srcCols, srcChannels, srcBitDepth, srcRowStep, srcColStep, stream); encoder.Encode (); } /*****************************************************************************/