blob: 67b17823e262bba903fbd55a4e437efbce5dfd8d [file] [log] [blame]
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001/*
2 * Copyright 2007 The Android Open Source Project
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
9#include "SkImageDecoder.h"
10#include "SkImageEncoder.h"
11#include "SkJpegUtility.h"
12#include "SkColorPriv.h"
13#include "SkDither.h"
14#include "SkScaledBitmapSampler.h"
15#include "SkStream.h"
16#include "SkTemplates.h"
djsollen@google.com11399402013-03-20 17:45:27 +000017#include "SkTime.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000018#include "SkUtils.h"
halcanary@google.com2a103182013-10-14 12:49:15 +000019#include "SkRTConf.h"
djsollen@google.com11399402013-03-20 17:45:27 +000020#include "SkRect.h"
21#include "SkCanvas.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000022
halcanary@google.comfed30372013-10-04 12:46:45 +000023
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000024#include <stdio.h>
25extern "C" {
26 #include "jpeglib.h"
27 #include "jerror.h"
28}
29
djsollen@google.com11399402013-03-20 17:45:27 +000030// These enable timing code that report milliseconds for an encoding/decoding
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000031//#define TIME_ENCODE
32//#define TIME_DECODE
33
34// this enables our rgb->yuv code, which is faster than libjpeg on ARM
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000035#define WE_CONVERT_TO_YUV
36
djsollen@google.com11399402013-03-20 17:45:27 +000037// If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers
38// support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565.
39
halcanary@google.comfed30372013-10-04 12:46:45 +000040#if defined(SK_DEBUG)
halcanary@google.com2a103182013-10-14 12:49:15 +000041#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS false
halcanary@google.com04b57f82013-10-14 20:08:48 +000042#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS false
halcanary@google.com2a103182013-10-14 12:49:15 +000043#else // !defined(SK_DEBUG)
44#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS true
halcanary@google.com04b57f82013-10-14 20:08:48 +000045#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS true
halcanary@google.comfed30372013-10-04 12:46:45 +000046#endif // defined(SK_DEBUG)
halcanary@google.com2a103182013-10-14 12:49:15 +000047SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderWarnings,
48 "images.jpeg.suppressDecoderWarnings",
49 DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS,
50 "Suppress most JPG warnings when calling decode functions.");
halcanary@google.com04b57f82013-10-14 20:08:48 +000051SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderErrors,
52 "images.jpeg.suppressDecoderErrors",
53 DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS,
54 "Suppress most JPG error messages when decode "
55 "function fails.");
halcanary@google.comfed30372013-10-04 12:46:45 +000056
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000057//////////////////////////////////////////////////////////////////////////
58//////////////////////////////////////////////////////////////////////////
59
scroggo@google.com590a5af2013-08-07 21:09:13 +000060static void overwrite_mem_buffer_size(jpeg_decompress_struct* cinfo) {
djsollen@google.com11399402013-03-20 17:45:27 +000061#ifdef SK_BUILD_FOR_ANDROID
62 /* Check if the device indicates that it has a large amount of system memory
63 * if so, increase the memory allocation to 30MB instead of the default 5MB.
64 */
65#ifdef ANDROID_LARGE_MEMORY_DEVICE
66 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
67#else
68 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
69#endif
70#endif // SK_BUILD_FOR_ANDROID
71}
72
73//////////////////////////////////////////////////////////////////////////
74//////////////////////////////////////////////////////////////////////////
75
halcanary@google.comfed30372013-10-04 12:46:45 +000076static void do_nothing_emit_message(jpeg_common_struct*, int) {
77 /* do nothing */
78}
halcanary@google.com04b57f82013-10-14 20:08:48 +000079static void do_nothing_output_message(j_common_ptr) {
80 /* do nothing */
81}
halcanary@google.comfed30372013-10-04 12:46:45 +000082
scroggo@google.com590a5af2013-08-07 21:09:13 +000083static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* src_mgr) {
84 SkASSERT(cinfo != NULL);
85 SkASSERT(src_mgr != NULL);
86 jpeg_create_decompress(cinfo);
87 overwrite_mem_buffer_size(cinfo);
88 cinfo->src = src_mgr;
halcanary@google.comfed30372013-10-04 12:46:45 +000089 /* To suppress warnings with a SK_DEBUG binary, set the
90 * environment variable "skia_images_jpeg_suppressDecoderWarnings"
91 * to "true". Inside a program that links to skia:
92 * SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); */
93 if (c_suppressJPEGImageDecoderWarnings) {
94 cinfo->err->emit_message = &do_nothing_emit_message;
95 }
halcanary@google.com04b57f82013-10-14 20:08:48 +000096 /* To suppress error messages with a SK_DEBUG binary, set the
97 * environment variable "skia_images_jpeg_suppressDecoderErrors"
98 * to "true". Inside a program that links to skia:
99 * SK_CONF_SET("images.jpeg.suppressDecoderErrors", true); */
100 if (c_suppressJPEGImageDecoderErrors) {
101 cinfo->err->output_message = &do_nothing_output_message;
102 }
scroggo@google.com590a5af2013-08-07 21:09:13 +0000103}
104
scroggo@google.coma1a51542013-08-07 21:02:32 +0000105#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000106class SkJPEGImageIndex {
107public:
scroggo@google.comb5571b32013-09-25 21:34:24 +0000108 SkJPEGImageIndex(SkStreamRewindable* stream, SkImageDecoder* decoder)
scroggo@google.coma1a51542013-08-07 21:02:32 +0000109 : fSrcMgr(stream, decoder)
110 , fInfoInitialized(false)
111 , fHuffmanCreated(false)
112 , fDecompressStarted(false)
113 {
114 SkDEBUGCODE(fReadHeaderSucceeded = false;)
115 }
djsollen@google.com11399402013-03-20 17:45:27 +0000116
117 ~SkJPEGImageIndex() {
scroggo@google.coma1a51542013-08-07 21:02:32 +0000118 if (fHuffmanCreated) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000119 // Set to false before calling the libjpeg function, in case
120 // the libjpeg function calls longjmp. Our setjmp handler may
121 // attempt to delete this SkJPEGImageIndex, thus entering this
122 // destructor again. Setting fHuffmanCreated to false first
123 // prevents an infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000124 fHuffmanCreated = false;
125 jpeg_destroy_huffman_index(&fHuffmanIndex);
126 }
127 if (fDecompressStarted) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000128 // Like fHuffmanCreated, set to false before calling libjpeg
129 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000130 fDecompressStarted = false;
131 jpeg_finish_decompress(&fCInfo);
132 }
133 if (fInfoInitialized) {
134 this->destroyInfo();
135 }
djsollen@google.com11399402013-03-20 17:45:27 +0000136 }
137
138 /**
scroggo@google.coma1a51542013-08-07 21:02:32 +0000139 * Destroy the cinfo struct.
140 * After this call, if a huffman index was already built, it
141 * can be used after calling initializeInfoAndReadHeader
142 * again. Must not be called after startTileDecompress except
143 * in the destructor.
djsollen@google.com11399402013-03-20 17:45:27 +0000144 */
scroggo@google.coma1a51542013-08-07 21:02:32 +0000145 void destroyInfo() {
146 SkASSERT(fInfoInitialized);
147 SkASSERT(!fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000148 // Like fHuffmanCreated, set to false before calling libjpeg
149 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000150 fInfoInitialized = false;
151 jpeg_destroy_decompress(&fCInfo);
152 SkDEBUGCODE(fReadHeaderSucceeded = false;)
153 }
154
155 /**
156 * Initialize the cinfo struct.
157 * Calls jpeg_create_decompress, makes customizations, and
158 * finally calls jpeg_read_header. Returns true if jpeg_read_header
159 * returns JPEG_HEADER_OK.
160 * If cinfo was already initialized, destroyInfo must be called to
161 * destroy the old one. Must not be called after startTileDecompress.
162 */
163 bool initializeInfoAndReadHeader() {
164 SkASSERT(!fInfoInitialized && !fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000165 initialize_info(&fCInfo, &fSrcMgr);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000166 fInfoInitialized = true;
167 const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true));
168 SkDEBUGCODE(fReadHeaderSucceeded = success;)
169 return success;
djsollen@google.com11399402013-03-20 17:45:27 +0000170 }
171
172 jpeg_decompress_struct* cinfo() { return &fCInfo; }
173
djsollen@google.com11399402013-03-20 17:45:27 +0000174 huffman_index* huffmanIndex() { return &fHuffmanIndex; }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000175
176 /**
177 * Build the index to be used for tile based decoding.
178 * Must only be called after a successful call to
179 * initializeInfoAndReadHeader and must not be called more
180 * than once.
181 */
182 bool buildHuffmanIndex() {
183 SkASSERT(fReadHeaderSucceeded);
184 SkASSERT(!fHuffmanCreated);
185 jpeg_create_huffman_index(&fCInfo, &fHuffmanIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000186 SkASSERT(1 == fCInfo.scale_num && 1 == fCInfo.scale_denom);
scroggo@google.com57a52982013-08-27 20:42:22 +0000187 fHuffmanCreated = jpeg_build_huffman_index(&fCInfo, &fHuffmanIndex);
188 return fHuffmanCreated;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000189 }
190
191 /**
192 * Start tile based decoding. Must only be called after a
193 * successful call to buildHuffmanIndex, and must only be
194 * called once.
195 */
196 bool startTileDecompress() {
197 SkASSERT(fHuffmanCreated);
198 SkASSERT(fReadHeaderSucceeded);
199 SkASSERT(!fDecompressStarted);
200 if (jpeg_start_tile_decompress(&fCInfo)) {
201 fDecompressStarted = true;
202 return true;
203 }
204 return false;
205 }
djsollen@google.com11399402013-03-20 17:45:27 +0000206
207private:
208 skjpeg_source_mgr fSrcMgr;
209 jpeg_decompress_struct fCInfo;
djsollen@google.com11399402013-03-20 17:45:27 +0000210 huffman_index fHuffmanIndex;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000211 bool fInfoInitialized;
212 bool fHuffmanCreated;
213 bool fDecompressStarted;
214 SkDEBUGCODE(bool fReadHeaderSucceeded;)
djsollen@google.com11399402013-03-20 17:45:27 +0000215};
scroggo@google.coma1a51542013-08-07 21:02:32 +0000216#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000217
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000218class SkJPEGImageDecoder : public SkImageDecoder {
219public:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000220#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000221 SkJPEGImageDecoder() {
222 fImageIndex = NULL;
223 fImageWidth = 0;
224 fImageHeight = 0;
225 }
226
227 virtual ~SkJPEGImageDecoder() {
228 SkDELETE(fImageIndex);
229 }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000230#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000231
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000232 virtual Format getFormat() const {
233 return kJPEG_Format;
234 }
235
236protected:
scroggo@google.comd79277f2013-08-07 19:53:53 +0000237#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000238 virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000239 virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
djsollen@google.com11399402013-03-20 17:45:27 +0000240#endif
241 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
242
243private:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000244#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000245 SkJPEGImageIndex* fImageIndex;
246 int fImageWidth;
247 int fImageHeight;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000248#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000249
scroggo@google.com590a5af2013-08-07 21:09:13 +0000250 /**
251 * Determine the appropriate bitmap config and out_color_space based on
252 * both the preference of the caller and the jpeg_color_space on the
253 * jpeg_decompress_struct passed in.
254 * Must be called after jpeg_read_header.
255 */
256 SkBitmap::Config getBitmapConfig(jpeg_decompress_struct*);
257
djsollen@google.com11399402013-03-20 17:45:27 +0000258 typedef SkImageDecoder INHERITED;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000259};
260
261//////////////////////////////////////////////////////////////////////////
262
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000263/* Automatically clean up after throwing an exception */
264class JPEGAutoClean {
265public:
266 JPEGAutoClean(): cinfo_ptr(NULL) {}
267 ~JPEGAutoClean() {
268 if (cinfo_ptr) {
269 jpeg_destroy_decompress(cinfo_ptr);
270 }
271 }
272 void set(jpeg_decompress_struct* info) {
273 cinfo_ptr = info;
274 }
275private:
276 jpeg_decompress_struct* cinfo_ptr;
277};
278
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000279///////////////////////////////////////////////////////////////////////////////
280
281/* If we need to better match the request, we might examine the image and
282 output dimensions, and determine if the downsampling jpeg provided is
283 not sufficient. If so, we can recompute a modified sampleSize value to
284 make up the difference.
285
286 To skip this additional scaling, just set sampleSize = 1; below.
287 */
288static int recompute_sampleSize(int sampleSize,
289 const jpeg_decompress_struct& cinfo) {
290 return sampleSize * cinfo.output_width / cinfo.image_width;
291}
292
293static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
294 /* These are initialized to 0, so if they have non-zero values, we assume
295 they are "valid" (i.e. have been computed by libjpeg)
296 */
djsollen@google.com11399402013-03-20 17:45:27 +0000297 return 0 != cinfo.output_width && 0 != cinfo.output_height;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000298}
299
djsollen@google.com11399402013-03-20 17:45:27 +0000300static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000301 for (int i = 0; i < count; i++) {
302 JSAMPLE* rowptr = (JSAMPLE*)buffer;
303 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
djsollen@google.com11399402013-03-20 17:45:27 +0000304 if (1 != row_count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000305 return false;
306 }
307 }
308 return true;
309}
310
scroggo@google.comd79277f2013-08-07 19:53:53 +0000311#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000312static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
313 huffman_index *index, void* buffer, int count) {
314 for (int i = 0; i < count; i++) {
315 JSAMPLE* rowptr = (JSAMPLE*)buffer;
316 int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
317 if (1 != row_count) {
318 return false;
319 }
320 }
321 return true;
322}
323#endif
324
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000325// This guy exists just to aid in debugging, as it allows debuggers to just
326// set a break-point in one place to see all error exists.
327static bool return_false(const jpeg_decompress_struct& cinfo,
scroggo@google.com228f2b82013-09-25 21:27:31 +0000328 const SkBitmap& bm, const char caller[]) {
halcanary@google.com04b57f82013-10-14 20:08:48 +0000329 if (!(c_suppressJPEGImageDecoderErrors)) {
330 char buffer[JMSG_LENGTH_MAX];
331 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
332 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
333 cinfo.err->msg_code, buffer, caller, bm.width(), bm.height());
334 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000335 return false; // must always return false
336}
337
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000338// Convert a scanline of CMYK samples to RGBX in place. Note that this
339// method moves the "scanline" pointer in its processing
340static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
341 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000342 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000343 // from easyrgb.com):
344 // CMYK -> CMY
345 // C = ( C * (1 - K) + K ) // for each CMY component
346 // CMY -> RGB
347 // R = ( 1 - C ) * 255 // for each RGB component
348 // Unfortunately we are seeing inverted CMYK so all the original terms
349 // are 1-. This yields:
350 // CMYK -> CMY
351 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
352 // The conversion from CMY->RGB remains the same
353 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
354 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
355 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
356 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
357 scanline[3] = 255;
358 }
359}
360
scroggo@google.com590a5af2013-08-07 21:09:13 +0000361/**
362 * Common code for setting the error manager.
363 */
364static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
365 SkASSERT(cinfo != NULL);
366 SkASSERT(errorManager != NULL);
367 cinfo->err = jpeg_std_error(errorManager);
368 errorManager->error_exit = skjpeg_error_exit;
369}
370
371/**
372 * Common code for turning off upsampling and smoothing. Turning these
373 * off helps performance without showing noticable differences in the
374 * resulting bitmap.
375 */
376static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
377 SkASSERT(cinfo != NULL);
378 /* this gives about 30% performance improvement. In theory it may
379 reduce the visual quality, in practice I'm not seeing a difference
380 */
381 cinfo->do_fancy_upsampling = 0;
382
383 /* this gives another few percents */
384 cinfo->do_block_smoothing = 0;
385}
386
387/**
388 * Common code for setting the dct method.
389 */
390static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
391 SkASSERT(cinfo != NULL);
392#ifdef DCT_IFAST_SUPPORTED
393 if (decoder.getPreferQualityOverSpeed()) {
394 cinfo->dct_method = JDCT_ISLOW;
395 } else {
396 cinfo->dct_method = JDCT_IFAST;
397 }
398#else
399 cinfo->dct_method = JDCT_ISLOW;
400#endif
401}
402
403SkBitmap::Config SkJPEGImageDecoder::getBitmapConfig(jpeg_decompress_struct* cinfo) {
404 SkASSERT(cinfo != NULL);
405
406 SrcDepth srcDepth = k32Bit_SrcDepth;
407 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
408 srcDepth = k8BitGray_SrcDepth;
409 }
410
411 SkBitmap::Config config = this->getPrefConfig(srcDepth, /*hasAlpha*/ false);
412 switch (config) {
413 case SkBitmap::kA8_Config:
414 // Only respect A8 config if the original is grayscale,
415 // in which case we will treat the grayscale as alpha
416 // values.
417 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
418 config = SkBitmap::kARGB_8888_Config;
419 }
420 break;
421 case SkBitmap::kARGB_8888_Config:
422 // Fall through.
423 case SkBitmap::kARGB_4444_Config:
424 // Fall through.
425 case SkBitmap::kRGB_565_Config:
426 // These are acceptable destination configs.
427 break;
428 default:
429 // Force all other configs to 8888.
430 config = SkBitmap::kARGB_8888_Config;
431 break;
432 }
433
434 switch (cinfo->jpeg_color_space) {
435 case JCS_CMYK:
436 // Fall through.
437 case JCS_YCCK:
438 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
439 // so libjpeg will give us CMYK samples back and we will later
440 // manually convert them to RGB
441 cinfo->out_color_space = JCS_CMYK;
442 break;
443 case JCS_GRAYSCALE:
444 if (SkBitmap::kA8_Config == config) {
445 cinfo->out_color_space = JCS_GRAYSCALE;
446 break;
447 }
448 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
449 // config. Fall through to set to the default.
450 default:
451 cinfo->out_color_space = JCS_RGB;
452 break;
453 }
454 return config;
455}
456
457#ifdef ANDROID_RGB
458/**
459 * Based on the config and dither mode, adjust out_color_space and
460 * dither_mode of cinfo.
461 */
462static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
463 SkBitmap::Config config,
464 const SkImageDecoder& decoder) {
465 SkASSERT(cinfo != NULL);
466 cinfo->dither_mode = JDITHER_NONE;
467 if (JCS_CMYK == cinfo->out_color_space) {
468 return;
469 }
470 switch(config) {
471 case SkBitmap::kARGB_8888_Config:
472 cinfo->out_color_space = JCS_RGBA_8888;
473 break;
474 case SkBitmap::kRGB_565_Config:
475 cinfo->out_color_space = JCS_RGB_565;
476 if (decoder.getDitherImage()) {
477 cinfo->dither_mode = JDITHER_ORDERED;
478 }
479 break;
480 default:
481 break;
482 }
483}
484#endif
485
halcanary@google.comfed30372013-10-04 12:46:45 +0000486
487/**
488 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
489 Used when decoding fails partway through reading scanlines to fill
490 remaining lines. */
491static void fill_below_level(int y, SkBitmap* bitmap) {
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000492 SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
halcanary@google.comfed30372013-10-04 12:46:45 +0000493 SkCanvas canvas(*bitmap);
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000494 canvas.clipRect(SkRect::Make(rect));
halcanary@google.comfed30372013-10-04 12:46:45 +0000495 canvas.drawColor(SK_ColorWHITE);
496}
497
498
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000499bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
500#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000501 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000502#endif
503
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000504 JPEGAutoClean autoClean;
505
506 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000507 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000508
scroggo@google.com590a5af2013-08-07 21:09:13 +0000509 skjpeg_error_mgr errorManager;
510 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000511
512 // All objects need to be instantiated before this setjmp call so that
513 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000514 if (setjmp(errorManager.fJmpBuf)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000515 return return_false(cinfo, *bm, "setjmp");
516 }
517
scroggo@google.com590a5af2013-08-07 21:09:13 +0000518 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000519 autoClean.set(&cinfo);
520
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000521 int status = jpeg_read_header(&cinfo, true);
522 if (status != JPEG_HEADER_OK) {
523 return return_false(cinfo, *bm, "read_header");
524 }
525
526 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
527 can) much faster that we, just use their num/denom api to approximate
528 the size.
529 */
530 int sampleSize = this->getSampleSize();
531
scroggo@google.com590a5af2013-08-07 21:09:13 +0000532 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000533
scroggo@google.com590a5af2013-08-07 21:09:13 +0000534 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000535 cinfo.scale_denom = sampleSize;
536
scroggo@google.com590a5af2013-08-07 21:09:13 +0000537 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000538
scroggo@google.com590a5af2013-08-07 21:09:13 +0000539 const SkBitmap::Config config = this->getBitmapConfig(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000540
541#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000542 adjust_out_color_space_and_dither(&cinfo, config, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000543#endif
544
djsollen@google.com11399402013-03-20 17:45:27 +0000545 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
reed@google.com383a6972013-10-21 14:00:07 +0000546 return bm->setConfig(config, cinfo.image_width, cinfo.image_height, 0,
547 SkBitmap::kA8_Config == config ?
548 kPremul_SkAlphaType : kOpaque_SkAlphaType);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000549 }
550
551 /* image_width and image_height are the original dimensions, available
552 after jpeg_read_header(). To see the scaled dimensions, we have to call
553 jpeg_start_decompress(), and then read output_width and output_height.
554 */
555 if (!jpeg_start_decompress(&cinfo)) {
556 /* If we failed here, we may still have enough information to return
557 to the caller if they just wanted (subsampled bounds). If sampleSize
558 was 1, then we would have already returned. Thus we just check if
559 we're in kDecodeBounds_Mode, and that we have valid output sizes.
560
561 One reason to fail here is that we have insufficient stream data
562 to complete the setup. However, output dimensions seem to get
563 computed very early, which is why this special check can pay off.
564 */
djsollen@google.com11399402013-03-20 17:45:27 +0000565 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000566 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
567 recompute_sampleSize(sampleSize, cinfo));
reed@google.com383a6972013-10-21 14:00:07 +0000568 return bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight(),
569 0, SkBitmap::kA8_Config == config ?
570 kPremul_SkAlphaType : kOpaque_SkAlphaType);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000571 } else {
572 return return_false(cinfo, *bm, "start_decompress");
573 }
574 }
575 sampleSize = recompute_sampleSize(sampleSize, cinfo);
576
577 // should we allow the Chooser (if present) to pick a config for us???
djsollen@google.com11399402013-03-20 17:45:27 +0000578 if (!this->chooseFromOneChoice(config, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000579 return return_false(cinfo, *bm, "chooseFromOneChoice");
580 }
581
djsollen@google.com11399402013-03-20 17:45:27 +0000582 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
reed@google.com383a6972013-10-21 14:00:07 +0000583 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0,
584 SkBitmap::kA8_Config != config ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000585 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
586 return true;
587 }
588 if (!this->allocPixelRef(bm, NULL)) {
589 return return_false(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000590 }
591
592 SkAutoLockPixels alp(*bm);
593
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000594#ifdef ANDROID_RGB
595 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
596 a significant performance boost.
597 */
598 if (sampleSize == 1 &&
rmistry@google.comd6176b02012-08-23 18:14:13 +0000599 ((config == SkBitmap::kARGB_8888_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000600 cinfo.out_color_space == JCS_RGBA_8888) ||
rmistry@google.comd6176b02012-08-23 18:14:13 +0000601 (config == SkBitmap::kRGB_565_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000602 cinfo.out_color_space == JCS_RGB_565)))
603 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000604 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000605 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000606
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000607 while (cinfo.output_scanline < cinfo.output_height) {
608 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000609 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000610 // if row_count == 0, then we didn't get a scanline,
611 // so return early. We will return a partial image.
612 fill_below_level(cinfo.output_scanline, bm);
613 cinfo.output_scanline = cinfo.output_height;
614 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000615 }
616 if (this->shouldCancelDecode()) {
617 return return_false(cinfo, *bm, "shouldCancelDecode");
618 }
619 rowptr += bpr;
620 }
621 jpeg_finish_decompress(&cinfo);
622 return true;
623 }
624#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000625
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000626 // check for supported formats
627 SkScaledBitmapSampler::SrcConfig sc;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000628 if (JCS_CMYK == cinfo.out_color_space) {
629 // In this case we will manually convert the CMYK values to RGB
630 sc = SkScaledBitmapSampler::kRGBX;
631 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000632 sc = SkScaledBitmapSampler::kRGB;
633#ifdef ANDROID_RGB
634 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
635 sc = SkScaledBitmapSampler::kRGBX;
636 } else if (JCS_RGB_565 == cinfo.out_color_space) {
637 sc = SkScaledBitmapSampler::kRGB_565;
638#endif
639 } else if (1 == cinfo.out_color_components &&
640 JCS_GRAYSCALE == cinfo.out_color_space) {
641 sc = SkScaledBitmapSampler::kGray;
642 } else {
643 return return_false(cinfo, *bm, "jpeg colorspace");
644 }
645
scroggo@google.com8d239242013-10-01 17:27:15 +0000646 if (!sampler.begin(bm, sc, *this)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000647 return return_false(cinfo, *bm, "sampler.begin");
648 }
649
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000650 // The CMYK work-around relies on 4 components per pixel here
djsollen@google.com11399402013-03-20 17:45:27 +0000651 SkAutoMalloc srcStorage(cinfo.output_width * 4);
652 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000653
654 // Possibly skip initial rows [sampler.srcY0]
655 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
656 return return_false(cinfo, *bm, "skip rows");
657 }
658
659 // now loop through scanlines until y == bm->height() - 1
660 for (int y = 0;; y++) {
661 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
662 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
663 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000664 // if row_count == 0, then we didn't get a scanline,
665 // so return early. We will return a partial image.
666 fill_below_level(y, bm);
667 cinfo.output_scanline = cinfo.output_height;
668 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000669 }
670 if (this->shouldCancelDecode()) {
671 return return_false(cinfo, *bm, "shouldCancelDecode");
672 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000673
674 if (JCS_CMYK == cinfo.out_color_space) {
675 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
676 }
677
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000678 sampler.next(srcRow);
679 if (bm->height() - 1 == y) {
680 // we're done
681 break;
682 }
683
684 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
685 return return_false(cinfo, *bm, "skip rows");
686 }
687 }
688
689 // we formally skip the rest, so we don't get a complaint from libjpeg
690 if (!skip_src_rows(&cinfo, srcRow,
691 cinfo.output_height - cinfo.output_scanline)) {
692 return return_false(cinfo, *bm, "skip rows");
693 }
694 jpeg_finish_decompress(&cinfo);
695
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000696 return true;
697}
698
scroggo@google.comd79277f2013-08-07 19:53:53 +0000699#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000700bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000701
scroggo@google.coma1a51542013-08-07 21:02:32 +0000702 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000703 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000704
705 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000706 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000707
708 // All objects need to be instantiated before this setjmp call so that
709 // they will be cleaned up properly if an error occurs.
710 if (setjmp(sk_err.fJmpBuf)) {
711 return false;
712 }
713
714 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000715 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000716 return false;
717 }
718
scroggo@google.coma1a51542013-08-07 21:02:32 +0000719 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000720 return false;
721 }
722
723 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000724 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000725
726 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000727 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000728 return false;
729 }
730
scroggo@google.com590a5af2013-08-07 21:09:13 +0000731 // FIXME: This sets cinfo->out_color_space, which we may change later
732 // based on the config in onDecodeSubset. This should be fine, since
733 // jpeg_init_read_tile_scanline will check out_color_space again after
734 // that change (when it calls jinit_color_deconverter).
735 (void) this->getBitmapConfig(cinfo);
736
737 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000738
739 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000740 if (!imageIndex->startTileDecompress()) {
741 return false;
742 }
djsollen@google.com11399402013-03-20 17:45:27 +0000743
scroggo@google.coma1a51542013-08-07 21:02:32 +0000744 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000745 fImageWidth = cinfo->output_width;
746 fImageHeight = cinfo->output_height;
747
748 if (width) {
749 *width = fImageWidth;
750 }
751 if (height) {
752 *height = fImageHeight;
753 }
djsollen@google.com11399402013-03-20 17:45:27 +0000754
755 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000756 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +0000757
758 return true;
759}
760
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000761bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000762 if (NULL == fImageIndex) {
763 return false;
764 }
765 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
766
767 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
768 if (!rect.intersect(region)) {
769 // If the requested region is entirely outside the image return false
770 return false;
771 }
772
773
774 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000775 set_error_mgr(cinfo, &errorManager);
776
djsollen@google.com11399402013-03-20 17:45:27 +0000777 if (setjmp(errorManager.fJmpBuf)) {
778 return false;
779 }
780
781 int requestedSampleSize = this->getSampleSize();
782 cinfo->scale_denom = requestedSampleSize;
783
scroggo@google.com590a5af2013-08-07 21:09:13 +0000784 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000785
scroggo@google.com590a5af2013-08-07 21:09:13 +0000786 const SkBitmap::Config config = this->getBitmapConfig(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000787#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000788 adjust_out_color_space_and_dither(cinfo, config, *this);
djsollen@google.com11399402013-03-20 17:45:27 +0000789#endif
790
791 int startX = rect.fLeft;
792 int startY = rect.fTop;
793 int width = rect.width();
794 int height = rect.height();
795
796 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
797 &startX, &startY, &width, &height);
798 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
799 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
800
801 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
802
803 SkBitmap bitmap;
reed@google.com383a6972013-10-21 14:00:07 +0000804 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0,
805 kOpaque_SkAlphaType);
djsollen@google.com11399402013-03-20 17:45:27 +0000806
807 // Check ahead of time if the swap(dest, src) is possible or not.
808 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
809 // swap happening. If no, then we will use alloc to allocate pixels to
810 // prevent garbage collection.
811 int w = rect.width() / actualSampleSize;
812 int h = rect.height() / actualSampleSize;
813 bool swapOnly = (rect == region) && bm->isNull() &&
814 (w == bitmap.width()) && (h == bitmap.height()) &&
815 ((startX - rect.x()) / actualSampleSize == 0) &&
816 ((startY - rect.y()) / actualSampleSize == 0);
817 if (swapOnly) {
818 if (!this->allocPixelRef(&bitmap, NULL)) {
819 return return_false(*cinfo, bitmap, "allocPixelRef");
820 }
821 } else {
822 if (!bitmap.allocPixels()) {
823 return return_false(*cinfo, bitmap, "allocPixels");
824 }
825 }
826
827 SkAutoLockPixels alp(bitmap);
828
829#ifdef ANDROID_RGB
830 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
831 a significant performance boost.
832 */
833 if (skiaSampleSize == 1 &&
834 ((config == SkBitmap::kARGB_8888_Config &&
835 cinfo->out_color_space == JCS_RGBA_8888) ||
836 (config == SkBitmap::kRGB_565_Config &&
837 cinfo->out_color_space == JCS_RGB_565)))
838 {
839 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
840 INT32 const bpr = bitmap.rowBytes();
841 int rowTotalCount = 0;
842
843 while (rowTotalCount < height) {
844 int rowCount = jpeg_read_tile_scanline(cinfo,
845 fImageIndex->huffmanIndex(),
846 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +0000847 // if rowCount == 0, then we didn't get a scanline, so abort.
848 // onDecodeSubset() relies on onBuildTileIndex(), which
849 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +0000850 if (0 == rowCount) {
851 return return_false(*cinfo, bitmap, "read_scanlines");
852 }
853 if (this->shouldCancelDecode()) {
854 return return_false(*cinfo, bitmap, "shouldCancelDecode");
855 }
856 rowTotalCount += rowCount;
857 rowptr += bpr;
858 }
859
860 if (swapOnly) {
861 bm->swap(bitmap);
862 } else {
863 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
864 region.width(), region.height(), startX, startY);
865 }
866 return true;
867 }
868#endif
869
870 // check for supported formats
871 SkScaledBitmapSampler::SrcConfig sc;
872 if (JCS_CMYK == cinfo->out_color_space) {
873 // In this case we will manually convert the CMYK values to RGB
874 sc = SkScaledBitmapSampler::kRGBX;
875 } else if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_space) {
876 sc = SkScaledBitmapSampler::kRGB;
877#ifdef ANDROID_RGB
878 } else if (JCS_RGBA_8888 == cinfo->out_color_space) {
879 sc = SkScaledBitmapSampler::kRGBX;
880 } else if (JCS_RGB_565 == cinfo->out_color_space) {
881 sc = SkScaledBitmapSampler::kRGB_565;
882#endif
883 } else if (1 == cinfo->out_color_components &&
884 JCS_GRAYSCALE == cinfo->out_color_space) {
885 sc = SkScaledBitmapSampler::kGray;
886 } else {
887 return return_false(*cinfo, *bm, "jpeg colorspace");
888 }
889
scroggo@google.com8d239242013-10-01 17:27:15 +0000890 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +0000891 return return_false(*cinfo, bitmap, "sampler.begin");
892 }
893
894 // The CMYK work-around relies on 4 components per pixel here
895 SkAutoMalloc srcStorage(width * 4);
896 uint8_t* srcRow = (uint8_t*)srcStorage.get();
897
898 // Possibly skip initial rows [sampler.srcY0]
899 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
900 return return_false(*cinfo, bitmap, "skip rows");
901 }
902
903 // now loop through scanlines until y == bitmap->height() - 1
904 for (int y = 0;; y++) {
905 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
906 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +0000907 // if row_count == 0, then we didn't get a scanline, so abort.
908 // onDecodeSubset() relies on onBuildTileIndex(), which
909 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +0000910 if (0 == row_count) {
911 return return_false(*cinfo, bitmap, "read_scanlines");
912 }
913 if (this->shouldCancelDecode()) {
914 return return_false(*cinfo, bitmap, "shouldCancelDecode");
915 }
916
917 if (JCS_CMYK == cinfo->out_color_space) {
918 convert_CMYK_to_RGB(srcRow, width);
919 }
920
921 sampler.next(srcRow);
922 if (bitmap.height() - 1 == y) {
923 // we're done
924 break;
925 }
926
927 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
928 sampler.srcDY() - 1)) {
929 return return_false(*cinfo, bitmap, "skip rows");
930 }
931 }
932 if (swapOnly) {
933 bm->swap(bitmap);
934 } else {
935 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
936 region.width(), region.height(), startX, startY);
937 }
938 return true;
939}
940#endif
941
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000942///////////////////////////////////////////////////////////////////////////////
943
944#include "SkColorPriv.h"
945
946// taken from jcolor.c in libjpeg
947#if 0 // 16bit - precise but slow
948 #define CYR 19595 // 0.299
949 #define CYG 38470 // 0.587
950 #define CYB 7471 // 0.114
951
952 #define CUR -11059 // -0.16874
953 #define CUG -21709 // -0.33126
954 #define CUB 32768 // 0.5
955
956 #define CVR 32768 // 0.5
957 #define CVG -27439 // -0.41869
958 #define CVB -5329 // -0.08131
959
960 #define CSHIFT 16
961#else // 8bit - fast, slightly less precise
962 #define CYR 77 // 0.299
963 #define CYG 150 // 0.587
964 #define CYB 29 // 0.114
965
966 #define CUR -43 // -0.16874
967 #define CUG -85 // -0.33126
968 #define CUB 128 // 0.5
969
970 #define CVR 128 // 0.5
971 #define CVG -107 // -0.41869
972 #define CVB -21 // -0.08131
973
974 #define CSHIFT 8
975#endif
976
977static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
978 int r = SkGetPackedR32(c);
979 int g = SkGetPackedG32(c);
980 int b = SkGetPackedB32(c);
981
982 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
983 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
984 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
985
986 dst[0] = SkToU8(y);
987 dst[1] = SkToU8(u + 128);
988 dst[2] = SkToU8(v + 128);
989}
990
991static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
992 int r = SkGetPackedR4444(c);
993 int g = SkGetPackedG4444(c);
994 int b = SkGetPackedB4444(c);
995
996 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
997 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
998 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
999
1000 dst[0] = SkToU8(y);
1001 dst[1] = SkToU8(u + 128);
1002 dst[2] = SkToU8(v + 128);
1003}
1004
1005static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
1006 int r = SkGetPackedR16(c);
1007 int g = SkGetPackedG16(c);
1008 int b = SkGetPackedB16(c);
1009
1010 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
1011 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
1012 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
1013
1014 dst[0] = SkToU8(y);
1015 dst[1] = SkToU8(u + 128);
1016 dst[2] = SkToU8(v + 128);
1017}
1018
1019///////////////////////////////////////////////////////////////////////////////
1020
1021typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1022 const void* SK_RESTRICT src, int width,
1023 const SkPMColor* SK_RESTRICT ctable);
1024
1025static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1026 const void* SK_RESTRICT srcRow, int width,
1027 const SkPMColor*) {
1028 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1029 while (--width >= 0) {
1030#ifdef WE_CONVERT_TO_YUV
1031 rgb2yuv_32(dst, *src++);
1032#else
1033 uint32_t c = *src++;
1034 dst[0] = SkGetPackedR32(c);
1035 dst[1] = SkGetPackedG32(c);
1036 dst[2] = SkGetPackedB32(c);
1037#endif
1038 dst += 3;
1039 }
1040}
1041
1042static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1043 const void* SK_RESTRICT srcRow, int width,
1044 const SkPMColor*) {
1045 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1046 while (--width >= 0) {
1047#ifdef WE_CONVERT_TO_YUV
1048 rgb2yuv_4444(dst, *src++);
1049#else
1050 SkPMColor16 c = *src++;
1051 dst[0] = SkPacked4444ToR32(c);
1052 dst[1] = SkPacked4444ToG32(c);
1053 dst[2] = SkPacked4444ToB32(c);
1054#endif
1055 dst += 3;
1056 }
1057}
1058
1059static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1060 const void* SK_RESTRICT srcRow, int width,
1061 const SkPMColor*) {
1062 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1063 while (--width >= 0) {
1064#ifdef WE_CONVERT_TO_YUV
1065 rgb2yuv_16(dst, *src++);
1066#else
1067 uint16_t c = *src++;
1068 dst[0] = SkPacked16ToR32(c);
1069 dst[1] = SkPacked16ToG32(c);
1070 dst[2] = SkPacked16ToB32(c);
1071#endif
1072 dst += 3;
1073 }
1074}
1075
1076static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1077 const void* SK_RESTRICT srcRow, int width,
1078 const SkPMColor* SK_RESTRICT ctable) {
1079 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1080 while (--width >= 0) {
1081#ifdef WE_CONVERT_TO_YUV
1082 rgb2yuv_32(dst, ctable[*src++]);
1083#else
1084 uint32_t c = ctable[*src++];
1085 dst[0] = SkGetPackedR32(c);
1086 dst[1] = SkGetPackedG32(c);
1087 dst[2] = SkGetPackedB32(c);
1088#endif
1089 dst += 3;
1090 }
1091}
1092
1093static WriteScanline ChooseWriter(const SkBitmap& bm) {
1094 switch (bm.config()) {
1095 case SkBitmap::kARGB_8888_Config:
1096 return Write_32_YUV;
1097 case SkBitmap::kRGB_565_Config:
1098 return Write_16_YUV;
1099 case SkBitmap::kARGB_4444_Config:
1100 return Write_4444_YUV;
1101 case SkBitmap::kIndex8_Config:
1102 return Write_Index_YUV;
1103 default:
1104 return NULL;
1105 }
1106}
1107
1108class SkJPEGImageEncoder : public SkImageEncoder {
1109protected:
1110 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1111#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001112 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001113#endif
1114
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001115 SkAutoLockPixels alp(bm);
1116 if (NULL == bm.getPixels()) {
1117 return false;
1118 }
1119
1120 jpeg_compress_struct cinfo;
1121 skjpeg_error_mgr sk_err;
1122 skjpeg_destination_mgr sk_wstream(stream);
1123
1124 // allocate these before set call setjmp
1125 SkAutoMalloc oneRow;
1126 SkAutoLockColors ctLocker;
1127
1128 cinfo.err = jpeg_std_error(&sk_err);
1129 sk_err.error_exit = skjpeg_error_exit;
1130 if (setjmp(sk_err.fJmpBuf)) {
1131 return false;
1132 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001133
mtklein@google.com8d725b22013-07-24 16:20:05 +00001134 // Keep after setjmp or mark volatile.
1135 const WriteScanline writer = ChooseWriter(bm);
1136 if (NULL == writer) {
1137 return false;
1138 }
1139
1140 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001141 cinfo.dest = &sk_wstream;
1142 cinfo.image_width = bm.width();
1143 cinfo.image_height = bm.height();
1144 cinfo.input_components = 3;
1145#ifdef WE_CONVERT_TO_YUV
1146 cinfo.in_color_space = JCS_YCbCr;
1147#else
1148 cinfo.in_color_space = JCS_RGB;
1149#endif
1150 cinfo.input_gamma = 1;
1151
1152 jpeg_set_defaults(&cinfo);
1153 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001154#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001155 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001156#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001157
1158 jpeg_start_compress(&cinfo, TRUE);
1159
1160 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001161 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001162
1163 const SkPMColor* colors = ctLocker.lockColors(bm);
1164 const void* srcRow = bm.getPixels();
1165
1166 while (cinfo.next_scanline < cinfo.image_height) {
1167 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1168
1169 writer(oneRowP, srcRow, width, colors);
1170 row_pointer[0] = oneRowP;
1171 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1172 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1173 }
1174
1175 jpeg_finish_compress(&cinfo);
1176 jpeg_destroy_compress(&cinfo);
1177
1178 return true;
1179 }
1180};
1181
1182///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001183DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1184DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1185///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001186
scroggo@google.comb5571b32013-09-25 21:34:24 +00001187static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001188 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001189 static const size_t HEADER_SIZE = sizeof(gHeader);
1190
1191 char buffer[HEADER_SIZE];
1192 size_t len = stream->read(buffer, HEADER_SIZE);
1193
1194 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001195 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001196 }
1197 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001198 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001199 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001200 return true;
1201}
1202
scroggo@google.comb5571b32013-09-25 21:34:24 +00001203
1204static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001205 if (is_jpeg(stream)) {
1206 return SkNEW(SkJPEGImageDecoder);
1207 }
1208 return NULL;
1209}
1210
scroggo@google.comb5571b32013-09-25 21:34:24 +00001211static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001212 if (is_jpeg(stream)) {
1213 return SkImageDecoder::kJPEG_Format;
1214 }
1215 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001216}
1217
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001218static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001219 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1220}
1221
mtklein@google.combd6343b2013-09-04 17:20:18 +00001222static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1223static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1224static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);