/*****************************************************************************/
// Copyright 2006-2012 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_read_image.cpp#7 $ */
/* $DateTime: 2012/07/31 22:04:34 $ */
/* $Change: 840853 $ */
/* $Author: tknoll $ */
/*****************************************************************************/
#include "dng_read_image.h"
#include "dng_abort_sniffer.h"
#include "dng_area_task.h"
#include "dng_bottlenecks.h"
#include "dng_exceptions.h"
#include "dng_flags.h"
#include "dng_host.h"
#include "dng_image.h"
#include "dng_ifd.h"
#include "dng_jpeg_image.h"
#include "dng_lossless_jpeg.h"
#include "dng_mutex.h"
#include "dng_memory.h"
#include "dng_pixel_buffer.h"
#include "dng_safe_arithmetic.h"
#include "dng_tag_types.h"
#include "dng_tag_values.h"
#include "dng_utils.h"
#include "zlib.h"
#if qDNGUseLibJPEG
#include "dng_jpeg_memory_source.h"
#include "dng_jpeglib.h"
#endif
#include <limits>
/******************************************************************************/
static void DecodeDelta8 (uint8 *dPtr,
uint32 rows,
uint32 cols,
uint32 channels)
{
const uint32 dRowStep = cols * channels;
for (uint32 row = 0; row < rows; row++)
{
for (uint32 col = 1; col < cols; col++)
{
for (uint32 channel = 0; channel < channels; channel++)
{
dPtr [col * channels + channel] += dPtr [(col - 1) * channels + channel];
}
}
dPtr += dRowStep;
}
}
/******************************************************************************/
static void DecodeDelta16 (uint16 *dPtr,
uint32 rows,
uint32 cols,
uint32 channels)
{
const uint32 dRowStep = cols * channels;
for (uint32 row = 0; row < rows; row++)
{
for (uint32 col = 1; col < cols; col++)
{
for (uint32 channel = 0; channel < channels; channel++)
{
dPtr [col * channels + channel] += dPtr [(col - 1) * channels + channel];
}
}
dPtr += dRowStep;
}
}
/******************************************************************************/
static void DecodeDelta32 (uint32 *dPtr,
uint32 rows,
uint32 cols,
uint32 channels)
{
const uint32 dRowStep = cols * channels;
for (uint32 row = 0; row < rows; row++)
{
for (uint32 col = 1; col < cols; col++)
{
for (uint32 channel = 0; channel < channels; channel++)
{
dPtr [col * channels + channel] += dPtr [(col - 1) * channels + channel];
}
}
dPtr += dRowStep;
}
}
/*****************************************************************************/
inline void DecodeDeltaBytes (uint8 *bytePtr, int32 cols, int32 channels)
{
if (channels == 1)
{
uint8 b0 = bytePtr [0];
bytePtr += 1;
for (int32 col = 1; col < cols; ++col)
{
b0 += bytePtr [0];
bytePtr [0] = b0;
bytePtr += 1;
}
}
else if (channels == 3)
{
uint8 b0 = bytePtr [0];
uint8 b1 = bytePtr [1];
uint8 b2 = bytePtr [2];
bytePtr += 3;
for (int32 col = 1; col < cols; ++col)
{
b0 += bytePtr [0];
b1 += bytePtr [1];
b2 += bytePtr [2];
bytePtr [0] = b0;
bytePtr [1] = b1;
bytePtr [2] = b2;
bytePtr += 3;
}
}
else if (channels == 4)
{
uint8 b0 = bytePtr [0];
uint8 b1 = bytePtr [1];
uint8 b2 = bytePtr [2];
uint8 b3 = bytePtr [3];
bytePtr += 4;
for (int32 col = 1; col < cols; ++col)
{
b0 += bytePtr [0];
b1 += bytePtr [1];
b2 += bytePtr [2];
b3 += bytePtr [3];
bytePtr [0] = b0;
bytePtr [1] = b1;
bytePtr [2] = b2;
bytePtr [3] = b3;
bytePtr += 4;
}
}
else
{
for (int32 col = 1; col < cols; ++col)
{
for (int32 chan = 0; chan < channels; ++chan)
{
bytePtr [chan + channels] += bytePtr [chan];
}
bytePtr += channels;
}
}
}
/*****************************************************************************/
static void DecodeFPDelta (uint8 *input,
uint8 *output,
int32 cols,
int32 channels,
int32 bytesPerSample)
{
DecodeDeltaBytes (input, cols * bytesPerSample, channels);
int32 rowIncrement = cols * channels;
if (bytesPerSample == 2)
{
#if qDNGBigEndian
const uint8 *input0 = input;
const uint8 *input1 = input + rowIncrement;
#else
const uint8 *input1 = input;
const uint8 *input0 = input + rowIncrement;
#endif
for (int32 col = 0; col < rowIncrement; ++col)
{
output [0] = input0 [col];
output [1] = input1 [col];
output += 2;
}
}
else if (bytesPerSample == 3)
{
const uint8 *input0 = input;
const uint8 *input1 = input + rowIncrement;
const uint8 *input2 = input + rowIncrement * 2;
for (int32 col = 0; col < rowIncrement; ++col)
{
output [0] = input0 [col];
output [1] = input1 [col];
output [2] = input2 [col];
output += 3;
}
}
else
{
#if qDNGBigEndian
const uint8 *input0 = input;
const uint8 *input1 = input + rowIncrement;
const uint8 *input2 = input + rowIncrement * 2;
const uint8 *input3 = input + rowIncrement * 3;
#else
const uint8 *input3 = input;
const uint8 *input2 = input + rowIncrement;
const uint8 *input1 = input + rowIncrement * 2;
const uint8 *input0 = input + rowIncrement * 3;
#endif
for (int32 col = 0; col < rowIncrement; ++col)
{
output [0] = input0 [col];
output [1] = input1 [col];
output [2] = input2 [col];
output [3] = input3 [col];
output += 4;
}
}
}
/*****************************************************************************/
bool DecodePackBits (dng_stream &stream,
uint8 *dPtr,
int32 dstCount)
{
while (dstCount > 0)
{
int32 runCount = (int8) stream.Get_uint8 ();
if (runCount >= 0)
{
++runCount;
dstCount -= runCount;
if (dstCount < 0)
return false;
stream.Get (dPtr, runCount);
dPtr += runCount;
}
else
{
runCount = -runCount + 1;
dstCount -= runCount;
if (dstCount < 0)
return false;
uint8 x = stream.Get_uint8 ();
while (runCount--)
{
*(dPtr++) = x;
}
}
}
return true;
}
/******************************************************************************/
class dng_lzw_expander
{
private:
enum
{
kResetCode = 256,
kEndCode = 257,
kTableSize = 4096
};
struct LZWExpanderNode
{
int16 prefix;
int16 final;
int16 depth;
int16 fake_for_padding;
};
dng_memory_data fBuffer;
LZWExpanderNode *fTable;
const uint8 *fSrcPtr;
int32 fSrcCount;
int32 fByteOffset;
uint32 fBitBuffer;
int32 fBitBufferCount;
int32 fNextCode;
int32 fCodeSize;
public:
dng_lzw_expander ();
bool Expand (const uint8 *sPtr,
uint8 *dPtr,
int32 sCount,
int32 dCount);
private:
void InitTable ();
void AddTable (int32 w, int32 k);
bool GetCodeWord (int32 &code);
// Hidden copy constructor and assignment operator.
dng_lzw_expander (const dng_lzw_expander &expander);
dng_lzw_expander & operator= (const dng_lzw_expander &expander);
};
/******************************************************************************/
dng_lzw_expander::dng_lzw_expander ()
: fBuffer ()
, fTable (NULL)
, fSrcPtr (NULL)
, fSrcCount (0)
, fByteOffset (0)
, fBitBuffer (0)
, fBitBufferCount (0)
, fNextCode (0)
, fCodeSize (0)
{
fBuffer.Allocate (kTableSize * sizeof (LZWExpanderNode));
fTable = (LZWExpanderNode *) fBuffer.Buffer ();
}
/******************************************************************************/
void dng_lzw_expander::InitTable ()
{
fCodeSize = 9;
fNextCode = 258;
LZWExpanderNode *node = &fTable [0];
for (int32 code = 0; code < 256; code++)
{
node->prefix = -1;
node->final = (int16) code;
node->depth = 1;
node++;
}
}
/******************************************************************************/
void dng_lzw_expander::AddTable (int32 w, int32 k)
{
DNG_ASSERT ((w >= 0) && (w <= kTableSize),
"bad w value in dng_lzw_expander::AddTable");
LZWExpanderNode *parentNode = &fTable [w];
int32 nextCode = fNextCode;
fNextCode++;
DNG_ASSERT ((nextCode >= 0) && (nextCode <= kTableSize),
"bad fNextCode value in dng_lzw_expander::AddTable");
LZWExpanderNode *node = &fTable [nextCode];
node->prefix = (int16) w;
node->final = (int16) k;
node->depth = 1 + parentNode->depth;
if (nextCode + 1 == (1 << fCodeSize) - 1)
{
if (fCodeSize != 12)
fCodeSize++;
}
}
/******************************************************************************/
bool dng_lzw_expander::GetCodeWord (int32 &code)
{
// The bit buffer has the current code in the most significant bits,
// so shift off the low orders.
int32 codeSize = fCodeSize;
code = fBitBuffer >> (32 - codeSize);
if (fBitBufferCount >= codeSize)
{
// Typical case; get the code from the bit buffer.
fBitBuffer <<= codeSize;
fBitBufferCount -= codeSize;
}
else
{
// The buffer needs to be refreshed.
const int32 bitsSoFar = fBitBufferCount;
if (fByteOffset >= fSrcCount)
return false;
// Buffer a long word
const uint8 *ptr = fSrcPtr + fByteOffset;
#if qDNGBigEndian
fBitBuffer = *((const uint32 *) ptr);
#else
{
uint32 b0 = ptr [0];
uint32 b1 = ptr [1];
uint32 b2 = ptr [2];
uint32 b3 = ptr [3];
fBitBuffer = (((((b0 << 8) | b1) << 8) | b2) << 8) | b3;
}
#endif
fBitBufferCount = 32;
fByteOffset += 4;
// Number of additional bits we need
const int32 bitsUsed = codeSize - bitsSoFar;
// Number of low order bits in the current buffer we don't care about
const int32 bitsNotUsed = 32 - bitsUsed;
code |= fBitBuffer >> bitsNotUsed;
fBitBuffer <<= bitsUsed;
fBitBufferCount -= bitsUsed;
}
return true;
}
/******************************************************************************/
bool dng_lzw_expander::Expand (const uint8 *sPtr,
uint8 *dPtr,
int32 sCount,
int32 dCount)
{
void *dStartPtr = dPtr;
fSrcPtr = sPtr;
fSrcCount = sCount;
fByteOffset = 0;
/* the master decode loop */
while (true)
{
InitTable ();
int32 code;
do
{
if (!GetCodeWord (code))
return false;
DNG_ASSERT (code <= fNextCode,
"Unexpected LZW code in dng_lzw_expander::Expand");
}
while (code == kResetCode);
if (code == kEndCode)
return true;
if (code > kEndCode)
return false;
int32 oldCode = code;
int32 inChar = code;
*(dPtr++) = (uint8) code;
if (--dCount == 0)
return true;
while (true)
{
if (!GetCodeWord (code))
return false;
if (code == kResetCode)
break;
if (code == kEndCode)
return true;
const int32 inCode = code;
bool repeatLastPixel = false;
if (code >= fNextCode)
{
// This is either a bad file or our code table is not big enough; we
// are going to repeat the last code seen and attempt to muddle thru.
code = oldCode;
repeatLastPixel = true;
}
// this can only happen if we hit 2 bad codes in a row
if (code > fNextCode)
return false;
const int32 depth = fTable [code].depth;
if (depth < dCount)
{
dCount -= depth;
dPtr += depth;
uint8 *ptr = dPtr;
// give the compiler an extra hint to optimize these as registers
const LZWExpanderNode *localTable = fTable;
int32 localCode = code;
// this is usually the hottest loop in LZW expansion
while (localCode >= kResetCode)
{
if (ptr <= dStartPtr)
return false; // about to trash memory
const LZWExpanderNode &node = localTable [localCode];
uint8 tempFinal = (uint8) node.final;
localCode = node.prefix;
// Check for bogus table entry
if (localCode < 0 || localCode > kTableSize)
return false;
*(--ptr) = tempFinal;
}
code = localCode;
inChar = localCode;
if (ptr <= dStartPtr)
return false; // about to trash memory
*(--ptr) = (uint8) inChar;
}
else
{
// There might not be enough room for the full code
// so skip the end of it.
const int32 skip = depth - dCount;
for (int32 i = 0; i < skip ; i++)
{
const LZWExpanderNode &node = fTable [code];
code = node.prefix;
}
int32 depthUsed = depth - skip;
dCount -= depthUsed;
dPtr += depthUsed;
uint8 *ptr = dPtr;
while (code >= 0)
{
if (ptr <= dStartPtr)
return false; // about to trash memory
const LZWExpanderNode &node = fTable [code];
*(--ptr) = (uint8) node.final;
code = node.prefix;
// Check for bogus table entry
if (code > kTableSize)
return false;
}
return true;
}
if (repeatLastPixel)
{
*(dPtr++) = (uint8) inChar;
if (--dCount == 0)
return true;
}
if (fNextCode < kTableSize)
{
AddTable (oldCode, code);
}
oldCode = inCode;
}
}
return false;
}
/*****************************************************************************/
dng_row_interleaved_image::dng_row_interleaved_image (dng_image &image,
uint32 factor)
: dng_image (image.Bounds (),
image.Planes (),
image.PixelType ())
, fImage (image )
, fFactor (factor)
{
}
/*****************************************************************************/
int32 dng_row_interleaved_image::MapRow (int32 row) const
{
uint32 rows = Height ();
int32 top = Bounds ().t;
uint32 fieldRow = row - top;
for (uint32 field = 0; true; field++)
{
uint32 fieldRows = (rows - field + fFactor - 1) / fFactor;
if (fieldRow < fieldRows)
{
return fieldRow * fFactor + field + top;
}
fieldRow -= fieldRows;
}
ThrowProgramError ();
return 0;
}
/*****************************************************************************/
void dng_row_interleaved_image::DoGet (dng_pixel_buffer &buffer) const
{
dng_pixel_buffer tempBuffer (buffer);
for (int32 row = buffer.fArea.t; row < buffer.fArea.b; row++)
{
tempBuffer.fArea.t = MapRow (row);
tempBuffer.fArea.b = tempBuffer.fArea.t + 1;
tempBuffer.fData = (void *) buffer.DirtyPixel (row,
buffer.fArea.l,
buffer.fPlane);
fImage.Get (tempBuffer);
}
}
/*****************************************************************************/
void dng_row_interleaved_image::DoPut (const dng_pixel_buffer &buffer)
{
dng_pixel_buffer tempBuffer (buffer);
for (int32 row = buffer.fArea.t; row < buffer.fArea.b; row++)
{
tempBuffer.fArea.t = MapRow (row);
tempBuffer.fArea.b = tempBuffer.fArea.t + 1;
tempBuffer.fData = (void *) buffer.ConstPixel (row,
buffer.fArea.l,
buffer.fPlane);
fImage.Put (tempBuffer);
}
}
/*****************************************************************************/
static void ReorderSubTileBlocks (dng_host &host,
const dng_ifd &ifd,
dng_pixel_buffer &buffer,
AutoPtr<dng_memory_block> &tempBuffer)
{
uint32 tempBufferSize = ComputeBufferSize(buffer.fPixelType,
buffer.fArea.Size(),
buffer.fPlanes, padNone);
if (!tempBuffer.Get () || tempBuffer->LogicalSize () < tempBufferSize)
{
tempBuffer.Reset (host.Allocate (tempBufferSize));
}
uint32 blockRows = ifd.fSubTileBlockRows;
uint32 blockCols = ifd.fSubTileBlockCols;
uint32 rowBlocks = buffer.fArea.H () / blockRows;
uint32 colBlocks = buffer.fArea.W () / blockCols;
int32 rowStep = buffer.fRowStep * buffer.fPixelSize;
int32 colStep = buffer.fColStep * buffer.fPixelSize;
int32 rowBlockStep = rowStep * blockRows;
int32 colBlockStep = colStep * blockCols;
uint32 blockColBytes = blockCols * buffer.fPlanes * buffer.fPixelSize;
const uint8 *s0 = (const uint8 *) buffer.fData;
uint8 *d0 = tempBuffer->Buffer_uint8 ();
for (uint32 rowBlock = 0; rowBlock < rowBlocks; rowBlock++)
{
uint8 *d1 = d0;
for (uint32 colBlock = 0; colBlock < colBlocks; colBlock++)
{
uint8 *d2 = d1;
for (uint32 blockRow = 0; blockRow < blockRows; blockRow++)
{
for (uint32 j = 0; j < blockColBytes; j++)
{
d2 [j] = s0 [j];
}
s0 += blockColBytes;
d2 += rowStep;
}
d1 += colBlockStep;
}
d0 += rowBlockStep;
}
// Copy back reordered pixels.
DoCopyBytes (tempBuffer->Buffer (),
buffer.fData,
tempBufferSize);
}
/*****************************************************************************/
class dng_image_spooler: public dng_spooler
{
private:
dng_host &fHost;
const dng_ifd &fIFD;
dng_image &fImage;
dng_rect fTileArea;
uint32 fPlane;
uint32 fPlanes;
dng_memory_block &fBlock;
AutoPtr<dng_memory_block> &fSubTileBuffer;
dng_rect fTileStrip;
uint8 *fBuffer;
uint32 fBufferCount;
uint32 fBufferSize;
public:
dng_image_spooler (dng_host &host,
const dng_ifd &ifd,
dng_image &image,
const dng_rect &tileArea,
uint32 plane,
uint32 planes,
dng_memory_block &block,
AutoPtr<dng_memory_block> &subTileBuffer);
virtual ~dng_image_spooler ();
virtual void Spool (const void *data,
uint32 count);
private:
// Hidden copy constructor and assignment operator.
dng_image_spooler (const dng_image_spooler &spooler);
dng_image_spooler & operator= (const dng_image_spooler &spooler);
};
/*****************************************************************************/
dng_image_spooler::dng_image_spooler (dng_host &host,
const dng_ifd &ifd,
dng_image &image,
const dng_rect &tileArea,
uint32 plane,
uint32 planes,
dng_memory_block &block,
AutoPtr<dng_memory_block> &subTileBuffer)
: fHost (host)
, fIFD (ifd)
, fImage (image)
, fTileArea (tileArea)
, fPlane (plane)
, fPlanes (planes)
, fBlock (block)
, fSubTileBuffer (subTileBuffer)
, fTileStrip ()
, fBuffer (NULL)
, fBufferCount (0)
, fBufferSize (0)
{
uint32 bytesPerRow = fTileArea.W () * fPlanes * (uint32) sizeof (uint16);
uint32 stripLength = Pin_uint32 (ifd.fSubTileBlockRows,
fBlock.LogicalSize () / bytesPerRow,
fTileArea.H ());
stripLength = stripLength / ifd.fSubTileBlockRows
* ifd.fSubTileBlockRows;
fTileStrip = fTileArea;
fTileStrip.b = fTileArea.t + stripLength;
fBuffer = (uint8 *) fBlock.Buffer ();
fBufferCount = 0;
fBufferSize = bytesPerRow * stripLength;
}
/*****************************************************************************/
dng_image_spooler::~dng_image_spooler ()
{
}
/*****************************************************************************/
void dng_image_spooler::Spool (const void *data,
uint32 count)
{
while (count)
{
uint32 block = Min_uint32 (count, fBufferSize - fBufferCount);
if (block == 0)
{
return;
}
DoCopyBytes (data,
fBuffer + fBufferCount,
block);
data = ((const uint8 *) data) + block;
count -= block;
fBufferCount += block;
if (fBufferCount == fBufferSize)
{
fHost.SniffForAbort ();
dng_pixel_buffer buffer (fTileStrip, fPlane, fPlanes, ttShort,
pcInterleaved, fBuffer);
if (fIFD.fSubTileBlockRows > 1)
{
ReorderSubTileBlocks (fHost,
fIFD,
buffer,
fSubTileBuffer);
}
fImage.Put (buffer);
uint32 stripLength = fTileStrip.H ();
fTileStrip.t = fTileStrip.b;
fTileStrip.b = Min_int32 (fTileStrip.t + stripLength,
fTileArea.b);
fBufferCount = 0;
fBufferSize = fTileStrip.W () *
fTileStrip.H () *
fPlanes * (uint32) sizeof (uint16);
}
}
}
/*****************************************************************************/
dng_read_image::dng_read_image ()
: fJPEGTables ()
{
}
/*****************************************************************************/
dng_read_image::~dng_read_image ()
{
}
/*****************************************************************************/
bool dng_read_image::ReadUncompressed (dng_host &host,
const dng_ifd &ifd,
dng_stream &stream,
dng_image &image,
const dng_rect &tileArea,
uint32 plane,
uint32 planes,
AutoPtr<dng_memory_block> &uncompressedBuffer,
AutoPtr<dng_memory_block> &subTileBlockBuffer)
{
uint32 rows = tileArea.H ();
uint32 samplesPerRow = tileArea.W ();
if (ifd.fPlanarConfiguration == pcRowInterleaved)
{
rows = SafeUint32Mult(rows, planes);
}
else
{
samplesPerRow = SafeUint32Mult(samplesPerRow, planes);
}
uint32 samplesPerTile = SafeUint32Mult(samplesPerRow, rows);
if (uncompressedBuffer.Get () == NULL)
{
#if qDNGValidate
ReportError ("Fuzz: Missing uncompressed buffer");
#endif
ThrowBadFormat ();
}
uint32 bitDepth = ifd.fBitsPerSample [plane];
uint32 pixelType = ttUndefined;
if (bitDepth == 8)
{
pixelType = ttByte;
stream.Get (uncompressedBuffer->Buffer (), samplesPerTile);
}
else if (bitDepth == 16 && ifd.fSampleFormat [0] == sfFloatingPoint)
{
pixelType = ttFloat;
uint32 *p_uint32 = (uint32 *) uncompressedBuffer->Buffer ();
for (uint32 j = 0; j < samplesPerTile; j++)
{
p_uint32 [j] = DNG_HalfToFloat (stream.Get_uint16 ());
}
}
else if (bitDepth == 24 && ifd.fSampleFormat [0] == sfFloatingPoint)
{
pixelType = ttFloat;
uint32 *p_uint32 = (uint32 *) uncompressedBuffer->Buffer ();
for (uint32 j = 0; j < samplesPerTile; j++)
{
uint8 input [3];
if (stream.LittleEndian ())
{
input [2] = stream.Get_uint8 ();
input [1] = stream.Get_uint8 ();
input [0] = stream.Get_uint8 ();
}
else
{
input [0] = stream.Get_uint8 ();
input [1] = stream.Get_uint8 ();
input [2] = stream.Get_uint8 ();
}
p_uint32 [j] = DNG_FP24ToFloat (input);
}
}
else if (bitDepth == 16)
{
pixelType = ttShort;
stream.Get (uncompressedBuffer->Buffer (), samplesPerTile * 2);
if (stream.SwapBytes ())
{
DoSwapBytes16 ((uint16 *) uncompressedBuffer->Buffer (),
samplesPerTile);
}
}
else if (bitDepth == 32)
{
pixelType = image.PixelType ();
stream.Get (uncompressedBuffer->Buffer (), samplesPerTile * 4);
if (stream.SwapBytes ())
{
DoSwapBytes32 ((uint32 *) uncompressedBuffer->Buffer (),
samplesPerTile);
}
}
else if (bitDepth == 12)
{
pixelType = ttShort;
uint16 *p = (uint16 *) uncompressedBuffer->Buffer ();
uint32 evenSamples = samplesPerRow >> 1;
for (uint32 row = 0; row < rows; row++)
{
for (uint32 j = 0; j < evenSamples; j++)
{
uint32 b0 = stream.Get_uint8 ();
uint32 b1 = stream.Get_uint8 ();
uint32 b2 = stream.Get_uint8 ();
p [0] = (uint16) ((b0 << 4) | (b1 >> 4));
p [1] = (uint16) (((b1 << 8) | b2) & 0x0FFF);
p += 2;
}
if (samplesPerRow & 1)
{
uint32 b0 = stream.Get_uint8 ();
uint32 b1 = stream.Get_uint8 ();
p [0] = (uint16) ((b0 << 4) | (b1 >> 4));
p += 1;
}
}
}
else if (bitDepth > 8 && bitDepth < 16)
{
pixelType = ttShort;
uint16 *p = (uint16 *) uncompressedBuffer->Buffer ();
uint32 bitMask = (1 << bitDepth) - 1;
for (uint32 row = 0; row < rows; row++)
{
uint32 bitBuffer = 0;
uint32 bufferBits = 0;
for (uint32 j = 0; j < samplesPerRow; j++)
{
while (bufferBits < bitDepth)
{
bitBuffer = (bitBuffer << 8) | stream.Get_uint8 ();
bufferBits += 8;
}
p [j] = (uint16) ((bitBuffer >> (bufferBits - bitDepth)) & bitMask);
bufferBits -= bitDepth;
}
p += samplesPerRow;
}
}
else if (bitDepth > 16 && bitDepth < 32)
{
pixelType = ttLong;
uint32 *p = (uint32 *) uncompressedBuffer->Buffer ();
uint32 bitMask = ((uint32) 1 << bitDepth) - 1;
for (uint32 row = 0; row < rows; row++)
{
uint64 bitBuffer = 0;
uint32 bufferBits = 0;
for (uint32 j = 0; j < samplesPerRow; j++)
{
while (bufferBits < bitDepth)
{
bitBuffer = (bitBuffer << 8) | stream.Get_uint8 ();
bufferBits += 8;
}
p [j] = ((uint32) (bitBuffer >> (bufferBits - bitDepth))) & bitMask;
bufferBits -= bitDepth;
}
p += samplesPerRow;
}
}
else
{
return false;
}
dng_pixel_buffer buffer (tileArea, plane, planes, pixelType,
ifd.fPlanarConfiguration, uncompressedBuffer->Buffer ());
if (ifd.fSampleBitShift)
{
buffer.ShiftRight (ifd.fSampleBitShift);
}
if (ifd.fSubTileBlockRows > 1)
{
ReorderSubTileBlocks (host,
ifd,
buffer,
subTileBlockBuffer);
}
image.Put (buffer);
return true;
}
/*****************************************************************************/
#if qDNGUseLibJPEG
/*****************************************************************************/
static void dng_error_exit (j_common_ptr cinfo)
{
// Output message.
(*cinfo->err->output_message) (cinfo);
// Convert to a dng_exception.
switch (cinfo->err->msg_code)
{
case JERR_OUT_OF_MEMORY:
{
ThrowMemoryFull ();
break;
}
default:
{
ThrowBadFormat ();
}
}
}
/*****************************************************************************/
static void dng_output_message (j_common_ptr cinfo)
{
// Format message to string.
char buffer [JMSG_LENGTH_MAX];
(*cinfo->err->format_message) (cinfo, buffer);
// Report the libjpeg message as a warning.
ReportWarning ("libjpeg", buffer);
}
/*****************************************************************************/
#endif
/*****************************************************************************/
void dng_read_image::DecodeLossyJPEG (dng_host &host,
dng_image &image,
const dng_rect &tileArea,
uint32 plane,
uint32 planes,
uint32 /* photometricInterpretation */,
uint32 jpegDataSize,
uint8 *jpegDataInMemory)
{
#if qDNGUseLibJPEG
struct jpeg_decompress_struct cinfo;
// Setup the error manager.
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error (&jerr);
jerr.error_exit = dng_error_exit;
jerr.output_message = dng_output_message;
try
{
// Create the decompression context.
jpeg_create_decompress (&cinfo);
// Set up the memory data source manager.
size_t jpegDataSizeAsSizet = 0;
ConvertUnsigned(jpegDataSize, &jpegDataSizeAsSizet);
jpeg_source_mgr memorySource =
CreateJpegMemorySource(jpegDataInMemory,
jpegDataSizeAsSizet);
cinfo.src = &memorySource;
// Read the JPEG header.
jpeg_read_header (&cinfo, TRUE);
// Check header.
{
// Number of components may not be negative.
if (cinfo.num_components < 0)
{
ThrowBadFormat();
}
// Convert relevant values from header to uint32.
uint32 imageWidthAsUint32 = 0;
uint32 imageHeightAsUint32 = 0;
uint32 numComponentsAsUint32 = 0;
ConvertUnsigned(cinfo.image_width, &imageWidthAsUint32);
ConvertUnsigned(cinfo.image_height, &imageHeightAsUint32);
// num_components is an int. Casting to unsigned is safe because the
// test above guarantees num_components is not negative.
ConvertUnsigned(static_cast<unsigned>(cinfo.num_components),
&numComponentsAsUint32);
// Check that dimensions of JPEG correspond to dimensions of tile.
if (imageWidthAsUint32 != tileArea.W () ||
imageHeightAsUint32 != tileArea.H () ||
numComponentsAsUint32 != planes )
{
ThrowBadFormat ();
}
}
// Start the compression.
jpeg_start_decompress (&cinfo);
// Setup a one-scanline size buffer.
dng_pixel_buffer buffer(tileArea, plane, planes, ttByte, pcInterleaved,
NULL);
buffer.fArea.b = tileArea.t + 1;
buffer.fDirty = true;
AutoPtr<dng_memory_block> bufferData (host.Allocate (buffer.fRowStep));
buffer.fData = bufferData->Buffer ();
uint8 *sampArray [1];
sampArray [0] = bufferData->Buffer_uint8 ();
// Read each scanline and save to image.
while (buffer.fArea.t < tileArea.b)
{
jpeg_read_scanlines (&cinfo, sampArray, 1);
image.Put (buffer);
buffer.fArea.t = buffer.fArea.b;
buffer.fArea.b = buffer.fArea.t + 1;
}
// Cleanup.
jpeg_finish_decompress (&cinfo);
jpeg_destroy_decompress (&cinfo);
}
catch (...)
{
jpeg_destroy_decompress (&cinfo);
throw;
}
#else
// The dng_sdk does not include a lossy JPEG decoder. Override this
// this method to add lossy JPEG support.
(void) host;
(void) image;
(void) tileArea;
(void) plane;
(void) planes;
(void) jpegDataSize;
(void) jpegDataInMemory;
ThrowProgramError ("Missing lossy JPEG decoder");
#endif
}
/*****************************************************************************/
static dng_memory_block * ReadJPEGDataToBlock (dng_host &host,
dng_stream &stream,
dng_memory_block *tablesBlock,
uint64 tileOffset,
uint32 tileByteCount,
bool patchFirstByte)
{
// This ensures that the "tileByteCount -= 2" operation below will not wrap
// around.
if (tileByteCount <= 2)
{
ThrowEndOfFile ();
}
uint32 tablesByteCount = tablesBlock ? tablesBlock->LogicalSize () : 0;
// This ensures that the "tablesByteCount -= 2" operation below will not
// wrap around.
if (tablesByteCount && tablesByteCount < 4)
{
ThrowEndOfFile ();
}
// The JPEG tables start with a two byte SOI marker, and
// and end with a two byte EOI marker. The JPEG tile
// data also starts with a two byte SOI marker. We can
// convert this combination a normal JPEG stream removing
// the last two bytes of the JPEG tables and the first two
// bytes of the tile data, and then concatenating them.
if (tablesByteCount)
{
// Ensure the "tileOffset += 2" operation below will not wrap around.
if (tileOffset > std::numeric_limits<uint64>::max () - 2)
{
ThrowEndOfFile();
}
tablesByteCount -= 2;
tileOffset += 2;
tileByteCount -= 2;
}
// Allocate buffer.
AutoPtr<dng_memory_block> buffer (host.Allocate (
SafeUint32Add(tablesByteCount, tileByteCount)));
// Read in table.
if (tablesByteCount)
{
DoCopyBytes (tablesBlock->Buffer (),
buffer->Buffer (),
tablesByteCount);
}
// Read in tile data.
stream.SetReadPosition (tileOffset);
stream.Get (buffer->Buffer_uint8 () + tablesByteCount, tileByteCount);
// Patch first byte, if required.
if (patchFirstByte)
{
buffer->Buffer_uint8 () [0] = 0xFF;
}
// Return buffer.
return buffer.Release ();
}
/*****************************************************************************/
bool dng_read_image::ReadBaselineJPEG (dng_host &host,
const dng_ifd &ifd,
dng_stream &stream,
dng_image &image,
const dng_rect &tileArea,
uint32 plane,
uint32 planes,
uint32 tileByteCount,
uint8 *jpegDataInMemory)
{
// Setup the data source.
if (fJPEGTables.Get () || !jpegDataInMemory)
{
AutoPtr<dng_memory_block> jpegDataBlock;
jpegDataBlock.Reset (ReadJPEGDataToBlock (host,
stream,
fJPEGTables.Get (),
stream.Position (),
tileByteCount,
ifd.fPatchFirstJPEGByte));
DecodeLossyJPEG (host,
image,
tileArea,
plane,
planes,
ifd.fPhotometricInterpretation,
jpegDataBlock->LogicalSize (),
jpegDataBlock->Buffer_uint8 ());
}
else
{
if (ifd.fPatchFirstJPEGByte && tileByteCount)
{
jpegDataInMemory [0] = 0xFF;
}
DecodeLossyJPEG (host,
image,
tileArea,
plane,
planes,
ifd.fPhotometricInterpretation,
tileByteCount,
jpegDataInMemory);
}
return true;
}
/*****************************************************************************/
bool dng_read_image::ReadLosslessJPEG (dng_host &host,
const dng_ifd &ifd,
dng_stream &stream,
dng_image &image,
const dng_rect &tileArea,
uint32 plane,
uint32 planes,
uint32 tileByteCount,
AutoPtr<dng_memory_block> &uncompressedBuffer,
AutoPtr<dng_memory_block> &subTileBlockBuffer)
{
// If the tile area is empty, there's nothing to read.
if (tileArea.IsEmpty ())
{
return true;
}
uint32 bytesPerRow = SafeUint32Mult (tileArea.W(), planes,
static_cast<uint32> (sizeof (uint16)));
uint32 rowsPerStrip = Pin_uint32 (ifd.fSubTileBlockRows,
kImageBufferSize / bytesPerRow,
tileArea.H ());
rowsPerStrip = rowsPerStrip / ifd.fSubTileBlockRows
* ifd.fSubTileBlockRows;
uint32 bufferSize = SafeUint32Mult (bytesPerRow, rowsPerStrip);
if (uncompressedBuffer.Get () &&
uncompressedBuffer->LogicalSize () < bufferSize)
{
uncompressedBuffer.Reset ();
}
if (uncompressedBuffer.Get () == NULL)
{
uncompressedBuffer.Reset (host.Allocate (bufferSize));
}
dng_image_spooler spooler (host,
ifd,
image,
tileArea,
plane,
planes,
*uncompressedBuffer.Get (),
subTileBlockBuffer);
uint32 decodedSize = SafeUint32Mult(tileArea.W (),
tileArea.H (),
planes, (uint32) sizeof (uint16));
bool bug16 = ifd.fLosslessJPEGBug16;
uint64 tileOffset = stream.Position ();
DecodeLosslessJPEG (stream,
spooler,
decodedSize,
decodedSize,
bug16);
if (stream.Position () > tileOffset + tileByteCount)
{
ThrowBadFormat ();
}
return true;
}
/*****************************************************************************/
bool dng_read_image::CanReadTile (const dng_ifd &ifd)
{
if (ifd.fSampleFormat [0] != sfUnsignedInteger &&
ifd.fSampleFormat [0] != sfFloatingPoint)
{
return false;
}
switch (ifd.fCompression)
{
case ccUncompressed:
{
if (ifd.fSampleFormat [0] == sfFloatingPoint)
{
return (ifd.fBitsPerSample [0] == 16 ||
ifd.fBitsPerSample [0] == 24 ||
ifd.fBitsPerSample [0] == 32);
}
return ifd.fBitsPerSample [0] >= 8 &&
ifd.fBitsPerSample [0] <= 32;
}
case ccJPEG:
{
if (ifd.fSampleFormat [0] != sfUnsignedInteger)
{
return false;
}
if (ifd.IsBaselineJPEG ())
{
// Baseline JPEG.
return true;
}
else
{
// Lossless JPEG.
return ifd.fBitsPerSample [0] >= 8 &&
ifd.fBitsPerSample [0] <= 16;
}
break;
}
case ccLZW:
case ccDeflate:
case ccOldDeflate:
case ccPackBits:
{
if (ifd.fSampleFormat [0] == sfFloatingPoint)
{
if (ifd.fCompression == ccPackBits)
{
return false;
}
if (ifd.fPredictor != cpNullPredictor &&
ifd.fPredictor != cpFloatingPoint &&
ifd.fPredictor != cpFloatingPointX2 &&
ifd.fPredictor != cpFloatingPointX4)
{
return false;
}
if (ifd.fBitsPerSample [0] != 16 &&
ifd.fBitsPerSample [0] != 24 &&
ifd.fBitsPerSample [0] != 32)
{
return false;
}
}
else
{
if (ifd.fPredictor != cpNullPredictor &&
ifd.fPredictor != cpHorizontalDifference &&
ifd.fPredictor != cpHorizontalDifferenceX2 &&
ifd.fPredictor != cpHorizontalDifferenceX4)
{
return false;
}
if (ifd.fBitsPerSample [0] != 8 &&
ifd.fBitsPerSample [0] != 16 &&
ifd.fBitsPerSample [0] != 32)
{
return false;
}
}
return true;
}
default:
{
break;
}
}
return false;
}
/*****************************************************************************/
bool dng_read_image::NeedsCompressedBuffer (const dng_ifd &ifd)
{
if (ifd.fCompression == ccLZW ||
ifd.fCompression == ccDeflate ||
ifd.fCompression == ccOldDeflate ||
ifd.fCompression == ccPackBits)
{
return true;
}
return false;
}
/*****************************************************************************/
void dng_read_image::ByteSwapBuffer (dng_host & /* host */,
dng_pixel_buffer &buffer)
{
uint32 pixels = buffer.fRowStep * buffer.fArea.H ();
switch (buffer.fPixelSize)
{
case 2:
{
DoSwapBytes16 ((uint16 *) buffer.fData,
pixels);
break;
}
case 4:
{
DoSwapBytes32 ((uint32 *) buffer.fData,
pixels);
break;
}
default:
break;
}
}
/*****************************************************************************/
void dng_read_image::DecodePredictor (dng_host & /* host */,
const dng_ifd &ifd,
dng_pixel_buffer &buffer)
{
switch (ifd.fPredictor)
{
case cpNullPredictor:
{
return;
}
case cpHorizontalDifference:
case cpHorizontalDifferenceX2:
case cpHorizontalDifferenceX4:
{
int32 xFactor = 1;
if (ifd.fPredictor == cpHorizontalDifferenceX2)
{
xFactor = 2;
}
else if (ifd.fPredictor == cpHorizontalDifferenceX4)
{
xFactor = 4;
}
switch (buffer.fPixelType)
{
case ttByte:
{
DecodeDelta8 ((uint8 *) buffer.fData,
buffer.fArea.H (),
buffer.fArea.W () / xFactor,
buffer.fPlanes * xFactor);
return;
}
case ttShort:
{
DecodeDelta16 ((uint16 *) buffer.fData,
buffer.fArea.H (),
buffer.fArea.W () / xFactor,
buffer.fPlanes * xFactor);
return;
}
case ttLong:
{
DecodeDelta32 ((uint32 *) buffer.fData,
buffer.fArea.H (),
buffer.fArea.W () / xFactor,
buffer.fPlanes * xFactor);
return;
}
default:
break;
}
break;
}
default:
break;
}
ThrowBadFormat ();
}
/*****************************************************************************/
void dng_read_image::ReadTile (dng_host &host,
const dng_ifd &ifd,
dng_stream &stream,
dng_image &image,
const dng_rect &tileArea,
uint32 plane,
uint32 planes,
uint32 tileByteCount,
AutoPtr<dng_memory_block> &compressedBuffer,
AutoPtr<dng_memory_block> &uncompressedBuffer,
AutoPtr<dng_memory_block> &subTileBlockBuffer)
{
switch (ifd.fCompression)
{
case ccLZW:
case ccDeflate:
case ccOldDeflate:
case ccPackBits:
{
// Figure out uncompressed size.
uint32 bytesPerSample = (ifd.fBitsPerSample [0] >> 3);
uint32 rowStep = 0;
uint32 sampleCount = 0;
if (!SafeUint32Mult (planes, tileArea.W (), &rowStep) ||
!SafeUint32Mult (rowStep, tileArea.H (), &sampleCount))
{
ThrowMemoryFull ("Arithmetic overflow computing sample count.");
}
// Setup pixel buffer to hold uncompressed data.
uint32 pixelType = ttUndefined;
if (ifd.fSampleFormat [0] == sfFloatingPoint)
{
pixelType = ttFloat;
}
else if (ifd.fBitsPerSample [0] == 8)
{
pixelType = ttByte;
}
else if (ifd.fBitsPerSample [0] == 16)
{
pixelType = ttShort;
}
else if (ifd.fBitsPerSample [0] == 32)
{
pixelType = ttLong;
}
else
{
ThrowBadFormat ();
}
uint32 uncompressedSize = ComputeBufferSize (pixelType, tileArea.Size(),
planes, padNone);
dng_pixel_buffer buffer (tileArea, plane, planes, pixelType, pcInterleaved,
NULL);
uint32 bufferSize = uncompressedSize;
// If we are using the floating point predictor, we need an extra
// buffer row.
if (ifd.fPredictor == cpFloatingPoint ||
ifd.fPredictor == cpFloatingPointX2 ||
ifd.fPredictor == cpFloatingPointX4)
{
uint32 rowSize = 0;
if (!SafeUint32Mult (rowStep, buffer.fPixelSize, &rowSize) ||
!SafeUint32Add (bufferSize, rowSize, &bufferSize))
{
ThrowMemoryFull ("Arithmetic overflow computing buffer size.");
}
}
// If are processing less than full size floating point data,
// we need space to expand the data to full floating point size.
if (buffer.fPixelType == ttFloat)
{
bufferSize = Max_uint32 (bufferSize,
SafeUint32Mult(sampleCount, 4));
}
// Sometimes with multi-threading and planar image using strips,
// we can process a small tile before a large tile on a thread.
// Simple fix is to just reallocate the buffer if it is too small.
if (uncompressedBuffer.Get () &&
uncompressedBuffer->LogicalSize () < bufferSize)
{
uncompressedBuffer.Reset ();
}
if (uncompressedBuffer.Get () == NULL)
{
uncompressedBuffer.Reset (host.Allocate (bufferSize));
}
buffer.fData = uncompressedBuffer->Buffer ();
// If using floating point predictor, move buffer pointer to second row.
if (ifd.fPredictor == cpFloatingPoint ||
ifd.fPredictor == cpFloatingPointX2 ||
ifd.fPredictor == cpFloatingPointX4)
{
buffer.fData = (uint8 *) buffer.fData +
buffer.fRowStep * buffer.fPixelSize;
}
// Decompress the data.
if (ifd.fCompression == ccLZW)
{
dng_lzw_expander expander;
if (!expander.Expand (compressedBuffer->Buffer_uint8 (),
(uint8 *) buffer.fData,
tileByteCount,
uncompressedSize))
{
ThrowBadFormat ();
}
}
else if (ifd.fCompression == ccPackBits)
{
dng_stream subStream (compressedBuffer->Buffer_uint8 (),
tileByteCount);
if (!DecodePackBits (subStream,
(uint8 *) buffer.fData,
uncompressedSize))
{
ThrowBadFormat ();
}
}
else
{
uLongf dstLen = uncompressedSize;
int err = uncompress ((Bytef *) buffer.fData,
&dstLen,
(const Bytef *) compressedBuffer->Buffer (),
tileByteCount);
if (err != Z_OK)
{
if (err == Z_MEM_ERROR)
{
ThrowMemoryFull ();
}
else if (err == Z_DATA_ERROR)
{
// Most other TIFF readers do not fail for this error
// so we should not either, even if it means showing
// a corrupted image to the user. Watson #2530216
// - tknoll 12/20/11
}
else
{
ThrowBadFormat ();
}
}
if (dstLen != uncompressedSize)
{
ThrowBadFormat ();
}
}
// The floating point predictor is byte order independent.
if (ifd.fPredictor == cpFloatingPoint ||
ifd.fPredictor == cpFloatingPointX2 ||
ifd.fPredictor == cpFloatingPointX4)
{
int32 xFactor = 1;
if (ifd.fPredictor == cpFloatingPointX2)
{
xFactor = 2;
}
else if (ifd.fPredictor == cpFloatingPointX4)
{
xFactor = 4;
}
for (int32 row = tileArea.t; row < tileArea.b; row++)
{
uint8 *srcPtr = (uint8 *) buffer.DirtyPixel (row , tileArea.l, plane);
// Destination is previous row.
// Subtracting buffer.fRowStep * buffer.fPixelSize will
// always result in a pointer that lies inside the buffer
// because above, we added exactly the same offset to
// buffer.fData (see the piece of code commented "move
// buffer pointer to second row").
uint8 *dstPtr = srcPtr -
buffer.fRowStep * buffer.fPixelSize;
DecodeFPDelta (srcPtr,
dstPtr,
tileArea.W () / xFactor,
planes * xFactor,
bytesPerSample);
}
buffer.fData = (uint8 *) buffer.fData -
buffer.fRowStep * buffer.fPixelSize;
}
else
{
// Both these compression algorithms are byte based.
if (stream.SwapBytes ())
{
ByteSwapBuffer (host,
buffer);
}
// Undo the predictor.
DecodePredictor (host,
ifd,
buffer);
}
// Expand floating point data, if needed.
if (buffer.fPixelType == ttFloat && buffer.fPixelSize == 2)
{
uint16 *srcPtr = (uint16 *) buffer.fData;
uint32 *dstPtr = (uint32 *) buffer.fData;
for (int32 index = sampleCount - 1; index >= 0; index--)
{
dstPtr [index] = DNG_HalfToFloat (srcPtr [index]);
}
buffer.fPixelSize = 4;
}
else if (buffer.fPixelType == ttFloat && buffer.fPixelSize == 3)
{
uint8 *srcPtr = ((uint8 *) buffer.fData) + (sampleCount - 1) * 3;
uint32 *dstPtr = ((uint32 *) buffer.fData) + (sampleCount - 1);
if (stream.BigEndian () || ifd.fPredictor == cpFloatingPoint ||
ifd.fPredictor == cpFloatingPointX2 ||
ifd.fPredictor == cpFloatingPointX4)
{
for (uint32 index = 0; index < sampleCount; index++)
{
*(dstPtr--) = DNG_FP24ToFloat (srcPtr);
srcPtr -= 3;
}
}
else
{
for (uint32 index = 0; index < sampleCount; index++)
{
uint8 input [3];
input [2] = srcPtr [0];
input [1] = srcPtr [1];
input [0] = srcPtr [2];
*(dstPtr--) = DNG_FP24ToFloat (input);
srcPtr -= 3;
}
}
buffer.fPixelSize = 4;
}
// Save the data.
image.Put (buffer);
return;
}
case ccUncompressed:
{
if (ReadUncompressed (host,
ifd,
stream,
image,
tileArea,
plane,
planes,
uncompressedBuffer,
subTileBlockBuffer))
{
return;
}
break;
}
case ccJPEG:
{
if (ifd.IsBaselineJPEG ())
{
// Baseline JPEG.
if (ReadBaselineJPEG (host,
ifd,
stream,
image,
tileArea,
plane,
planes,
tileByteCount,
compressedBuffer.Get () ? compressedBuffer->Buffer_uint8 () : NULL))
{
return;
}
}
else
{
// Otherwise is should be lossless JPEG.
if (ReadLosslessJPEG (host,
ifd,
stream,
image,
tileArea,
plane,
planes,
tileByteCount,
uncompressedBuffer,
subTileBlockBuffer))
{
return;
}
}
break;
}
case ccLossyJPEG:
{
if (ReadBaselineJPEG (host,
ifd,
stream,
image,
tileArea,
plane,
planes,
tileByteCount,
compressedBuffer.Get () ? compressedBuffer->Buffer_uint8 () : NULL))
{
return;
}
break;
}
default:
break;
}
ThrowBadFormat ();
}
/*****************************************************************************/
bool dng_read_image::CanRead (const dng_ifd &ifd)
{
if (ifd.fImageWidth < 1 ||
ifd.fImageLength < 1)
{
return false;
}
if (ifd.fSamplesPerPixel < 1)
{
return false;
}
if (ifd.fBitsPerSample [0] < 1)
{
return false;
}
for (uint32 j = 1; j < Min_uint32 (ifd.fSamplesPerPixel,
kMaxSamplesPerPixel); j++)
{
if (ifd.fBitsPerSample [j] !=
ifd.fBitsPerSample [0])
{
return false;
}
if (ifd.fSampleFormat [j] !=
ifd.fSampleFormat [0])
{
return false;
}
}
if ((ifd.fPlanarConfiguration != pcInterleaved ) &&
(ifd.fPlanarConfiguration != pcPlanar ) &&
(ifd.fPlanarConfiguration != pcRowInterleaved))
{
return false;
}
if (ifd.fUsesStrips == ifd.fUsesTiles)
{
return false;
}
uint32 tileCount = ifd.TilesPerImage ();
if (tileCount < 1)
{
return false;
}
bool needTileByteCounts = (ifd.TileByteCount (ifd.TileArea (0, 0)) == 0);
if (tileCount == 1)
{
if (needTileByteCounts)
{
if (ifd.fTileByteCount [0] < 1)
{
return false;
}
}
}
else
{
if (ifd.fTileOffsetsCount != tileCount)
{
return false;
}
if (needTileByteCounts)
{
if (ifd.fTileByteCountsCount != tileCount)
{
return false;
}
}
}
if (!CanReadTile (ifd))
{
return false;
}
return true;
}
/*****************************************************************************/
class dng_read_tiles_task : public dng_area_task
{
private:
dng_read_image &fReadImage;
dng_host &fHost;
const dng_ifd &fIFD;
dng_stream &fStream;
dng_image &fImage;
dng_jpeg_image *fJPEGImage;
dng_fingerprint *fJPEGTileDigest;
uint32 fOuterSamples;
uint32 fInnerSamples;
uint32 fTilesDown;
uint32 fTilesAcross;
uint64 *fTileOffset;
uint32 *fTileByteCount;
uint32 fCompressedSize;
uint32 fUncompressedSize;
dng_mutex fMutex;
uint32 fNextTileIndex;
public:
dng_read_tiles_task (dng_read_image &readImage,
dng_host &host,
const dng_ifd &ifd,
dng_stream &stream,
dng_image &image,
dng_jpeg_image *jpegImage,
dng_fingerprint *jpegTileDigest,
uint32 outerSamples,
uint32 innerSamples,
uint32 tilesDown,
uint32 tilesAcross,
uint64 *tileOffset,
uint32 *tileByteCount,
uint32 compressedSize,
uint32 uncompressedSize)
: fReadImage (readImage)
, fHost (host)
, fIFD (ifd)
, fStream (stream)
, fImage (image)
, fJPEGImage (jpegImage)
, fJPEGTileDigest (jpegTileDigest)
, fOuterSamples (outerSamples)
, fInnerSamples (innerSamples)
, fTilesDown (tilesDown)
, fTilesAcross (tilesAcross)
, fTileOffset (tileOffset)
, fTileByteCount (tileByteCount)
, fCompressedSize (compressedSize)
, fUncompressedSize (uncompressedSize)
, fMutex ("dng_read_tiles_task")
, fNextTileIndex (0)
{
fMinTaskArea = 16 * 16;
fUnitCell = dng_point (16, 16);
fMaxTileSize = dng_point (16, 16);
}
void Process (uint32 /* threadIndex */,
const dng_rect & /* tile */,
dng_abort_sniffer *sniffer)
{
AutoPtr<dng_memory_block> compressedBuffer;
AutoPtr<dng_memory_block> uncompressedBuffer;
AutoPtr<dng_memory_block> subTileBlockBuffer;
if (!fJPEGImage)
{
compressedBuffer.Reset (fHost.Allocate (fCompressedSize));
}
if (fUncompressedSize)
{
uncompressedBuffer.Reset (fHost.Allocate (fUncompressedSize));
}
while (true)
{
uint32 tileIndex;
uint32 byteCount;
{
dng_lock_mutex lock (&fMutex);
if (fNextTileIndex == fOuterSamples * fTilesDown * fTilesAcross)
{
return;
}
tileIndex = fNextTileIndex++;
TempStreamSniffer noSniffer (fStream, NULL);
fStream.SetReadPosition (fTileOffset [tileIndex]);
byteCount = fTileByteCount [tileIndex];
if (fJPEGImage)
{
fJPEGImage->fJPEGData [tileIndex] . Reset (fHost.Allocate (byteCount));
}
fStream.Get (fJPEGImage ? fJPEGImage->fJPEGData [tileIndex]->Buffer ()
: compressedBuffer->Buffer (),
byteCount);
}
dng_abort_sniffer::SniffForAbort (sniffer);
if (fJPEGTileDigest)
{
dng_md5_printer printer;
printer.Process (compressedBuffer->Buffer (),
byteCount);
fJPEGTileDigest [tileIndex] = printer.Result ();
}
dng_stream tileStream (fJPEGImage ? fJPEGImage->fJPEGData [tileIndex]->Buffer ()
: compressedBuffer->Buffer (),
byteCount);
tileStream.SetLittleEndian (fStream.LittleEndian ());
uint32 plane = tileIndex / (fTilesDown * fTilesAcross);
uint32 rowIndex = (tileIndex - plane * fTilesDown * fTilesAcross) / fTilesAcross;
uint32 colIndex = tileIndex - (plane * fTilesDown + rowIndex) * fTilesAcross;
dng_rect tileArea = fIFD.TileArea (rowIndex, colIndex);
dng_host host (&fHost.Allocator (),
sniffer); // Cannot use sniffer attached to main host
fReadImage.ReadTile (host,
fIFD,
tileStream,
fImage,
tileArea,
plane,
fInnerSamples,
byteCount,
fJPEGImage ? fJPEGImage->fJPEGData [tileIndex]
: compressedBuffer,
uncompressedBuffer,
subTileBlockBuffer);
}
}
private:
// Hidden copy constructor and assignment operator.
dng_read_tiles_task (const dng_read_tiles_task &);
dng_read_tiles_task & operator= (const dng_read_tiles_task &);
};
/*****************************************************************************/
void dng_read_image::Read (dng_host &host,
const dng_ifd &ifd,
dng_stream &stream,
dng_image &image,
dng_jpeg_image *jpegImage,
dng_fingerprint *jpegDigest)
{
uint32 tileIndex;
// Deal with row interleaved images.
if (ifd.fRowInterleaveFactor > 1 &&
ifd.fRowInterleaveFactor < ifd.fImageLength)
{
dng_ifd tempIFD (ifd);
tempIFD.fRowInterleaveFactor = 1;
dng_row_interleaved_image tempImage (image,
ifd.fRowInterleaveFactor);
Read (host,
tempIFD,
stream,
tempImage,
jpegImage,
jpegDigest);
return;
}
// Figure out inner and outer samples.
uint32 innerSamples = 1;
uint32 outerSamples = 1;
if (ifd.fPlanarConfiguration == pcPlanar)
{
outerSamples = ifd.fSamplesPerPixel;
}
else
{
innerSamples = ifd.fSamplesPerPixel;
}
// Calculate number of tiles to read.
uint32 tilesAcross = ifd.TilesAcross ();
uint32 tilesDown = ifd.TilesDown ();
uint32 tileCount = SafeUint32Mult (tilesAcross, tilesDown, outerSamples);
// Find the tile offsets.
dng_memory_data tileOffsetData (tileCount, sizeof (uint64));
uint64 *tileOffset = tileOffsetData.Buffer_uint64 ();
if (tileCount <= dng_ifd::kMaxTileInfo)
{
for (tileIndex = 0; tileIndex < tileCount; tileIndex++)
{
tileOffset [tileIndex] = ifd.fTileOffset [tileIndex];
}
}
else
{
stream.SetReadPosition (ifd.fTileOffsetsOffset);
for (tileIndex = 0; tileIndex < tileCount; tileIndex++)
{
tileOffset [tileIndex] = stream.TagValue_uint32 (ifd.fTileOffsetsType);
}
}
// Quick validity check on tile offsets.
for (tileIndex = 0; tileIndex < tileCount; tileIndex++)
{
#if qDNGValidate
if (tileOffset [tileIndex] < 8)
{
ReportWarning ("Tile/Strip offset less than 8");
}
#endif
if (tileOffset [tileIndex] >= stream.Length ())
{
ThrowBadFormat ();
}
}
// Buffer to hold the tile byte counts, if needed.
dng_memory_data tileByteCountData;
uint32 *tileByteCount = NULL;
// If we can compute the number of bytes needed to store the
// data, we can split the read for each tile into sub-tiles.
uint32 uncompressedSize = 0;
uint32 subTileLength = ifd.fTileLength;
if (ifd.TileByteCount (ifd.TileArea (0, 0)) != 0)
{
uint32 bytesPerPixel = TagTypeSize (ifd.PixelType ());
uint32 bytesPerRow = SafeUint32Mult (ifd.fTileWidth, innerSamples,
bytesPerPixel);
subTileLength = Pin_uint32 (ifd.fSubTileBlockRows,
kImageBufferSize / bytesPerRow,
ifd.fTileLength);
subTileLength = subTileLength / ifd.fSubTileBlockRows
* ifd.fSubTileBlockRows;
uncompressedSize = SafeUint32Mult (subTileLength, bytesPerRow);
}
// Else we need to know the byte counts.
else
{
tileByteCountData.Allocate (tileCount, sizeof (uint32));
tileByteCount = tileByteCountData.Buffer_uint32 ();
if (tileCount <= dng_ifd::kMaxTileInfo)
{
for (tileIndex = 0; tileIndex < tileCount; tileIndex++)
{
tileByteCount [tileIndex] = ifd.fTileByteCount [tileIndex];
}
}
else
{
stream.SetReadPosition (ifd.fTileByteCountsOffset);
for (tileIndex = 0; tileIndex < tileCount; tileIndex++)
{
tileByteCount [tileIndex] = stream.TagValue_uint32 (ifd.fTileByteCountsType);
}
}
// Quick validity check on tile byte counts.
for (tileIndex = 0; tileIndex < tileCount; tileIndex++)
{
if (tileByteCount [tileIndex] < 1 ||
tileByteCount [tileIndex] > stream.Length ())
{
ThrowBadFormat ();
}
}
}
// Find maximum tile size, if possible.
uint32 maxTileByteCount = 0;
if (tileByteCount)
{
for (tileIndex = 0; tileIndex < tileCount; tileIndex++)
{
maxTileByteCount = Max_uint32 (maxTileByteCount,
tileByteCount [tileIndex]);
}
}
// Do we need a compressed data buffer?
uint32 compressedSize = 0;
bool needsCompressedBuffer = NeedsCompressedBuffer (ifd);
if (needsCompressedBuffer)
{
if (!tileByteCount)
{
ThrowBadFormat ();
}
compressedSize = maxTileByteCount;
}
// Are we keeping the compressed JPEG image data?
if (jpegImage)
{
if (ifd.IsBaselineJPEG ())
{
jpegImage->fImageSize.h = ifd.fImageWidth;
jpegImage->fImageSize.v = ifd.fImageLength;
jpegImage->fTileSize.h = ifd.fTileWidth;
jpegImage->fTileSize.v = ifd.fTileLength;
jpegImage->fUsesStrips = ifd.fUsesStrips;
jpegImage->fJPEGData.Reset (tileCount);
}
else
{
jpegImage = NULL;
}
}
// Do we need to read the JPEG tables?
if (ifd.fJPEGTablesOffset && ifd.fJPEGTablesCount)
{
if (ifd.IsBaselineJPEG ())
{
fJPEGTables.Reset (host.Allocate (ifd.fJPEGTablesCount));
stream.SetReadPosition (ifd.fJPEGTablesOffset);
stream.Get (fJPEGTables->Buffer (),
fJPEGTables->LogicalSize ());
}
}
AutoArray<dng_fingerprint> jpegTileDigest;
if (jpegDigest)
{
jpegTileDigest.Reset (
SafeUint32Add(tileCount, (fJPEGTables.Get () ? 1 : 0)));
}
// Don't read planes we are not actually saving.
outerSamples = Min_uint32 (image.Planes (), outerSamples);
// See if we can do this read using multiple threads.
bool useMultipleThreads = (outerSamples * tilesDown * tilesAcross >= 2) &&
(host.PerformAreaTaskThreads () > 1) &&
(maxTileByteCount > 0 && maxTileByteCount <= 1024 * 1024) &&
(subTileLength == ifd.fTileLength) &&
(ifd.fCompression != ccUncompressed);
#if qImagecore
useMultipleThreads = false;
#endif
if (useMultipleThreads)
{
uint32 threadCount = Min_uint32 (outerSamples * tilesDown * tilesAcross,
host.PerformAreaTaskThreads ());
dng_read_tiles_task task (*this,
host,
ifd,
stream,
image,
jpegImage,
jpegTileDigest.Get (),
outerSamples,
innerSamples,
tilesDown,
tilesAcross,
tileOffset,
tileByteCount,
maxTileByteCount,
uncompressedSize);
host.PerformAreaTask (task,
dng_rect (0, 0, 16, 16 * threadCount));
}
// Else use a single thread to read all the tiles.
else
{
AutoPtr<dng_memory_block> compressedBuffer;
AutoPtr<dng_memory_block> uncompressedBuffer;
AutoPtr<dng_memory_block> subTileBlockBuffer;
if (uncompressedSize)
{
uncompressedBuffer.Reset (host.Allocate (uncompressedSize));
}
if (compressedSize && !jpegImage)
{
compressedBuffer.Reset (host.Allocate (compressedSize));
}
else if (jpegDigest)
{
compressedBuffer.Reset (host.Allocate (maxTileByteCount));
}
tileIndex = 0;
for (uint32 plane = 0; plane < outerSamples; plane++)
{
for (uint32 rowIndex = 0; rowIndex < tilesDown; rowIndex++)
{
for (uint32 colIndex = 0; colIndex < tilesAcross; colIndex++)
{
stream.SetReadPosition (tileOffset [tileIndex]);
dng_rect tileArea = ifd.TileArea (rowIndex, colIndex);
uint32 subTileCount = (tileArea.H () + subTileLength - 1) /
subTileLength;
for (uint32 subIndex = 0; subIndex < subTileCount; subIndex++)
{
host.SniffForAbort ();
dng_rect subArea (tileArea);
subArea.t = tileArea.t + subIndex * subTileLength;
subArea.b = Min_int32 (subArea.t + subTileLength,
tileArea.b);
uint32 subByteCount;
if (tileByteCount)
{
subByteCount = tileByteCount [tileIndex];
}
else
{
subByteCount = ifd.TileByteCount (subArea);
}
if (jpegImage)
{
jpegImage->fJPEGData [tileIndex].Reset (host.Allocate (subByteCount));
stream.Get (jpegImage->fJPEGData [tileIndex]->Buffer (), subByteCount);
stream.SetReadPosition (tileOffset [tileIndex]);
}
else if ((needsCompressedBuffer || jpegDigest) && subByteCount)
{
stream.Get (compressedBuffer->Buffer (), subByteCount);
if (jpegDigest)
{
dng_md5_printer printer;
printer.Process (compressedBuffer->Buffer (),
subByteCount);
jpegTileDigest [tileIndex] = printer.Result ();
}
}
ReadTile (host,
ifd,
stream,
image,
subArea,
plane,
innerSamples,
subByteCount,
jpegImage ? jpegImage->fJPEGData [tileIndex] : compressedBuffer,
uncompressedBuffer,
subTileBlockBuffer);
}
tileIndex++;
}
}
}
}
// Finish up JPEG digest computation, if needed.
if (jpegDigest)
{
if (fJPEGTables.Get ())
{
dng_md5_printer printer;
printer.Process (fJPEGTables->Buffer (),
fJPEGTables->LogicalSize ());
jpegTileDigest [tileCount] = printer.Result ();
}
dng_md5_printer printer2;
for (uint32 j = 0; j < tileCount + (fJPEGTables.Get () ? 1 : 0); j++)
{
printer2.Process (jpegTileDigest [j].data,
dng_fingerprint::kDNGFingerprintSize);
}
*jpegDigest = printer2.Result ();
}
// Keep the JPEG table in the jpeg image, if any.
if (jpegImage)
{
jpegImage->fJPEGTables.Reset (fJPEGTables.Release ());
}
}
/*****************************************************************************/