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