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