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.
386 lines
8.5 KiB
386 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 ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|