You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
385 lines
8.5 KiB
385 lines
8.5 KiB
/*****************************************************************************/ |
|
// Copyright 2011 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_jpeg_image.cpp#1 $ */ |
|
/* $DateTime: 2012/05/30 13:28:51 $ */ |
|
/* $Change: 832332 $ */ |
|
/* $Author: tknoll $ */ |
|
|
|
/*****************************************************************************/ |
|
|
|
#include "dng_jpeg_image.h" |
|
|
|
#include "dng_abort_sniffer.h" |
|
#include "dng_area_task.h" |
|
#include "dng_assertions.h" |
|
#include "dng_host.h" |
|
#include "dng_ifd.h" |
|
#include "dng_image.h" |
|
#include "dng_image_writer.h" |
|
#include "dng_memory_stream.h" |
|
#include "dng_mutex.h" |
|
#include "dng_safe_arithmetic.h" |
|
|
|
/*****************************************************************************/ |
|
|
|
dng_jpeg_image::dng_jpeg_image () |
|
|
|
: fImageSize () |
|
, fTileSize () |
|
, fUsesStrips (false) |
|
, fJPEGTables () |
|
, fJPEGData () |
|
|
|
{ |
|
|
|
} |
|
|
|
/*****************************************************************************/ |
|
|
|
class dng_jpeg_image_encode_task : public dng_area_task |
|
{ |
|
|
|
private: |
|
|
|
dng_host &fHost; |
|
|
|
dng_image_writer &fWriter; |
|
|
|
const dng_image &fImage; |
|
|
|
dng_jpeg_image &fJPEGImage; |
|
|
|
uint32 fTileCount; |
|
|
|
const dng_ifd &fIFD; |
|
|
|
dng_mutex fMutex; |
|
|
|
uint32 fNextTileIndex; |
|
|
|
public: |
|
|
|
dng_jpeg_image_encode_task (dng_host &host, |
|
dng_image_writer &writer, |
|
const dng_image &image, |
|
dng_jpeg_image &jpegImage, |
|
uint32 tileCount, |
|
const dng_ifd &ifd) |
|
|
|
: fHost (host) |
|
, fWriter (writer) |
|
, fImage (image) |
|
, fJPEGImage (jpegImage) |
|
, fTileCount (tileCount) |
|
, fIFD (ifd) |
|
, fMutex ("dng_jpeg_image_encode_task") |
|
, fNextTileIndex (0) |
|
|
|
{ |
|
|
|
fMinTaskArea = 16 * 16; |
|
fUnitCell = dng_point (16, 16); |
|
fMaxTileSize = dng_point (16, 16); |
|
|
|
} |
|
|
|
void Process (uint32 /* threadIndex */, |
|
const dng_rect & /* tile */, |
|
dng_abort_sniffer *sniffer) |
|
{ |
|
|
|
AutoPtr<dng_memory_block> compressedBuffer; |
|
AutoPtr<dng_memory_block> uncompressedBuffer; |
|
AutoPtr<dng_memory_block> subTileBlockBuffer; |
|
AutoPtr<dng_memory_block> tempBuffer; |
|
|
|
uint32 uncompressedSize = SafeUint32Mult ( |
|
fIFD.fTileLength, fIFD.fTileWidth, fIFD.fSamplesPerPixel); |
|
|
|
uncompressedBuffer.Reset (fHost.Allocate (uncompressedSize)); |
|
|
|
uint32 tilesAcross = fIFD.TilesAcross (); |
|
|
|
while (true) |
|
{ |
|
|
|
uint32 tileIndex; |
|
|
|
{ |
|
|
|
dng_lock_mutex lock (&fMutex); |
|
|
|
if (fNextTileIndex == fTileCount) |
|
{ |
|
return; |
|
} |
|
|
|
tileIndex = fNextTileIndex++; |
|
|
|
} |
|
|
|
dng_abort_sniffer::SniffForAbort (sniffer); |
|
|
|
uint32 rowIndex = tileIndex / tilesAcross; |
|
uint32 colIndex = tileIndex % tilesAcross; |
|
|
|
dng_rect tileArea = fIFD.TileArea (rowIndex, colIndex); |
|
|
|
dng_memory_stream stream (fHost.Allocator ()); |
|
|
|
fWriter.WriteTile (fHost, |
|
fIFD, |
|
stream, |
|
fImage, |
|
tileArea, |
|
1, |
|
compressedBuffer, |
|
uncompressedBuffer, |
|
subTileBlockBuffer, |
|
tempBuffer); |
|
|
|
fJPEGImage.fJPEGData [tileIndex].Reset (stream.AsMemoryBlock (fHost.Allocator ())); |
|
|
|
} |
|
|
|
} |
|
|
|
private: |
|
|
|
// Hidden copy constructor and assignment operator. |
|
|
|
dng_jpeg_image_encode_task (const dng_jpeg_image_encode_task &); |
|
|
|
dng_jpeg_image_encode_task & operator= (const dng_jpeg_image_encode_task &); |
|
|
|
}; |
|
|
|
/*****************************************************************************/ |
|
|
|
void dng_jpeg_image::Encode (dng_host &host, |
|
const dng_negative &negative, |
|
dng_image_writer &writer, |
|
const dng_image &image) |
|
{ |
|
|
|
#if qDNGValidate |
|
dng_timer timer ("Encode JPEG Proxy time"); |
|
#endif |
|
|
|
DNG_ASSERT (image.PixelType () == ttByte, "Cannot JPEG encode non-byte image"); |
|
|
|
fImageSize = image.Bounds ().Size (); |
|
|
|
dng_ifd ifd; |
|
|
|
ifd.fImageWidth = fImageSize.h; |
|
ifd.fImageLength = fImageSize.v; |
|
|
|
ifd.fSamplesPerPixel = image.Planes (); |
|
|
|
ifd.fBitsPerSample [0] = 8; |
|
ifd.fBitsPerSample [1] = 8; |
|
ifd.fBitsPerSample [2] = 8; |
|
ifd.fBitsPerSample [3] = 8; |
|
|
|
ifd.fPhotometricInterpretation = piLinearRaw; |
|
|
|
ifd.fCompression = ccLossyJPEG; |
|
|
|
ifd.FindTileSize (512 * 512 * ifd.fSamplesPerPixel); |
|
|
|
fTileSize.h = ifd.fTileWidth; |
|
fTileSize.v = ifd.fTileLength; |
|
|
|
// Need a higher quality for raw proxies than non-raw proxies, |
|
// since users often perform much greater color changes. Also, use |
|
// we are targeting a "large" size proxy (larger than 5MP pixels), or this |
|
// is a full size proxy, then use a higher quality. |
|
|
|
bool useHigherQuality = (uint64) ifd.fImageWidth * |
|
(uint64) ifd.fImageLength > 5000000 || |
|
image.Bounds ().Size () == negative.OriginalDefaultFinalSize (); |
|
|
|
if (negative.ColorimetricReference () == crSceneReferred) |
|
{ |
|
ifd.fCompressionQuality = useHigherQuality ? 11 : 10; |
|
} |
|
else |
|
{ |
|
ifd.fCompressionQuality = useHigherQuality ? 10 : 8; |
|
} |
|
|
|
uint32 tilesAcross = ifd.TilesAcross (); |
|
uint32 tilesDown = ifd.TilesDown (); |
|
|
|
uint32 tileCount = tilesAcross * tilesDown; |
|
|
|
fJPEGData.Reset (tileCount); |
|
|
|
uint32 threadCount = Min_uint32 (tileCount, |
|
host.PerformAreaTaskThreads ()); |
|
|
|
dng_jpeg_image_encode_task task (host, |
|
writer, |
|
image, |
|
*this, |
|
tileCount, |
|
ifd); |
|
|
|
host.PerformAreaTask (task, |
|
dng_rect (0, 0, 16, 16 * threadCount)); |
|
|
|
} |
|
|
|
/*****************************************************************************/ |
|
|
|
class dng_jpeg_image_find_digest_task : public dng_area_task |
|
{ |
|
|
|
private: |
|
|
|
const dng_jpeg_image &fJPEGImage; |
|
|
|
uint32 fTileCount; |
|
|
|
dng_fingerprint *fDigests; |
|
|
|
dng_mutex fMutex; |
|
|
|
uint32 fNextTileIndex; |
|
|
|
public: |
|
|
|
dng_jpeg_image_find_digest_task (const dng_jpeg_image &jpegImage, |
|
uint32 tileCount, |
|
dng_fingerprint *digests) |
|
|
|
: fJPEGImage (jpegImage) |
|
, fTileCount (tileCount) |
|
, fDigests (digests) |
|
, fMutex ("dng_jpeg_image_find_digest_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) |
|
{ |
|
|
|
while (true) |
|
{ |
|
|
|
uint32 tileIndex; |
|
|
|
{ |
|
|
|
dng_lock_mutex lock (&fMutex); |
|
|
|
if (fNextTileIndex == fTileCount) |
|
{ |
|
return; |
|
} |
|
|
|
tileIndex = fNextTileIndex++; |
|
|
|
} |
|
|
|
dng_abort_sniffer::SniffForAbort (sniffer); |
|
|
|
dng_md5_printer printer; |
|
|
|
printer.Process (fJPEGImage.fJPEGData [tileIndex]->Buffer (), |
|
fJPEGImage.fJPEGData [tileIndex]->LogicalSize ()); |
|
|
|
fDigests [tileIndex] = printer.Result (); |
|
|
|
} |
|
|
|
} |
|
|
|
private: |
|
|
|
// Hidden copy constructor and assignment operator. |
|
|
|
dng_jpeg_image_find_digest_task (const dng_jpeg_image_find_digest_task &); |
|
|
|
dng_jpeg_image_find_digest_task & operator= (const dng_jpeg_image_find_digest_task &); |
|
|
|
}; |
|
|
|
/*****************************************************************************/ |
|
|
|
dng_fingerprint dng_jpeg_image::FindDigest (dng_host &host) const |
|
{ |
|
|
|
uint32 tileCount = TileCount (); |
|
|
|
uint32 arrayCount = tileCount + (fJPEGTables.Get () ? 1 : 0); |
|
|
|
AutoArray<dng_fingerprint> digests (arrayCount); |
|
|
|
// Compute digest of each compressed tile. |
|
|
|
{ |
|
|
|
uint32 threadCount = Min_uint32 (tileCount, |
|
host.PerformAreaTaskThreads ()); |
|
|
|
dng_jpeg_image_find_digest_task task (*this, |
|
tileCount, |
|
digests.Get ()); |
|
|
|
host.PerformAreaTask (task, |
|
dng_rect (0, 0, 16, 16 * threadCount)); |
|
|
|
} |
|
|
|
// Compute digest of JPEG tables, if any. |
|
|
|
if (fJPEGTables.Get ()) |
|
{ |
|
|
|
dng_md5_printer printer; |
|
|
|
printer.Process (fJPEGTables->Buffer (), |
|
fJPEGTables->LogicalSize ()); |
|
|
|
digests [tileCount] = printer.Result (); |
|
|
|
} |
|
|
|
// Combine digests into a single digest. |
|
|
|
{ |
|
|
|
dng_md5_printer printer; |
|
|
|
for (uint32 k = 0; k < arrayCount; k++) |
|
{ |
|
|
|
printer.Process (digests [k].data, |
|
dng_fingerprint::kDNGFingerprintSize); |
|
|
|
} |
|
|
|
return printer.Result (); |
|
|
|
} |
|
|
|
} |
|
|
|
/*****************************************************************************/ |
|
|
|
|