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 | |
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 | |
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 | |
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 | |
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 | |
325 | 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 | |