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 
dng_jpeg_image()31 dng_jpeg_image::dng_jpeg_image ()
32 
33 	:	fImageSize  ()
34 	,	fTileSize   ()
35 	,	fUsesStrips (false)
36 	,	fJPEGTables ()
37 	,	fJPEGData   ()
38 
39 	{
40 
41 	}
42 
43 /*****************************************************************************/
44 
45 class 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 
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)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 
Process(uint32,const dng_rect &,dng_abort_sniffer * sniffer)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 
Encode(dng_host & host,const dng_negative & negative,dng_image_writer & writer,const dng_image & image)165 void 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 
242 class 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 
dng_jpeg_image_find_digest_task(const dng_jpeg_image & jpegImage,uint32 tileCount,dng_fingerprint * digests)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 
Process(uint32,const dng_rect &,dng_abort_sniffer * sniffer)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 
FindDigest(dng_host & host) const325 dng_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