blob: b08835b542303a41932ccb28cd434144c8cf8080 [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
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000498/**
499 * Get the config and bytes per pixel of the source data. Return
500 * whether the data is supported.
501 */
502static bool get_src_config(const jpeg_decompress_struct& cinfo,
503 SkScaledBitmapSampler::SrcConfig* sc,
504 int* srcBytesPerPixel) {
505 SkASSERT(sc != NULL && srcBytesPerPixel != NULL);
506 if (JCS_CMYK == cinfo.out_color_space) {
507 // In this case we will manually convert the CMYK values to RGB
508 *sc = SkScaledBitmapSampler::kRGBX;
509 // The CMYK work-around relies on 4 components per pixel here
510 *srcBytesPerPixel = 4;
511 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
512 *sc = SkScaledBitmapSampler::kRGB;
513 *srcBytesPerPixel = 3;
514#ifdef ANDROID_RGB
515 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
516 *sc = SkScaledBitmapSampler::kRGBX;
517 *srcBytesPerPixel = 4;
518 } else if (JCS_RGB_565 == cinfo.out_color_space) {
519 *sc = SkScaledBitmapSampler::kRGB_565;
520 *srcBytesPerPixel = 2;
521#endif
522 } else if (1 == cinfo.out_color_components &&
523 JCS_GRAYSCALE == cinfo.out_color_space) {
524 *sc = SkScaledBitmapSampler::kGray;
525 *srcBytesPerPixel = 1;
526 } else {
527 return false;
528 }
529 return true;
530}
halcanary@google.comfed30372013-10-04 12:46:45 +0000531
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000532bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
533#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000534 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000535#endif
536
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000537 JPEGAutoClean autoClean;
538
539 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000540 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000541
scroggo@google.com590a5af2013-08-07 21:09:13 +0000542 skjpeg_error_mgr errorManager;
543 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000544
545 // All objects need to be instantiated before this setjmp call so that
546 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000547 if (setjmp(errorManager.fJmpBuf)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000548 return return_false(cinfo, *bm, "setjmp");
549 }
550
scroggo@google.com590a5af2013-08-07 21:09:13 +0000551 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000552 autoClean.set(&cinfo);
553
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000554 int status = jpeg_read_header(&cinfo, true);
555 if (status != JPEG_HEADER_OK) {
556 return return_false(cinfo, *bm, "read_header");
557 }
558
559 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
560 can) much faster that we, just use their num/denom api to approximate
561 the size.
562 */
563 int sampleSize = this->getSampleSize();
564
scroggo@google.com590a5af2013-08-07 21:09:13 +0000565 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000566
scroggo@google.com590a5af2013-08-07 21:09:13 +0000567 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000568 cinfo.scale_denom = sampleSize;
569
scroggo@google.com590a5af2013-08-07 21:09:13 +0000570 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000571
scroggo@google.com590a5af2013-08-07 21:09:13 +0000572 const SkBitmap::Config config = this->getBitmapConfig(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000573
574#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000575 adjust_out_color_space_and_dither(&cinfo, config, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000576#endif
577
djsollen@google.com11399402013-03-20 17:45:27 +0000578 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000579 // Assume an A8 bitmap is not opaque to avoid the check of each
580 // individual pixel. It is very unlikely to be opaque, since
581 // an opaque A8 bitmap would not be very interesting.
582 // Otherwise, a jpeg image is opaque.
reed@google.com383a6972013-10-21 14:00:07 +0000583 return bm->setConfig(config, cinfo.image_width, cinfo.image_height, 0,
584 SkBitmap::kA8_Config == config ?
585 kPremul_SkAlphaType : kOpaque_SkAlphaType);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000586 }
587
588 /* image_width and image_height are the original dimensions, available
589 after jpeg_read_header(). To see the scaled dimensions, we have to call
590 jpeg_start_decompress(), and then read output_width and output_height.
591 */
592 if (!jpeg_start_decompress(&cinfo)) {
593 /* If we failed here, we may still have enough information to return
594 to the caller if they just wanted (subsampled bounds). If sampleSize
595 was 1, then we would have already returned. Thus we just check if
596 we're in kDecodeBounds_Mode, and that we have valid output sizes.
597
598 One reason to fail here is that we have insufficient stream data
599 to complete the setup. However, output dimensions seem to get
600 computed very early, which is why this special check can pay off.
601 */
djsollen@google.com11399402013-03-20 17:45:27 +0000602 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000603 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
604 recompute_sampleSize(sampleSize, cinfo));
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000605 // Assume an A8 bitmap is not opaque to avoid the check of each
606 // individual pixel. It is very unlikely to be opaque, since
607 // an opaque A8 bitmap would not be very interesting.
608 // Otherwise, a jpeg image is opaque.
reed@google.com383a6972013-10-21 14:00:07 +0000609 return bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight(),
610 0, SkBitmap::kA8_Config == config ?
611 kPremul_SkAlphaType : kOpaque_SkAlphaType);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000612 } else {
613 return return_false(cinfo, *bm, "start_decompress");
614 }
615 }
616 sampleSize = recompute_sampleSize(sampleSize, cinfo);
617
618 // should we allow the Chooser (if present) to pick a config for us???
djsollen@google.com11399402013-03-20 17:45:27 +0000619 if (!this->chooseFromOneChoice(config, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000620 return return_false(cinfo, *bm, "chooseFromOneChoice");
621 }
622
djsollen@google.com11399402013-03-20 17:45:27 +0000623 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000624 // Assume an A8 bitmap is not opaque to avoid the check of each
625 // individual pixel. It is very unlikely to be opaque, since
626 // an opaque A8 bitmap would not be very interesting.
627 // Otherwise, a jpeg image is opaque.
reed@google.com383a6972013-10-21 14:00:07 +0000628 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0,
629 SkBitmap::kA8_Config != config ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000630 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
631 return true;
632 }
633 if (!this->allocPixelRef(bm, NULL)) {
634 return return_false(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000635 }
636
637 SkAutoLockPixels alp(*bm);
638
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000639#ifdef ANDROID_RGB
640 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
641 a significant performance boost.
642 */
643 if (sampleSize == 1 &&
rmistry@google.comd6176b02012-08-23 18:14:13 +0000644 ((config == SkBitmap::kARGB_8888_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000645 cinfo.out_color_space == JCS_RGBA_8888) ||
rmistry@google.comd6176b02012-08-23 18:14:13 +0000646 (config == SkBitmap::kRGB_565_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000647 cinfo.out_color_space == JCS_RGB_565)))
648 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000649 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000650 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000651
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000652 while (cinfo.output_scanline < cinfo.output_height) {
653 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000654 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000655 // if row_count == 0, then we didn't get a scanline,
656 // so return early. We will return a partial image.
657 fill_below_level(cinfo.output_scanline, bm);
658 cinfo.output_scanline = cinfo.output_height;
659 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000660 }
661 if (this->shouldCancelDecode()) {
662 return return_false(cinfo, *bm, "shouldCancelDecode");
663 }
664 rowptr += bpr;
665 }
666 jpeg_finish_decompress(&cinfo);
667 return true;
668 }
669#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000670
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000671 // check for supported formats
672 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000673 int srcBytesPerPixel;
674
675 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000676 return return_false(cinfo, *bm, "jpeg colorspace");
677 }
678
scroggo@google.com8d239242013-10-01 17:27:15 +0000679 if (!sampler.begin(bm, sc, *this)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000680 return return_false(cinfo, *bm, "sampler.begin");
681 }
682
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000683 SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000684 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000685
686 // Possibly skip initial rows [sampler.srcY0]
687 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
688 return return_false(cinfo, *bm, "skip rows");
689 }
690
691 // now loop through scanlines until y == bm->height() - 1
692 for (int y = 0;; y++) {
693 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
694 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
695 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000696 // if row_count == 0, then we didn't get a scanline,
697 // so return early. We will return a partial image.
698 fill_below_level(y, bm);
699 cinfo.output_scanline = cinfo.output_height;
700 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000701 }
702 if (this->shouldCancelDecode()) {
703 return return_false(cinfo, *bm, "shouldCancelDecode");
704 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000705
706 if (JCS_CMYK == cinfo.out_color_space) {
707 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
708 }
709
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000710 sampler.next(srcRow);
711 if (bm->height() - 1 == y) {
712 // we're done
713 break;
714 }
715
716 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
717 return return_false(cinfo, *bm, "skip rows");
718 }
719 }
720
721 // we formally skip the rest, so we don't get a complaint from libjpeg
722 if (!skip_src_rows(&cinfo, srcRow,
723 cinfo.output_height - cinfo.output_scanline)) {
724 return return_false(cinfo, *bm, "skip rows");
725 }
726 jpeg_finish_decompress(&cinfo);
727
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000728 return true;
729}
730
scroggo@google.comd79277f2013-08-07 19:53:53 +0000731#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000732bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000733
scroggo@google.coma1a51542013-08-07 21:02:32 +0000734 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000735 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000736
737 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000738 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000739
740 // All objects need to be instantiated before this setjmp call so that
741 // they will be cleaned up properly if an error occurs.
742 if (setjmp(sk_err.fJmpBuf)) {
743 return false;
744 }
745
746 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000747 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000748 return false;
749 }
750
scroggo@google.coma1a51542013-08-07 21:02:32 +0000751 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000752 return false;
753 }
754
755 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000756 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000757
758 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000759 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000760 return false;
761 }
762
scroggo@google.com590a5af2013-08-07 21:09:13 +0000763 // FIXME: This sets cinfo->out_color_space, which we may change later
764 // based on the config in onDecodeSubset. This should be fine, since
765 // jpeg_init_read_tile_scanline will check out_color_space again after
766 // that change (when it calls jinit_color_deconverter).
767 (void) this->getBitmapConfig(cinfo);
768
769 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000770
771 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000772 if (!imageIndex->startTileDecompress()) {
773 return false;
774 }
djsollen@google.com11399402013-03-20 17:45:27 +0000775
scroggo@google.coma1a51542013-08-07 21:02:32 +0000776 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000777 fImageWidth = cinfo->output_width;
778 fImageHeight = cinfo->output_height;
779
780 if (width) {
781 *width = fImageWidth;
782 }
783 if (height) {
784 *height = fImageHeight;
785 }
djsollen@google.com11399402013-03-20 17:45:27 +0000786
787 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000788 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +0000789
790 return true;
791}
792
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000793bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000794 if (NULL == fImageIndex) {
795 return false;
796 }
797 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
798
799 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
800 if (!rect.intersect(region)) {
801 // If the requested region is entirely outside the image return false
802 return false;
803 }
804
805
806 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000807 set_error_mgr(cinfo, &errorManager);
808
djsollen@google.com11399402013-03-20 17:45:27 +0000809 if (setjmp(errorManager.fJmpBuf)) {
810 return false;
811 }
812
813 int requestedSampleSize = this->getSampleSize();
814 cinfo->scale_denom = requestedSampleSize;
815
scroggo@google.com590a5af2013-08-07 21:09:13 +0000816 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000817
scroggo@google.com590a5af2013-08-07 21:09:13 +0000818 const SkBitmap::Config config = this->getBitmapConfig(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000819#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000820 adjust_out_color_space_and_dither(cinfo, config, *this);
djsollen@google.com11399402013-03-20 17:45:27 +0000821#endif
822
823 int startX = rect.fLeft;
824 int startY = rect.fTop;
825 int width = rect.width();
826 int height = rect.height();
827
828 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
829 &startX, &startY, &width, &height);
830 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
831 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
832
833 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
834
835 SkBitmap bitmap;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000836 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
837 // Assume an A8 bitmap is not opaque to avoid the check of each
838 // individual pixel. It is very unlikely to be opaque, since
839 // an opaque A8 bitmap would not be very interesting.
840 // Otherwise, a jpeg image is opaque.
reed@google.com383a6972013-10-21 14:00:07 +0000841 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0,
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000842 config == SkBitmap::kA8_Config ? kPremul_SkAlphaType :
reed@google.com383a6972013-10-21 14:00:07 +0000843 kOpaque_SkAlphaType);
djsollen@google.com11399402013-03-20 17:45:27 +0000844
845 // Check ahead of time if the swap(dest, src) is possible or not.
846 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
847 // swap happening. If no, then we will use alloc to allocate pixels to
848 // prevent garbage collection.
849 int w = rect.width() / actualSampleSize;
850 int h = rect.height() / actualSampleSize;
851 bool swapOnly = (rect == region) && bm->isNull() &&
852 (w == bitmap.width()) && (h == bitmap.height()) &&
853 ((startX - rect.x()) / actualSampleSize == 0) &&
854 ((startY - rect.y()) / actualSampleSize == 0);
855 if (swapOnly) {
856 if (!this->allocPixelRef(&bitmap, NULL)) {
857 return return_false(*cinfo, bitmap, "allocPixelRef");
858 }
859 } else {
860 if (!bitmap.allocPixels()) {
861 return return_false(*cinfo, bitmap, "allocPixels");
862 }
863 }
864
865 SkAutoLockPixels alp(bitmap);
866
867#ifdef ANDROID_RGB
868 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
869 a significant performance boost.
870 */
871 if (skiaSampleSize == 1 &&
872 ((config == SkBitmap::kARGB_8888_Config &&
873 cinfo->out_color_space == JCS_RGBA_8888) ||
874 (config == SkBitmap::kRGB_565_Config &&
875 cinfo->out_color_space == JCS_RGB_565)))
876 {
877 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
878 INT32 const bpr = bitmap.rowBytes();
879 int rowTotalCount = 0;
880
881 while (rowTotalCount < height) {
882 int rowCount = jpeg_read_tile_scanline(cinfo,
883 fImageIndex->huffmanIndex(),
884 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +0000885 // if rowCount == 0, then we didn't get a scanline, so abort.
886 // onDecodeSubset() relies on onBuildTileIndex(), which
887 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +0000888 if (0 == rowCount) {
889 return return_false(*cinfo, bitmap, "read_scanlines");
890 }
891 if (this->shouldCancelDecode()) {
892 return return_false(*cinfo, bitmap, "shouldCancelDecode");
893 }
894 rowTotalCount += rowCount;
895 rowptr += bpr;
896 }
897
898 if (swapOnly) {
899 bm->swap(bitmap);
900 } else {
901 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
902 region.width(), region.height(), startX, startY);
903 }
904 return true;
905 }
906#endif
907
908 // check for supported formats
909 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000910 int srcBytesPerPixel;
911
912 if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
djsollen@google.com11399402013-03-20 17:45:27 +0000913 return return_false(*cinfo, *bm, "jpeg colorspace");
914 }
915
scroggo@google.com8d239242013-10-01 17:27:15 +0000916 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +0000917 return return_false(*cinfo, bitmap, "sampler.begin");
918 }
919
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000920 SkAutoMalloc srcStorage(width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000921 uint8_t* srcRow = (uint8_t*)srcStorage.get();
922
923 // Possibly skip initial rows [sampler.srcY0]
924 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
925 return return_false(*cinfo, bitmap, "skip rows");
926 }
927
928 // now loop through scanlines until y == bitmap->height() - 1
929 for (int y = 0;; y++) {
930 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
931 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +0000932 // if row_count == 0, then we didn't get a scanline, so abort.
933 // onDecodeSubset() relies on onBuildTileIndex(), which
934 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +0000935 if (0 == row_count) {
936 return return_false(*cinfo, bitmap, "read_scanlines");
937 }
938 if (this->shouldCancelDecode()) {
939 return return_false(*cinfo, bitmap, "shouldCancelDecode");
940 }
941
942 if (JCS_CMYK == cinfo->out_color_space) {
943 convert_CMYK_to_RGB(srcRow, width);
944 }
945
946 sampler.next(srcRow);
947 if (bitmap.height() - 1 == y) {
948 // we're done
949 break;
950 }
951
952 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
953 sampler.srcDY() - 1)) {
954 return return_false(*cinfo, bitmap, "skip rows");
955 }
956 }
957 if (swapOnly) {
958 bm->swap(bitmap);
959 } else {
960 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
961 region.width(), region.height(), startX, startY);
962 }
963 return true;
964}
965#endif
966
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000967///////////////////////////////////////////////////////////////////////////////
968
969#include "SkColorPriv.h"
970
971// taken from jcolor.c in libjpeg
972#if 0 // 16bit - precise but slow
973 #define CYR 19595 // 0.299
974 #define CYG 38470 // 0.587
975 #define CYB 7471 // 0.114
976
977 #define CUR -11059 // -0.16874
978 #define CUG -21709 // -0.33126
979 #define CUB 32768 // 0.5
980
981 #define CVR 32768 // 0.5
982 #define CVG -27439 // -0.41869
983 #define CVB -5329 // -0.08131
984
985 #define CSHIFT 16
986#else // 8bit - fast, slightly less precise
987 #define CYR 77 // 0.299
988 #define CYG 150 // 0.587
989 #define CYB 29 // 0.114
990
991 #define CUR -43 // -0.16874
992 #define CUG -85 // -0.33126
993 #define CUB 128 // 0.5
994
995 #define CVR 128 // 0.5
996 #define CVG -107 // -0.41869
997 #define CVB -21 // -0.08131
998
999 #define CSHIFT 8
1000#endif
1001
1002static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
1003 int r = SkGetPackedR32(c);
1004 int g = SkGetPackedG32(c);
1005 int b = SkGetPackedB32(c);
1006
1007 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
1008 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
1009 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
1010
1011 dst[0] = SkToU8(y);
1012 dst[1] = SkToU8(u + 128);
1013 dst[2] = SkToU8(v + 128);
1014}
1015
1016static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
1017 int r = SkGetPackedR4444(c);
1018 int g = SkGetPackedG4444(c);
1019 int b = SkGetPackedB4444(c);
1020
1021 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
1022 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
1023 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
1024
1025 dst[0] = SkToU8(y);
1026 dst[1] = SkToU8(u + 128);
1027 dst[2] = SkToU8(v + 128);
1028}
1029
1030static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
1031 int r = SkGetPackedR16(c);
1032 int g = SkGetPackedG16(c);
1033 int b = SkGetPackedB16(c);
1034
1035 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
1036 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
1037 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
1038
1039 dst[0] = SkToU8(y);
1040 dst[1] = SkToU8(u + 128);
1041 dst[2] = SkToU8(v + 128);
1042}
1043
1044///////////////////////////////////////////////////////////////////////////////
1045
1046typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1047 const void* SK_RESTRICT src, int width,
1048 const SkPMColor* SK_RESTRICT ctable);
1049
1050static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1051 const void* SK_RESTRICT srcRow, int width,
1052 const SkPMColor*) {
1053 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1054 while (--width >= 0) {
1055#ifdef WE_CONVERT_TO_YUV
1056 rgb2yuv_32(dst, *src++);
1057#else
1058 uint32_t c = *src++;
1059 dst[0] = SkGetPackedR32(c);
1060 dst[1] = SkGetPackedG32(c);
1061 dst[2] = SkGetPackedB32(c);
1062#endif
1063 dst += 3;
1064 }
1065}
1066
1067static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1068 const void* SK_RESTRICT srcRow, int width,
1069 const SkPMColor*) {
1070 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1071 while (--width >= 0) {
1072#ifdef WE_CONVERT_TO_YUV
1073 rgb2yuv_4444(dst, *src++);
1074#else
1075 SkPMColor16 c = *src++;
1076 dst[0] = SkPacked4444ToR32(c);
1077 dst[1] = SkPacked4444ToG32(c);
1078 dst[2] = SkPacked4444ToB32(c);
1079#endif
1080 dst += 3;
1081 }
1082}
1083
1084static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1085 const void* SK_RESTRICT srcRow, int width,
1086 const SkPMColor*) {
1087 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1088 while (--width >= 0) {
1089#ifdef WE_CONVERT_TO_YUV
1090 rgb2yuv_16(dst, *src++);
1091#else
1092 uint16_t c = *src++;
1093 dst[0] = SkPacked16ToR32(c);
1094 dst[1] = SkPacked16ToG32(c);
1095 dst[2] = SkPacked16ToB32(c);
1096#endif
1097 dst += 3;
1098 }
1099}
1100
1101static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1102 const void* SK_RESTRICT srcRow, int width,
1103 const SkPMColor* SK_RESTRICT ctable) {
1104 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1105 while (--width >= 0) {
1106#ifdef WE_CONVERT_TO_YUV
1107 rgb2yuv_32(dst, ctable[*src++]);
1108#else
1109 uint32_t c = ctable[*src++];
1110 dst[0] = SkGetPackedR32(c);
1111 dst[1] = SkGetPackedG32(c);
1112 dst[2] = SkGetPackedB32(c);
1113#endif
1114 dst += 3;
1115 }
1116}
1117
1118static WriteScanline ChooseWriter(const SkBitmap& bm) {
1119 switch (bm.config()) {
1120 case SkBitmap::kARGB_8888_Config:
1121 return Write_32_YUV;
1122 case SkBitmap::kRGB_565_Config:
1123 return Write_16_YUV;
1124 case SkBitmap::kARGB_4444_Config:
1125 return Write_4444_YUV;
1126 case SkBitmap::kIndex8_Config:
1127 return Write_Index_YUV;
1128 default:
1129 return NULL;
1130 }
1131}
1132
1133class SkJPEGImageEncoder : public SkImageEncoder {
1134protected:
1135 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1136#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001137 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001138#endif
1139
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001140 SkAutoLockPixels alp(bm);
1141 if (NULL == bm.getPixels()) {
1142 return false;
1143 }
1144
1145 jpeg_compress_struct cinfo;
1146 skjpeg_error_mgr sk_err;
1147 skjpeg_destination_mgr sk_wstream(stream);
1148
1149 // allocate these before set call setjmp
1150 SkAutoMalloc oneRow;
1151 SkAutoLockColors ctLocker;
1152
1153 cinfo.err = jpeg_std_error(&sk_err);
1154 sk_err.error_exit = skjpeg_error_exit;
1155 if (setjmp(sk_err.fJmpBuf)) {
1156 return false;
1157 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001158
mtklein@google.com8d725b22013-07-24 16:20:05 +00001159 // Keep after setjmp or mark volatile.
1160 const WriteScanline writer = ChooseWriter(bm);
1161 if (NULL == writer) {
1162 return false;
1163 }
1164
1165 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001166 cinfo.dest = &sk_wstream;
1167 cinfo.image_width = bm.width();
1168 cinfo.image_height = bm.height();
1169 cinfo.input_components = 3;
1170#ifdef WE_CONVERT_TO_YUV
1171 cinfo.in_color_space = JCS_YCbCr;
1172#else
1173 cinfo.in_color_space = JCS_RGB;
1174#endif
1175 cinfo.input_gamma = 1;
1176
1177 jpeg_set_defaults(&cinfo);
1178 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001179#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001180 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001181#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001182
1183 jpeg_start_compress(&cinfo, TRUE);
1184
1185 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001186 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001187
1188 const SkPMColor* colors = ctLocker.lockColors(bm);
1189 const void* srcRow = bm.getPixels();
1190
1191 while (cinfo.next_scanline < cinfo.image_height) {
1192 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1193
1194 writer(oneRowP, srcRow, width, colors);
1195 row_pointer[0] = oneRowP;
1196 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1197 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1198 }
1199
1200 jpeg_finish_compress(&cinfo);
1201 jpeg_destroy_compress(&cinfo);
1202
1203 return true;
1204 }
1205};
1206
1207///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001208DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1209DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1210///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001211
scroggo@google.comb5571b32013-09-25 21:34:24 +00001212static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001213 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001214 static const size_t HEADER_SIZE = sizeof(gHeader);
1215
1216 char buffer[HEADER_SIZE];
1217 size_t len = stream->read(buffer, HEADER_SIZE);
1218
1219 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001220 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001221 }
1222 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001223 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001224 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001225 return true;
1226}
1227
scroggo@google.comb5571b32013-09-25 21:34:24 +00001228
1229static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001230 if (is_jpeg(stream)) {
1231 return SkNEW(SkJPEGImageDecoder);
1232 }
1233 return NULL;
1234}
1235
scroggo@google.comb5571b32013-09-25 21:34:24 +00001236static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001237 if (is_jpeg(stream)) {
1238 return SkImageDecoder::kJPEG_Format;
1239 }
1240 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001241}
1242
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001243static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001244 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1245}
1246
mtklein@google.combd6343b2013-09-04 17:20:18 +00001247static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1248static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1249static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);