Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(231)

Side by Side Diff: src/codec/SkJpegCodec.cpp

Issue 1076923002: SkJpegCodec (Closed) Base URL: https://skia.googlesource.com/skia.git@gif-real
Patch Set: JpegAutoClean is easier to use, Scaling is tested Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkJpegCodec.h"
9
10 #include "SkCodec.h"
11 #include "SkJpegAutoClean.h"
12 #include "SkJpegCodec.h"
13 #include "SkJpegUtility.h"
14 #include "SkCodecPriv.h"
15 #include "SkColorPriv.h"
16 #include "SkStream.h"
17 #include "SkTemplates.h"
18 #include "SkTypes.h"
19 #define IDCT_SCALING_SUPPORTED
scroggo 2015/04/14 13:10:33 Is this necessary? Does it need to be defined bef
msarett 2015/04/14 19:30:36 This is a mistake. I meant to delete this.
20 // stdio is needed for jpeglib
21 #include <stdio.h>
scroggo 2015/04/14 13:10:33 Again, please keep these separate from the Skia in
msarett 2015/04/14 19:30:36 Done.
22 #include "jerror.h"
23 #include "jpegint.h"
24 #include "jpeglib.h"
25
26 // ANDROID_RGB
27 // If this is defined in the jpeg headers it indicates that jpeg offers
28 // support for two additional formats: JCS_RGBA_8888 and JCS_RGB_565.
29
30 /*
31 * Choose the size of the memory buffer on Android
32 */
33 static void overwrite_mem_buffer_size(jpeg_decompress_struct* dinfo) {
34 #ifdef SK_BUILD_FOR_ANDROID
35
36 // Use 30 MB for devices with a large amount of system memory and 5MB otherwise
37 // TODO: (msarett) This matches SkImageDecoder. Why were these values chosen?
38 #ifdef ANDROID_LARGE_MEMORY_DEVICE
39 dinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
40 #else
41 dinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
42 #endif
43
44 #endif // SK_BUILD_FOR_ANDROID
45 }
46
47 /*
48 * Print informative error messages
49 */
50 static void print_jpeg_decoder_errors(const jpeg_decompress_struct& dinfo, int w idth,
51 int height, const char caller[]) {
52 char buffer[JMSG_LENGTH_MAX];
53 dinfo.err->format_message((const j_common_ptr) &dinfo, buffer);
54 SkCodecPrintf("libjpeg error %d <%s> from %s [%d %d]\n",
55 dinfo.err->msg_code, buffer, caller, width, height);
56 }
57
58 static bool return_false(const jpeg_decompress_struct& dinfo, const char caller[ ]) {
59 print_jpeg_decoder_errors(dinfo, dinfo.output_width, dinfo.output_height, ca ller);
60 return false;
61 }
62
63 static SkCodec::Result return_failure(const jpeg_decompress_struct& dinfo, const char caller[],
64 SkCodec::Result result) {
65 print_jpeg_decoder_errors(dinfo, dinfo.output_width, dinfo.output_height, ca ller);
66 return result;
67 }
68
69 /*
70 * Convert a row of CMYK samples to RGBX in place.
71 * Note that this method moves the row pointer.
72 * @param width the number of pixels in the row that is being converted
73 * CMYK is stored as four bytes per pixel
74 */
75 static void convert_CMYK_to_RGB(uint8_t* row, uint32_t width) {
76 // We will implement a crude conversion from CMYK -> RGB using formulas
77 // from easyrgb.com.
78 //
79 // CMYK -> CMY
80 // C = C * (1 - K) + K
81 // M = M * (1 - K) + K
82 // Y = Y * (1 - K) + K
83 //
84 // libjpeg actually gives us inverted CMYK, so we must subtract the
85 // original terms from 1.
86 // CMYK -> CMY
87 // C = (1 - C) * (1 - (1 - K)) + (1 - K)
88 // M = (1 - M) * (1 - (1 - K)) + (1 - K)
89 // Y = (1 - Y) * (1 - (1 - K)) + (1 - K)
90 //
91 // Simplifying the above expression.
92 // CMYK -> CMY
93 // C = 1 - CK
94 // M = 1 - MK
95 // Y = 1 - YK
96 //
97 // CMY -> RGB
98 // R = (1 - C) * 255
99 // G = (1 - M) * 255
100 // B = (1 - Y) * 255
101 //
102 // Therefore the full conversion is below. This can be verified at
103 // www.rapidtables.com (assuming inverted CMYK).
104 // CMYK -> RGB
105 // R = C * K * 255
106 // G = M * K * 255
107 // B = Y * K * 255
108 //
109 // As a final note, we have treated the CMYK values as if they were on
110 // a scale from 0-1, when in fact they are 8-bit ints scaling from 0-255.
111 // We must divide each CMYK component by 255 to obtain the true conversion
112 // we should perform.
113 // CMYK -> RGB
114 // R = C * K / 255
115 // G = M * K / 255
116 // B = Y * K / 255
117 for (uint32_t x = 0; x < width; x++, row += 4) {
118 row[0] = SkMulDiv255Round(row[0], row[3]);
119 row[1] = SkMulDiv255Round(row[1], row[3]);
120 row[2] = SkMulDiv255Round(row[2], row[3]);
121 row[3] = 0xFF;
122 }
123 }
124
125 /*
126 * Get the preferred color type to decode to based on the properties of the jpeg .
127 * Also, inform libjpeg what format to decode to.
128 */
129 SkColorType get_color_type(jpeg_decompress_struct* dinfo) {
130 SkASSERT(dinfo != NULL);
131
132 switch (dinfo->jpeg_color_space) {
133 case JCS_CMYK:
134 case JCS_YCCK:
135 // libjpeg cannot convert from CMYK or YCCK to RGB.
136 // Here, we ask libjpeg to give us CMYK samples back and
137 // we will later manually convert them to RGB.
138 dinfo->out_color_space = JCS_CMYK;
139 return kN32_SkColorType;
140 case JCS_GRAYSCALE:
141 dinfo->out_color_space = JCS_GRAYSCALE;
142 return kGray_8_SkColorType;
143 default:
144 #ifdef ANDROID_RGB
145 dinfo->out_color_space = JCS_RGBA_8888;
146 #else
147 dinfo->out_color_space = JCS_RGB;
148 #endif
149 return kN32_SkColorType;
150 }
151 }
152
153 /*
154 * Get the config and bytes per pixel of the source data.
155 * Return whether the data is supported.
156 */
157 static SkSwizzler::SrcConfig get_src_config(const jpeg_decompress_struct* dinfo) {
158 // We will manually convert CMYK to RGB
159 if (JCS_CMYK == dinfo->out_color_space) {
160 return SkSwizzler::kRGBX;
161 }
162
163 // RGB
164 if (3 == dinfo->out_color_components && JCS_RGB == dinfo->out_color_space) {
165 return SkSwizzler::kRGB;
166 }
167
168 // RGB for Android
169 #ifdef ANDROID_RGB
170 if (JCS_RGBA_8888 == dinfo->out_color_space) {
171 return SkSwizzler::kRGBX;
172 }
173 if (JCS_RGB_565 == dinfo->out_color_space) {
174 return SkSwizzler::kRGB_565;
175 }
176 #endif
177
178 // Grayscale
179 if (1 == dinfo->out_color_components && JCS_GRAYSCALE == dinfo->out_color_sp ace) {
180 return SkSwizzler::kGray;
181 }
182
183 // Indicate that we cannot find a matching config
184 return SkSwizzler::kUnknown;
scroggo 2015/04/14 13:10:32 I'm assuming CreateSwizzler will fail for kUnknown
msarett 2015/04/14 19:30:36 Yes it will return NULL. And we do a NULL check o
185 }
186
187 bool SkJpegCodec::IsJpeg(SkStream* stream) {
188 static const uint8_t jpegSig[] = { 0xFF, 0xD8, 0xFF };
189 char buffer[sizeof(jpegSig)];
190 return stream->read(buffer, sizeof(jpegSig)) == sizeof(jpegSig) &&
191 !memcmp(buffer, jpegSig, sizeof(jpegSig));
192 }
193
194 bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut,
195 JpegAutoClean** decodeMgrOut) {
196 // The decompress info struct holds all of the information related to the de code
197 jpeg_decompress_struct* dinfo = SkNEW(jpeg_decompress_struct);
scroggo 2015/04/14 13:10:32 Here is what I would do. Make dinfo a member of Jp
msarett 2015/04/14 19:30:36 Done.
198
199 // The source manager handles the input stream for libjpeg
200 skjpeg_source_mgr* srcMgr = SkNEW_ARGS(skjpeg_source_mgr, (stream));
201
202 // Set up the error manager. This must be set before the call to jpeg_creat e_decompress
203 // in case there is a failure in initializing memory in libjpeg.
204 skjpeg_error_mgr* errorMgr = SkNEW(skjpeg_error_mgr);
205 dinfo->err = jpeg_std_error(errorMgr);
206 errorMgr->error_exit = skjpeg_err_exit;
207
208 // Use a containter to automatically free memory.
209 SkAutoTDelete<JpegAutoClean> decodeMgr(SkNEW_ARGS(JpegAutoClean, (dinfo, src Mgr, errorMgr)));
210
211 // All objects need to be instantiated before this setjmp call so that
212 // they will be cleaned up properly if an error occurs.
213 if (setjmp(errorMgr->fJmpBuf)) {
214 return return_false(*dinfo, "setjmp");
215 }
216
217 // Initialize the jpeg info struct to prepare to decode
218 jpeg_create_decompress(dinfo);
219 dinfo->src = srcMgr;
220 overwrite_mem_buffer_size(dinfo);
221
222 // Read the jpeg header
223 int status = jpeg_read_header(dinfo, true);
224 if (status != JPEG_HEADER_OK) {
225 return return_false(*dinfo, "read_header");
226 }
227
228 // Choose a method of performing the discrete cosine transform
229 #ifdef DCT_IFAST_SUPPORTED
msarett 2015/04/13 20:54:06 Do we want to use this? DCT_IFAST is faster but l
scroggo 2015/04/14 13:10:33 I've been following a bug where people are complai
msarett 2015/04/14 19:30:36 Cool that will simplify things. AFAICT we have th
scroggo 2015/04/15 00:31:05 FYI: The bug I referenced is here: https://code.go
230 dinfo->dct_method = JDCT_IFAST;
231 #else
232 dinfo->dct_method = JDCT_ISLOW;
233 #endif
234
235 if (NULL != codecOut) {
236 // Recommend the color type to decode to
237 const SkColorType colorType = get_color_type(dinfo);
238
239 // Create image info object and the codec
240 const SkImageInfo& imageInfo = SkImageInfo::Make(dinfo->image_width,
241 dinfo->image_height, colorType, kOpaque_SkAlphaType);
242 *codecOut = SkNEW_ARGS(SkJpegCodec, (imageInfo, stream, decodeMgr.detach ()));
243 } else {
244 SkASSERT(NULL != decodeMgrOut);
245 *decodeMgrOut = decodeMgr.detach();
246 }
247 return true;
248 }
249
250 SkCodec* SkJpegCodec::NewFromStream(SkStream* stream) {
251 SkAutoTDelete<SkStream> streamDeleter(stream);
252 SkCodec* codec = NULL;
253 if (ReadHeader(stream, &codec, NULL)) {
254 // Codec has taken ownership of the stream, we do not need to delete it
255 SkASSERT(codec);
256 streamDeleter.detach();
257 return codec;
258 }
259 return NULL;
260 }
261
262 SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream,
263 JpegAutoClean* decodeMgr)
264 : INHERITED(srcInfo, stream)
265 , fDecodeMgr(decodeMgr)
266 {}
267
268 /*
269 * Divide and round up
270 */
271 long div_round_up (long a, long b) {
272 return (a + b - 1L) / b;
273 }
274
275 /*
276 * Return a valid set of output dimensions for this decoder, given an input scal e
277 */
278 SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const {
msarett 2015/04/13 20:54:06 This function is not in a finished state, but I di
scroggo 2015/04/14 13:10:32 It looks like you defined it yourself at the top o
msarett 2015/04/14 19:30:36 Yeah I forgot to remove the define at the top of t
scroggo 2015/04/15 00:31:05 It seems like both are a bit hacky: Option 1: Cop
msarett 2015/04/15 12:43:11 I agree that it is unfortunate that both approache
279 // libjpeg supports scaling by 1/1, 1/2, 1/4, and 1/8, so we will support th ese as well
280 long scale;
281 if (desiredScale > 0.75f) {
282 scale = 1;
283 } else if (desiredScale > 0.375f) {
284 scale = 2;
285 } else if (desiredScale > 0.1875f) {
286 scale = 4;
287 } else {
288 scale = 8;
289 }
290
291 // Return the calculated output dimensions for the given scale
292 fDecodeMgr->dinfo()->scale_denom = 1;
293 return SkISize::Make(div_round_up((long) fDecodeMgr->dinfo()->image_width, s cale),
294 div_round_up((long) fDecodeMgr->dinfo()->image_height, scale));
295 }
296
297 /*
298 * Checks if the conversion between the input image and the requested output
299 * image has been implemented
300 */
301 static bool conversion_possible(const SkImageInfo& dst,
302 const SkImageInfo& src) {
303 // Ensure that the profile type is unchanged
304 if (dst.profileType() != src.profileType()) {
305 return false;
306 }
307
308 // Ensure that the alpha type is opaque
309 if (kOpaque_SkAlphaType != dst.alphaType()) {
310 return false;
311 }
312
313 // Always allow kN32 as the color type
314 if (kN32_SkColorType == dst.colorType()) {
315 return true;
316 }
317
318 // Otherwise require that the destination color type match our recommendatio n
319 return dst.colorType() == src.colorType();
320 }
321
322 /*
323 * Initiates the jpeg decode
324 */
325 SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
326 void* dst, size_t dstRowBytes,
327 const Options& options, SkPMColor*, int*) {
328 // Rewind the stream if needed
329 SkCodec::RewindState rewindState = this->rewindIfNeeded();
330 if (rewindState == kCouldNotRewind_RewindState) {
331 return kCouldNotRewind;
332 } else if (rewindState == kRewound_RewindState) {
333 JpegAutoClean* decodeMgr = NULL;
334 if (!ReadHeader(this->stream(), NULL, &decodeMgr)) {
335 return kCouldNotRewind;
336 }
337 SkASSERT(NULL != decodeMgr);
338 fDecodeMgr.reset(decodeMgr);
339 }
340
341 // Get the decompress info pointer
342 jpeg_decompress_struct* dinfo = fDecodeMgr->dinfo();
343
344 // Set a new jump location for libjpeg errors
345 skjpeg_error_mgr* errorMgr = (skjpeg_error_mgr*) dinfo->err;
346 if (setjmp(errorMgr->fJmpBuf)) {
347 return return_failure(*dinfo, "setjmp", kIncompleteInput);
348 }
349
350 // Check if we can decode to the requested destination
351 if (!conversion_possible(dstInfo, this->getInfo())) {
352 return return_failure(*dinfo, "conversion_possible", kInvalidConversion) ;
353 }
354 // Check if we can scale to the requested dimensions
355 // libjpeg can scale to 1/1, 1/2, 1/4, and 1/8
356 SkASSERT(1 == dinfo->scale_num);
357 SkASSERT(1 == dinfo->scale_denom);
358 jpeg_calc_output_dimensions(dinfo);
359 const uint32_t dstWidth = dstInfo.width();
360 const uint32_t dstHeight = dstInfo.height();
361 while (dinfo->output_width != dstWidth ||
362 dinfo->output_height != dstHeight) {
363
364 // Return a failure if we have tried all of the possible scales
365 if (8 == dinfo->scale_denom || dstWidth > dinfo->output_width
366 || dstHeight > dinfo->output_height) {
367 return return_failure(*dinfo, "cannot scale to requested dimensions" , kInvalidScale);
368 }
369
370 // Try the next scale
371 dinfo->scale_denom *= 2;
372 jpeg_calc_output_dimensions(dinfo);
373 }
374
375 // Now, given valid output dimensions, we can start the decompress
376 if (!jpeg_start_decompress(dinfo)) {
377 return return_failure(*dinfo, "start_decompress", kInvalidInput);
378 }
379
380 // Create the swizzler
381 const SkSwizzler::SrcConfig srcConfig = get_src_config(dinfo);
382 SkAutoTDelete<SkSwizzler> swizzler(SkSwizzler::CreateSwizzler(srcConfig, NUL L, dstInfo,
383 dst, dstRowBytes, options.fZeroInitialized));
384 if (NULL == swizzler) {
385 return return_failure(*dinfo, "CreateSwizzler", kInvalidInput);
386 }
387 const uint32_t srcBytesPerPixel = SkSwizzler::BytesPerPixel(srcConfig);
388
389 // This is usually 1, but can also be 2 or 4.
390 // If we wanted to always read one row at a time, we could, but we will save space and time
391 // by using the recommendation from libjpeg.
392 const uint32_t rowsPerDecode = dinfo->rec_outbuf_height;
393 SkASSERT(rowsPerDecode <= 4);
394
395 // Create a buffer to contain decoded rows (libjpeg requires a 2D array)
396 const uint32_t srcRowBytes = srcBytesPerPixel * dstWidth;
397 SkAutoTDeleteArray<uint8_t> srcBuffer(SkNEW_ARRAY(uint8_t, srcRowBytes * row sPerDecode));
398 JSAMPLE* srcRows[4];
399 uint8_t* srcPtr = srcBuffer.get();
400 for (uint8_t i = 0; i < rowsPerDecode; i++) {
401 srcRows[i] = (JSAMPLE*) srcPtr;
402 srcPtr += srcRowBytes;
403 }
404
405 // Ensure that we loop enough times to decode all of the rows
406 // libjpeg will prevent us from reading past the bottom of the image
407 for (uint32_t y = 0; y < dstHeight + rowsPerDecode - 1; y += rowsPerDecode) {
408 // Read rows of the image
409 uint32_t rowsDecoded = jpeg_read_scanlines(dinfo, srcRows, rowsPerDecode );
410
411 // Convert to RGB if necessary
412 if (JCS_CMYK == dinfo->out_color_space) {
413 convert_CMYK_to_RGB(srcRows[0], dstWidth * rowsDecoded);
414 }
415
416 // Swizzle to output destination
417 for (uint32_t i = 0; i < rowsDecoded; i++) {
418 swizzler->next(srcRows[i]);
419 }
420
421 // If we cannot read enough rows, assume the input is incomplete
422 if (rowsDecoded < rowsPerDecode && y + rowsDecoded < dstHeight) {
423 // Fill the remainder of the image with black.
424 // This error handling behavior is unspecified but SkCodec consisten tly
425 // uses black as the fill color for opaque images.
426 SkSwizzler::Fill(swizzler->getDstRow(), dstInfo, dstRowBytes,
427 dstHeight - y - rowsDecoded, SK_ColorBLACK, NULL);
428
429 // Suppress incomplete input message from libjpeg
430 // We will report our own warnings with SkCodecPrintf
431 dinfo->output_scanline = dstHeight;
432
433 // Finish the decode and indicate that the input was incomplete
434 jpeg_finish_decompress(dinfo);
435 return return_failure(*dinfo, "Incomplete image data", kIncompleteIn put);
436 }
437 }
438 jpeg_finish_decompress(dinfo);
439
440 return kSuccess;
441 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698