/*****************************************************************************/ // 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 /******************************************************************************/ 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 = 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 &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 (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 &uncompressedBuffer, AutoPtr &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(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 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::max () - 2) { ThrowEndOfFile(); } tablesByteCount -= 2; tileOffset += 2; tileByteCount -= 2; } // Allocate buffer. AutoPtr 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 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) { // If the tile area is empty, there's nothing to read. if (tileArea.IsEmpty ()) { return true; } uint32 bytesPerRow = SafeUint32Mult (tileArea.W(), planes, static_cast (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 &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 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 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 = 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 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 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 ()); } } /*****************************************************************************/