/*****************************************************************************/
// 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 ();
}
/*****************************************************************************/