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