From ab987e10f154f5536bb8fd936ae0966e909fa969 Mon Sep 17 00:00:00 2001 From: luxagraf Date: Thu, 15 Jun 2023 15:58:59 -0500 Subject: added all my scripts --- gpr/source/lib/dng_sdk/dng_read_image.cpp | 3194 +++++++++++++++++++++++++++++ 1 file changed, 3194 insertions(+) create mode 100644 gpr/source/lib/dng_sdk/dng_read_image.cpp (limited to 'gpr/source/lib/dng_sdk/dng_read_image.cpp') diff --git a/gpr/source/lib/dng_sdk/dng_read_image.cpp b/gpr/source/lib/dng_sdk/dng_read_image.cpp new file mode 100644 index 0000000..4b83d8c --- /dev/null +++ b/gpr/source/lib/dng_sdk/dng_read_image.cpp @@ -0,0 +1,3194 @@ +/*****************************************************************************/ +// 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_tag_types.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +/******************************************************************************/ + +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 &tempBuffer) + { + + uint32 tempBufferSize = buffer.fArea.W () * + buffer.fArea.H () * + buffer.fPlanes * + buffer.fPixelSize; + + 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 &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 &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 &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; + + buffer.fArea = fTileStrip; + + buffer.fPlane = fPlane; + buffer.fPlanes = fPlanes; + + buffer.fRowStep = fPlanes * fTileStrip.W (); + buffer.fColStep = fPlanes; + buffer.fPlaneStep = 1; + + buffer.fData = fBuffer; + + buffer.fPixelType = ttShort; + buffer.fPixelSize = 2; + + 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 &uncompressedBuffer, + AutoPtr &subTileBlockBuffer) + { + + uint32 rows = tileArea.H (); + uint32 samplesPerRow = tileArea.W (); + + if (ifd.fPlanarConfiguration == pcRowInterleaved) + { + rows *= planes; + } + else + { + samplesPerRow *= planes; + } + + uint32 samplesPerTile = samplesPerRow * rows; + + dng_pixel_buffer buffer; + + buffer.fArea = tileArea; + + buffer.fPlane = plane; + buffer.fPlanes = planes; + + buffer.fRowStep = planes * tileArea.W (); + + if (ifd.fPlanarConfiguration == pcRowInterleaved) + { + buffer.fColStep = 1; + buffer.fPlaneStep = tileArea.W (); + } + + else + { + buffer.fColStep = planes; + buffer.fPlaneStep = 1; + } + + if (uncompressedBuffer.Get () == NULL) + { + + #if qDNGValidate + + ReportError ("Fuzz: Missing uncompressed buffer"); + + #endif + + ThrowBadFormat (); + + } + + buffer.fData = uncompressedBuffer->Buffer (); + + uint32 bitDepth = ifd.fBitsPerSample [plane]; + + if (bitDepth == 8) + { + + buffer.fPixelType = ttByte; + buffer.fPixelSize = 1; + + stream.Get (buffer.fData, samplesPerTile); + + } + + else if (bitDepth == 16 && ifd.fSampleFormat [0] == sfFloatingPoint) + { + + buffer.fPixelType = ttFloat; + buffer.fPixelSize = 4; + + uint32 *p_uint32 = (uint32 *) buffer.fData; + + for (uint32 j = 0; j < samplesPerTile; j++) + { + + p_uint32 [j] = DNG_HalfToFloat (stream.Get_uint16 ()); + + } + + } + + else if (bitDepth == 24 && ifd.fSampleFormat [0] == sfFloatingPoint) + { + + buffer.fPixelType = ttFloat; + buffer.fPixelSize = 4; + + uint32 *p_uint32 = (uint32 *) buffer.fData; + + 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) + { + + buffer.fPixelType = ttShort; + buffer.fPixelSize = 2; + + stream.Get (buffer.fData, samplesPerTile * 2); + + if (stream.SwapBytes ()) + { + + DoSwapBytes16 ((uint16 *) buffer.fData, + samplesPerTile); + + } + + } + + else if (bitDepth == 32) + { + + buffer.fPixelType = image.PixelType (); + buffer.fPixelSize = 4; + + stream.Get (buffer.fData, samplesPerTile * 4); + + if (stream.SwapBytes ()) + { + + DoSwapBytes32 ((uint32 *) buffer.fData, + samplesPerTile); + + } + + } + + else if (bitDepth == 12) + { + + buffer.fPixelType = ttShort; + buffer.fPixelSize = 2; + + uint16 *p = (uint16 *) buffer.fData; + + 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) + { + + buffer.fPixelType = ttShort; + buffer.fPixelSize = 2; + + uint16 *p = (uint16 *) buffer.fData; + + 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) + { + + buffer.fPixelType = ttLong; + buffer.fPixelSize = 4; + + uint32 *p = (uint32 *) buffer.fData; + + uint32 bitMask = (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; + + } + + if (ifd.fSampleBitShift) + { + + buffer.ShiftRight (ifd.fSampleBitShift); + + } + + if (ifd.fSubTileBlockRows > 1) + { + + ReorderSubTileBlocks (host, + ifd, + buffer, + subTileBlockBuffer); + + } + + image.Put (buffer); + + return true; + + } + +/*****************************************************************************/ + +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) + { + + // 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"); + + } + +/*****************************************************************************/ + +static dng_memory_block * ReadJPEGDataToBlock (dng_host &host, + dng_stream &stream, + dng_memory_block *tablesBlock, + uint64 tileOffset, + uint32 tileByteCount, + bool patchFirstByte) + { + + if (tileByteCount <= 2) + { + ThrowEndOfFile (); + } + + uint32 tablesByteCount = tablesBlock ? tablesBlock->LogicalSize () : 0; + + 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) + { + + tablesByteCount -= 2; + + tileOffset += 2; + tileByteCount -= 2; + + } + + // Allocate buffer. + + AutoPtr buffer (host.Allocate (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 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 &uncompressedBuffer, + AutoPtr &subTileBlockBuffer) + { + + uint32 bytesPerRow = tileArea.W () * planes * (uint32) sizeof (uint16); + + uint32 rowsPerStrip = Pin_uint32 (ifd.fSubTileBlockRows, + kImageBufferSize / bytesPerRow, + tileArea.H ()); + + rowsPerStrip = rowsPerStrip / ifd.fSubTileBlockRows + * ifd.fSubTileBlockRows; + + uint32 bufferSize = 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 = 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 &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer) + { + + switch (ifd.fCompression) + { + case ccLZW: + case ccDeflate: + case ccOldDeflate: + case ccPackBits: + { + + // Figure out uncompressed size. + + uint32 bytesPerSample = (ifd.fBitsPerSample [0] >> 3); + + uint32 sampleCount = planes * tileArea.W () * tileArea.H (); + + uint32 uncompressedSize = sampleCount * bytesPerSample; + + // Setup pixel buffer to hold uncompressed data. + + dng_pixel_buffer buffer; + + if (ifd.fSampleFormat [0] == sfFloatingPoint) + { + buffer.fPixelType = ttFloat; + } + + else if (ifd.fBitsPerSample [0] == 8) + { + buffer.fPixelType = ttByte; + } + + else if (ifd.fBitsPerSample [0] == 16) + { + buffer.fPixelType = ttShort; + } + + else if (ifd.fBitsPerSample [0] == 32) + { + buffer.fPixelType = ttLong; + } + + else + { + ThrowBadFormat (); + } + + buffer.fPixelSize = bytesPerSample; + + buffer.fArea = tileArea; + + buffer.fPlane = plane; + buffer.fPlanes = planes; + + buffer.fPlaneStep = 1; + + buffer.fColStep = planes; + buffer.fRowStep = planes * tileArea.W (); + + 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) + { + bufferSize += buffer.fRowStep * buffer.fPixelSize; + } + + // 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, 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 + { + // Not supported currently + ThrowProgramError(); + } + + // 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); + uint8 *dstPtr = (uint8 *) buffer.DirtyPixel (row - 1, tileArea.l, plane); + + 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 compressedBuffer; + AutoPtr uncompressedBuffer; + AutoPtr 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 = tilesAcross * tilesDown * outerSamples; + + // Find the tile offsets. + + dng_memory_data tileOffsetData (tileCount * (uint32) 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 = ifd.fTileWidth * innerSamples * bytesPerPixel; + + subTileLength = Pin_uint32 (ifd.fSubTileBlockRows, + kImageBufferSize / bytesPerRow, + ifd.fTileLength); + + subTileLength = subTileLength / ifd.fSubTileBlockRows + * ifd.fSubTileBlockRows; + + uncompressedSize = subTileLength * bytesPerRow; + + } + + // Else we need to know the byte counts. + + else + { + + tileByteCountData.Allocate (tileCount * (uint32) 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 (new dng_jpeg_image_tile_ptr [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 jpegTileDigest; + + if (jpegDigest) + { + + jpegTileDigest.Reset (new dng_fingerprint [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 compressedBuffer; + AutoPtr uncompressedBuffer; + AutoPtr 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 ()); + + } + + } + +/*****************************************************************************/ -- cgit v1.2.3-70-g09d2