/*****************************************************************************/
// 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 ();
}
}
/*****************************************************************************/