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