1/*****************************************************************************/
2// Copyright 2011 Adobe Systems Incorporated
3// All Rights Reserved.
4//
5// NOTICE:  Adobe permits you to use, modify, and distribute this file in
6// accordance with the terms of the Adobe license agreement accompanying it.
7/*****************************************************************************/
8
9/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_jpeg_image.cpp#1 $ */
10/* $DateTime: 2012/05/30 13:28:51 $ */
11/* $Change: 832332 $ */
12/* $Author: tknoll $ */
13
14/*****************************************************************************/
15
16#include "dng_jpeg_image.h"
17
18#include "dng_abort_sniffer.h"
19#include "dng_area_task.h"
20#include "dng_assertions.h"
21#include "dng_host.h"
22#include "dng_ifd.h"
23#include "dng_image.h"
24#include "dng_image_writer.h"
25#include "dng_memory_stream.h"
26#include "dng_mutex.h"
27#include "dng_safe_arithmetic.h"
28
29/*****************************************************************************/
30
31dng_jpeg_image::dng_jpeg_image ()
32
33	:	fImageSize  ()
34	,	fTileSize   ()
35	,	fUsesStrips (false)
36	,	fJPEGTables ()
37	,	fJPEGData   ()
38
39	{
40
41	}
42
43/*****************************************************************************/
44
45class dng_jpeg_image_encode_task : public dng_area_task
46	{
47
48	private:
49
50		dng_host &fHost;
51
52		dng_image_writer &fWriter;
53
54		const dng_image &fImage;
55
56		dng_jpeg_image &fJPEGImage;
57
58		uint32 fTileCount;
59
60		const dng_ifd &fIFD;
61
62		dng_mutex fMutex;
63
64		uint32 fNextTileIndex;
65
66	public:
67
68		dng_jpeg_image_encode_task (dng_host &host,
69									dng_image_writer &writer,
70									const dng_image &image,
71									dng_jpeg_image &jpegImage,
72									uint32 tileCount,
73									const dng_ifd &ifd)
74
75			:	fHost			  (host)
76			,	fWriter			  (writer)
77			,	fImage			  (image)
78			,	fJPEGImage        (jpegImage)
79			,	fTileCount		  (tileCount)
80			,	fIFD		      (ifd)
81			,	fMutex			  ("dng_jpeg_image_encode_task")
82			,	fNextTileIndex	  (0)
83
84			{
85
86			fMinTaskArea = 16 * 16;
87			fUnitCell    = dng_point (16, 16);
88			fMaxTileSize = dng_point (16, 16);
89
90			}
91
92		void Process (uint32 /* threadIndex */,
93					  const dng_rect & /* tile */,
94					  dng_abort_sniffer *sniffer)
95			{
96
97			AutoPtr<dng_memory_block> compressedBuffer;
98			AutoPtr<dng_memory_block> uncompressedBuffer;
99			AutoPtr<dng_memory_block> subTileBlockBuffer;
100			AutoPtr<dng_memory_block> tempBuffer;
101
102			uint32 uncompressedSize = SafeUint32Mult (
103				fIFD.fTileLength, fIFD.fTileWidth, fIFD.fSamplesPerPixel);
104
105			uncompressedBuffer.Reset (fHost.Allocate (uncompressedSize));
106
107			uint32 tilesAcross = fIFD.TilesAcross ();
108
109			while (true)
110				{
111
112				uint32 tileIndex;
113
114					{
115
116					dng_lock_mutex lock (&fMutex);
117
118					if (fNextTileIndex == fTileCount)
119						{
120						return;
121						}
122
123					tileIndex = fNextTileIndex++;
124
125					}
126
127				dng_abort_sniffer::SniffForAbort (sniffer);
128
129				uint32 rowIndex = tileIndex / tilesAcross;
130				uint32 colIndex = tileIndex % tilesAcross;
131
132				dng_rect tileArea = fIFD.TileArea (rowIndex, colIndex);
133
134				dng_memory_stream stream (fHost.Allocator ());
135
136				fWriter.WriteTile (fHost,
137								   fIFD,
138								   stream,
139								   fImage,
140								   tileArea,
141								   1,
142								   compressedBuffer,
143								   uncompressedBuffer,
144								   subTileBlockBuffer,
145								   tempBuffer);
146
147				fJPEGImage.fJPEGData [tileIndex].Reset (stream.AsMemoryBlock (fHost.Allocator ()));
148
149				}
150
151			}
152
153	private:
154
155		// Hidden copy constructor and assignment operator.
156
157		dng_jpeg_image_encode_task (const dng_jpeg_image_encode_task &);
158
159		dng_jpeg_image_encode_task & operator= (const dng_jpeg_image_encode_task &);
160
161	};
162
163/*****************************************************************************/
164
165void dng_jpeg_image::Encode (dng_host &host,
166							 const dng_negative &negative,
167							 dng_image_writer &writer,
168							 const dng_image &image)
169	{
170
171	#if qDNGValidate
172	dng_timer timer ("Encode JPEG Proxy time");
173	#endif
174
175	DNG_ASSERT (image.PixelType () == ttByte, "Cannot JPEG encode non-byte image");
176
177	fImageSize = image.Bounds ().Size ();
178
179	dng_ifd ifd;
180
181	ifd.fImageWidth  = fImageSize.h;
182	ifd.fImageLength = fImageSize.v;
183
184	ifd.fSamplesPerPixel = image.Planes ();
185
186	ifd.fBitsPerSample [0] = 8;
187	ifd.fBitsPerSample [1] = 8;
188	ifd.fBitsPerSample [2] = 8;
189	ifd.fBitsPerSample [3] = 8;
190
191	ifd.fPhotometricInterpretation = piLinearRaw;
192
193	ifd.fCompression = ccLossyJPEG;
194
195	ifd.FindTileSize (512 * 512 * ifd.fSamplesPerPixel);
196
197	fTileSize.h = ifd.fTileWidth;
198	fTileSize.v = ifd.fTileLength;
199
200	// Need a higher quality for raw proxies than non-raw proxies,
201	// since users often perform much greater color changes.  Also, use
202	// we are targeting a "large" size proxy (larger than 5MP pixels), or this
203	// is a full size proxy, then use a higher quality.
204
205	bool useHigherQuality = (uint64) ifd.fImageWidth *
206							(uint64) ifd.fImageLength > 5000000 ||
207							image.Bounds ().Size () == negative.OriginalDefaultFinalSize ();
208
209	if (negative.ColorimetricReference () == crSceneReferred)
210		{
211		ifd.fCompressionQuality = useHigherQuality ? 11 : 10;
212		}
213	else
214		{
215		ifd.fCompressionQuality = useHigherQuality ? 10 : 8;
216		}
217
218	uint32 tilesAcross = ifd.TilesAcross ();
219	uint32 tilesDown   = ifd.TilesDown   ();
220
221	uint32 tileCount = tilesAcross * tilesDown;
222
223	fJPEGData.Reset (tileCount);
224
225	uint32 threadCount = Min_uint32 (tileCount,
226									 host.PerformAreaTaskThreads ());
227
228	dng_jpeg_image_encode_task task (host,
229									 writer,
230									 image,
231									 *this,
232									 tileCount,
233									 ifd);
234
235	host.PerformAreaTask (task,
236						  dng_rect (0, 0, 16, 16 * threadCount));
237
238	}
239
240/*****************************************************************************/
241
242class dng_jpeg_image_find_digest_task : public dng_area_task
243	{
244
245	private:
246
247		const dng_jpeg_image &fJPEGImage;
248
249		uint32 fTileCount;
250
251		dng_fingerprint *fDigests;
252
253		dng_mutex fMutex;
254
255		uint32 fNextTileIndex;
256
257	public:
258
259		dng_jpeg_image_find_digest_task (const dng_jpeg_image &jpegImage,
260										 uint32 tileCount,
261										 dng_fingerprint *digests)
262
263			:	fJPEGImage        (jpegImage)
264			,	fTileCount		  (tileCount)
265			,	fDigests		  (digests)
266			,	fMutex			  ("dng_jpeg_image_find_digest_task")
267			,	fNextTileIndex	  (0)
268
269			{
270
271			fMinTaskArea = 16 * 16;
272			fUnitCell    = dng_point (16, 16);
273			fMaxTileSize = dng_point (16, 16);
274
275			}
276
277		void Process (uint32 /* threadIndex */,
278					  const dng_rect & /* tile */,
279					  dng_abort_sniffer *sniffer)
280			{
281
282			while (true)
283				{
284
285				uint32 tileIndex;
286
287					{
288
289					dng_lock_mutex lock (&fMutex);
290
291					if (fNextTileIndex == fTileCount)
292						{
293						return;
294						}
295
296					tileIndex = fNextTileIndex++;
297
298					}
299
300				dng_abort_sniffer::SniffForAbort (sniffer);
301
302				dng_md5_printer printer;
303
304				printer.Process (fJPEGImage.fJPEGData [tileIndex]->Buffer      (),
305								 fJPEGImage.fJPEGData [tileIndex]->LogicalSize ());
306
307				fDigests [tileIndex] = printer.Result ();
308
309				}
310
311			}
312
313	private:
314
315		// Hidden copy constructor and assignment operator.
316
317		dng_jpeg_image_find_digest_task (const dng_jpeg_image_find_digest_task &);
318
319		dng_jpeg_image_find_digest_task & operator= (const dng_jpeg_image_find_digest_task &);
320
321	};
322
323/*****************************************************************************/
324
325dng_fingerprint dng_jpeg_image::FindDigest (dng_host &host) const
326	{
327
328	uint32 tileCount = TileCount ();
329
330	uint32 arrayCount = tileCount + (fJPEGTables.Get () ? 1 : 0);
331
332	AutoArray<dng_fingerprint> digests (arrayCount);
333
334	// Compute digest of each compressed tile.
335
336		{
337
338		uint32 threadCount = Min_uint32 (tileCount,
339										 host.PerformAreaTaskThreads ());
340
341		dng_jpeg_image_find_digest_task task (*this,
342											  tileCount,
343											  digests.Get ());
344
345		host.PerformAreaTask (task,
346							  dng_rect (0, 0, 16, 16 * threadCount));
347
348		}
349
350	// Compute digest of JPEG tables, if any.
351
352	if (fJPEGTables.Get ())
353		{
354
355		dng_md5_printer printer;
356
357		printer.Process (fJPEGTables->Buffer      (),
358						 fJPEGTables->LogicalSize ());
359
360		digests [tileCount] = printer.Result ();
361
362		}
363
364	// Combine digests into a single digest.
365
366		{
367
368		dng_md5_printer printer;
369
370		for (uint32 k = 0; k < arrayCount; k++)
371			{
372
373			printer.Process (digests [k].data,
374							 dng_fingerprint::kDNGFingerprintSize);
375
376			}
377
378		return printer.Result ();
379
380		}
381
382	}
383
384/*****************************************************************************/
385
386