You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3408 lines
64 KiB
3408 lines
64 KiB
/*****************************************************************************/
|
|
// 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 ());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|