blob: befe6dc580d949adad230f99e07599531229a5e8 [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 /**
reed6c225732014-06-09 19:52:07 -0700251 * Determine the appropriate bitmap colortype and out_color_space based on
scroggo@google.com590a5af2013-08-07 21:09:13 +0000252 * 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 */
reed6c225732014-06-09 19:52:07 -0700256 SkColorType getBitmapColorType(jpeg_decompress_struct*);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000257
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
reed6c225732014-06-09 19:52:07 -0700403SkColorType SkJPEGImageDecoder::getBitmapColorType(jpeg_decompress_struct* cinfo) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000404 SkASSERT(cinfo != NULL);
405
406 SrcDepth srcDepth = k32Bit_SrcDepth;
407 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
408 srcDepth = k8BitGray_SrcDepth;
409 }
410
reed6c225732014-06-09 19:52:07 -0700411 SkColorType colorType = this->getPrefColorType(srcDepth, /*hasAlpha*/ false);
412 switch (colorType) {
413 case kAlpha_8_SkColorType:
414 // Only respect A8 colortype if the original is grayscale,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000415 // in which case we will treat the grayscale as alpha
416 // values.
417 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
reed6c225732014-06-09 19:52:07 -0700418 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000419 }
420 break;
reed6c225732014-06-09 19:52:07 -0700421 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000422 // Fall through.
reed6c225732014-06-09 19:52:07 -0700423 case kARGB_4444_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000424 // Fall through.
reed6c225732014-06-09 19:52:07 -0700425 case kRGB_565_SkColorType:
426 // These are acceptable destination colortypes.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000427 break;
428 default:
reed6c225732014-06-09 19:52:07 -0700429 // Force all other colortypes to 8888.
430 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000431 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:
reed6c225732014-06-09 19:52:07 -0700444 if (kAlpha_8_SkColorType == colorType) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000445 cinfo->out_color_space = JCS_GRAYSCALE;
446 break;
447 }
448 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
reed6c225732014-06-09 19:52:07 -0700449 // colortype. Fall through to set to the default.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000450 default:
451 cinfo->out_color_space = JCS_RGB;
452 break;
453 }
reed6c225732014-06-09 19:52:07 -0700454 return colorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000455}
456
scroggo@google.com590a5af2013-08-07 21:09:13 +0000457/**
reed6c225732014-06-09 19:52:07 -0700458 * Based on the colortype and dither mode, adjust out_color_space and
459 * dither_mode of cinfo. Only does work in ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000460 */
461static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
reed6c225732014-06-09 19:52:07 -0700462 SkColorType colorType,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000463 const SkImageDecoder& decoder) {
464 SkASSERT(cinfo != NULL);
reed6c225732014-06-09 19:52:07 -0700465#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000466 cinfo->dither_mode = JDITHER_NONE;
467 if (JCS_CMYK == cinfo->out_color_space) {
468 return;
469 }
reed6c225732014-06-09 19:52:07 -0700470 switch (colorType) {
471 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000472 cinfo->out_color_space = JCS_RGBA_8888;
473 break;
reed6c225732014-06-09 19:52:07 -0700474 case kRGB_565_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000475 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 }
scroggo@google.com590a5af2013-08-07 21:09:13 +0000483#endif
reed6c225732014-06-09 19:52:07 -0700484}
scroggo@google.com590a5af2013-08-07 21:09:13 +0000485
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
reed6c225732014-06-09 19:52:07 -0700572 const SkColorType colorType = this->getBitmapColorType(&cinfo);
573 const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ?
574 kPremul_SkAlphaType : kOpaque_SkAlphaType;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000575
reed6c225732014-06-09 19:52:07 -0700576 adjust_out_color_space_and_dither(&cinfo, colorType, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000577
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.
reed6c225732014-06-09 19:52:07 -0700583 return bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
584 colorType, alphaType));
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000585 }
586
587 /* image_width and image_height are the original dimensions, available
588 after jpeg_read_header(). To see the scaled dimensions, we have to call
589 jpeg_start_decompress(), and then read output_width and output_height.
590 */
591 if (!jpeg_start_decompress(&cinfo)) {
592 /* If we failed here, we may still have enough information to return
593 to the caller if they just wanted (subsampled bounds). If sampleSize
594 was 1, then we would have already returned. Thus we just check if
595 we're in kDecodeBounds_Mode, and that we have valid output sizes.
596
597 One reason to fail here is that we have insufficient stream data
598 to complete the setup. However, output dimensions seem to get
599 computed very early, which is why this special check can pay off.
600 */
djsollen@google.com11399402013-03-20 17:45:27 +0000601 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000602 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
603 recompute_sampleSize(sampleSize, cinfo));
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000604 // Assume an A8 bitmap is not opaque to avoid the check of each
605 // individual pixel. It is very unlikely to be opaque, since
606 // an opaque A8 bitmap would not be very interesting.
607 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700608 return bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
609 colorType, alphaType));
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000610 } else {
611 return return_false(cinfo, *bm, "start_decompress");
612 }
613 }
614 sampleSize = recompute_sampleSize(sampleSize, cinfo);
615
reed6c225732014-06-09 19:52:07 -0700616 // should we allow the Chooser (if present) to pick a colortype for us???
617 if (!this->chooseFromOneChoice(colorType, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000618 return return_false(cinfo, *bm, "chooseFromOneChoice");
619 }
620
djsollen@google.com11399402013-03-20 17:45:27 +0000621 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000622 // Assume an A8 bitmap is not opaque to avoid the check of each
623 // individual pixel. It is very unlikely to be opaque, since
624 // an opaque A8 bitmap would not be very interesting.
625 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700626 bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
627 colorType, alphaType));
scroggo@google.combc69ce92013-07-09 15:45:14 +0000628 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
629 return true;
630 }
631 if (!this->allocPixelRef(bm, NULL)) {
632 return return_false(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000633 }
634
635 SkAutoLockPixels alp(*bm);
636
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000637#ifdef ANDROID_RGB
638 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
639 a significant performance boost.
640 */
641 if (sampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700642 ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||
643 (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000644 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000645 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000646 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000647
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000648 while (cinfo.output_scanline < cinfo.output_height) {
649 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000650 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000651 // if row_count == 0, then we didn't get a scanline,
652 // so return early. We will return a partial image.
653 fill_below_level(cinfo.output_scanline, bm);
654 cinfo.output_scanline = cinfo.output_height;
655 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000656 }
657 if (this->shouldCancelDecode()) {
658 return return_false(cinfo, *bm, "shouldCancelDecode");
659 }
660 rowptr += bpr;
661 }
662 jpeg_finish_decompress(&cinfo);
663 return true;
664 }
665#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000666
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000667 // check for supported formats
668 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000669 int srcBytesPerPixel;
670
671 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000672 return return_false(cinfo, *bm, "jpeg colorspace");
673 }
674
scroggo@google.com8d239242013-10-01 17:27:15 +0000675 if (!sampler.begin(bm, sc, *this)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000676 return return_false(cinfo, *bm, "sampler.begin");
677 }
678
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000679 SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000680 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000681
682 // Possibly skip initial rows [sampler.srcY0]
683 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
684 return return_false(cinfo, *bm, "skip rows");
685 }
686
687 // now loop through scanlines until y == bm->height() - 1
688 for (int y = 0;; y++) {
689 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
690 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
691 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000692 // if row_count == 0, then we didn't get a scanline,
693 // so return early. We will return a partial image.
694 fill_below_level(y, bm);
695 cinfo.output_scanline = cinfo.output_height;
696 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000697 }
698 if (this->shouldCancelDecode()) {
699 return return_false(cinfo, *bm, "shouldCancelDecode");
700 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000701
702 if (JCS_CMYK == cinfo.out_color_space) {
703 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
704 }
705
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000706 sampler.next(srcRow);
707 if (bm->height() - 1 == y) {
708 // we're done
709 break;
710 }
711
712 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
713 return return_false(cinfo, *bm, "skip rows");
714 }
715 }
716
717 // we formally skip the rest, so we don't get a complaint from libjpeg
718 if (!skip_src_rows(&cinfo, srcRow,
719 cinfo.output_height - cinfo.output_scanline)) {
720 return return_false(cinfo, *bm, "skip rows");
721 }
722 jpeg_finish_decompress(&cinfo);
723
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000724 return true;
725}
726
scroggo@google.comd79277f2013-08-07 19:53:53 +0000727#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000728bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000729
scroggo@google.coma1a51542013-08-07 21:02:32 +0000730 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000731 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000732
733 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000734 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000735
736 // All objects need to be instantiated before this setjmp call so that
737 // they will be cleaned up properly if an error occurs.
738 if (setjmp(sk_err.fJmpBuf)) {
739 return false;
740 }
741
742 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000743 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000744 return false;
745 }
746
scroggo@google.coma1a51542013-08-07 21:02:32 +0000747 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000748 return false;
749 }
750
751 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000752 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000753
754 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000755 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000756 return false;
757 }
758
scroggo@google.com590a5af2013-08-07 21:09:13 +0000759 // FIXME: This sets cinfo->out_color_space, which we may change later
760 // based on the config in onDecodeSubset. This should be fine, since
761 // jpeg_init_read_tile_scanline will check out_color_space again after
762 // that change (when it calls jinit_color_deconverter).
reed6c225732014-06-09 19:52:07 -0700763 (void) this->getBitmapColorType(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000764
765 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000766
767 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000768 if (!imageIndex->startTileDecompress()) {
769 return false;
770 }
djsollen@google.com11399402013-03-20 17:45:27 +0000771
scroggo@google.coma1a51542013-08-07 21:02:32 +0000772 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000773 fImageWidth = cinfo->output_width;
774 fImageHeight = cinfo->output_height;
775
776 if (width) {
777 *width = fImageWidth;
778 }
779 if (height) {
780 *height = fImageHeight;
781 }
djsollen@google.com11399402013-03-20 17:45:27 +0000782
783 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000784 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +0000785
786 return true;
787}
788
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000789bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000790 if (NULL == fImageIndex) {
791 return false;
792 }
793 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
794
795 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
796 if (!rect.intersect(region)) {
797 // If the requested region is entirely outside the image return false
798 return false;
799 }
800
801
802 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000803 set_error_mgr(cinfo, &errorManager);
804
djsollen@google.com11399402013-03-20 17:45:27 +0000805 if (setjmp(errorManager.fJmpBuf)) {
806 return false;
807 }
808
809 int requestedSampleSize = this->getSampleSize();
810 cinfo->scale_denom = requestedSampleSize;
811
scroggo@google.com590a5af2013-08-07 21:09:13 +0000812 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000813
reed6c225732014-06-09 19:52:07 -0700814 const SkColorType colorType = this->getBitmapColorType(cinfo);
815 adjust_out_color_space_and_dither(cinfo, colorType, *this);
djsollen@google.com11399402013-03-20 17:45:27 +0000816
817 int startX = rect.fLeft;
818 int startY = rect.fTop;
819 int width = rect.width();
820 int height = rect.height();
821
822 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
823 &startX, &startY, &width, &height);
824 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
825 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
826
827 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
828
829 SkBitmap bitmap;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000830 // Assume an A8 bitmap is not opaque to avoid the check of each
831 // individual pixel. It is very unlikely to be opaque, since
832 // an opaque A8 bitmap would not be very interesting.
833 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700834 bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType,
835 kAlpha_8_SkColorType == colorType ?
836 kPremul_SkAlphaType : kOpaque_SkAlphaType));
djsollen@google.com11399402013-03-20 17:45:27 +0000837
838 // Check ahead of time if the swap(dest, src) is possible or not.
839 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
840 // swap happening. If no, then we will use alloc to allocate pixels to
841 // prevent garbage collection.
842 int w = rect.width() / actualSampleSize;
843 int h = rect.height() / actualSampleSize;
844 bool swapOnly = (rect == region) && bm->isNull() &&
845 (w == bitmap.width()) && (h == bitmap.height()) &&
846 ((startX - rect.x()) / actualSampleSize == 0) &&
847 ((startY - rect.y()) / actualSampleSize == 0);
848 if (swapOnly) {
849 if (!this->allocPixelRef(&bitmap, NULL)) {
850 return return_false(*cinfo, bitmap, "allocPixelRef");
851 }
852 } else {
853 if (!bitmap.allocPixels()) {
854 return return_false(*cinfo, bitmap, "allocPixels");
855 }
856 }
857
858 SkAutoLockPixels alp(bitmap);
859
860#ifdef ANDROID_RGB
861 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
862 a significant performance boost.
863 */
864 if (skiaSampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700865 ((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||
866 (kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565)))
djsollen@google.com11399402013-03-20 17:45:27 +0000867 {
868 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
869 INT32 const bpr = bitmap.rowBytes();
870 int rowTotalCount = 0;
871
872 while (rowTotalCount < height) {
873 int rowCount = jpeg_read_tile_scanline(cinfo,
874 fImageIndex->huffmanIndex(),
875 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +0000876 // if rowCount == 0, then we didn't get a scanline, so abort.
877 // onDecodeSubset() relies on onBuildTileIndex(), which
878 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +0000879 if (0 == rowCount) {
880 return return_false(*cinfo, bitmap, "read_scanlines");
881 }
882 if (this->shouldCancelDecode()) {
883 return return_false(*cinfo, bitmap, "shouldCancelDecode");
884 }
885 rowTotalCount += rowCount;
886 rowptr += bpr;
887 }
888
889 if (swapOnly) {
890 bm->swap(bitmap);
891 } else {
892 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
893 region.width(), region.height(), startX, startY);
894 }
895 return true;
896 }
897#endif
898
899 // check for supported formats
900 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000901 int srcBytesPerPixel;
902
903 if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
djsollen@google.com11399402013-03-20 17:45:27 +0000904 return return_false(*cinfo, *bm, "jpeg colorspace");
905 }
906
scroggo@google.com8d239242013-10-01 17:27:15 +0000907 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +0000908 return return_false(*cinfo, bitmap, "sampler.begin");
909 }
910
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000911 SkAutoMalloc srcStorage(width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000912 uint8_t* srcRow = (uint8_t*)srcStorage.get();
913
914 // Possibly skip initial rows [sampler.srcY0]
915 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
916 return return_false(*cinfo, bitmap, "skip rows");
917 }
918
919 // now loop through scanlines until y == bitmap->height() - 1
920 for (int y = 0;; y++) {
921 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
922 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +0000923 // if row_count == 0, then we didn't get a scanline, so abort.
924 // onDecodeSubset() relies on onBuildTileIndex(), which
925 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +0000926 if (0 == row_count) {
927 return return_false(*cinfo, bitmap, "read_scanlines");
928 }
929 if (this->shouldCancelDecode()) {
930 return return_false(*cinfo, bitmap, "shouldCancelDecode");
931 }
932
933 if (JCS_CMYK == cinfo->out_color_space) {
934 convert_CMYK_to_RGB(srcRow, width);
935 }
936
937 sampler.next(srcRow);
938 if (bitmap.height() - 1 == y) {
939 // we're done
940 break;
941 }
942
943 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
944 sampler.srcDY() - 1)) {
945 return return_false(*cinfo, bitmap, "skip rows");
946 }
947 }
948 if (swapOnly) {
949 bm->swap(bitmap);
950 } else {
951 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
952 region.width(), region.height(), startX, startY);
953 }
954 return true;
955}
956#endif
957
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000958///////////////////////////////////////////////////////////////////////////////
959
960#include "SkColorPriv.h"
961
962// taken from jcolor.c in libjpeg
963#if 0 // 16bit - precise but slow
964 #define CYR 19595 // 0.299
965 #define CYG 38470 // 0.587
966 #define CYB 7471 // 0.114
967
968 #define CUR -11059 // -0.16874
969 #define CUG -21709 // -0.33126
970 #define CUB 32768 // 0.5
971
972 #define CVR 32768 // 0.5
973 #define CVG -27439 // -0.41869
974 #define CVB -5329 // -0.08131
975
976 #define CSHIFT 16
977#else // 8bit - fast, slightly less precise
978 #define CYR 77 // 0.299
979 #define CYG 150 // 0.587
980 #define CYB 29 // 0.114
981
982 #define CUR -43 // -0.16874
983 #define CUG -85 // -0.33126
984 #define CUB 128 // 0.5
985
986 #define CVR 128 // 0.5
987 #define CVG -107 // -0.41869
988 #define CVB -21 // -0.08131
989
990 #define CSHIFT 8
991#endif
992
993static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
994 int r = SkGetPackedR32(c);
995 int g = SkGetPackedG32(c);
996 int b = SkGetPackedB32(c);
997
998 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
999 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
1000 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
1001
1002 dst[0] = SkToU8(y);
1003 dst[1] = SkToU8(u + 128);
1004 dst[2] = SkToU8(v + 128);
1005}
1006
1007static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
1008 int r = SkGetPackedR4444(c);
1009 int g = SkGetPackedG4444(c);
1010 int b = SkGetPackedB4444(c);
1011
1012 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
1013 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
1014 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
1015
1016 dst[0] = SkToU8(y);
1017 dst[1] = SkToU8(u + 128);
1018 dst[2] = SkToU8(v + 128);
1019}
1020
1021static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
1022 int r = SkGetPackedR16(c);
1023 int g = SkGetPackedG16(c);
1024 int b = SkGetPackedB16(c);
1025
1026 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
1027 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
1028 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
1029
1030 dst[0] = SkToU8(y);
1031 dst[1] = SkToU8(u + 128);
1032 dst[2] = SkToU8(v + 128);
1033}
1034
1035///////////////////////////////////////////////////////////////////////////////
1036
1037typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1038 const void* SK_RESTRICT src, int width,
1039 const SkPMColor* SK_RESTRICT ctable);
1040
1041static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1042 const void* SK_RESTRICT srcRow, int width,
1043 const SkPMColor*) {
1044 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1045 while (--width >= 0) {
1046#ifdef WE_CONVERT_TO_YUV
1047 rgb2yuv_32(dst, *src++);
1048#else
1049 uint32_t c = *src++;
1050 dst[0] = SkGetPackedR32(c);
1051 dst[1] = SkGetPackedG32(c);
1052 dst[2] = SkGetPackedB32(c);
1053#endif
1054 dst += 3;
1055 }
1056}
1057
1058static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1059 const void* SK_RESTRICT srcRow, int width,
1060 const SkPMColor*) {
1061 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1062 while (--width >= 0) {
1063#ifdef WE_CONVERT_TO_YUV
1064 rgb2yuv_4444(dst, *src++);
1065#else
1066 SkPMColor16 c = *src++;
1067 dst[0] = SkPacked4444ToR32(c);
1068 dst[1] = SkPacked4444ToG32(c);
1069 dst[2] = SkPacked4444ToB32(c);
1070#endif
1071 dst += 3;
1072 }
1073}
1074
1075static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1076 const void* SK_RESTRICT srcRow, int width,
1077 const SkPMColor*) {
1078 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1079 while (--width >= 0) {
1080#ifdef WE_CONVERT_TO_YUV
1081 rgb2yuv_16(dst, *src++);
1082#else
1083 uint16_t c = *src++;
1084 dst[0] = SkPacked16ToR32(c);
1085 dst[1] = SkPacked16ToG32(c);
1086 dst[2] = SkPacked16ToB32(c);
1087#endif
1088 dst += 3;
1089 }
1090}
1091
1092static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1093 const void* SK_RESTRICT srcRow, int width,
1094 const SkPMColor* SK_RESTRICT ctable) {
1095 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1096 while (--width >= 0) {
1097#ifdef WE_CONVERT_TO_YUV
1098 rgb2yuv_32(dst, ctable[*src++]);
1099#else
1100 uint32_t c = ctable[*src++];
1101 dst[0] = SkGetPackedR32(c);
1102 dst[1] = SkGetPackedG32(c);
1103 dst[2] = SkGetPackedB32(c);
1104#endif
1105 dst += 3;
1106 }
1107}
1108
1109static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -07001110 switch (bm.colorType()) {
1111 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001112 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -07001113 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001114 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -07001115 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001116 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -07001117 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001118 return Write_Index_YUV;
1119 default:
1120 return NULL;
1121 }
1122}
1123
1124class SkJPEGImageEncoder : public SkImageEncoder {
1125protected:
1126 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1127#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001128 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001129#endif
1130
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001131 SkAutoLockPixels alp(bm);
1132 if (NULL == bm.getPixels()) {
1133 return false;
1134 }
1135
1136 jpeg_compress_struct cinfo;
1137 skjpeg_error_mgr sk_err;
1138 skjpeg_destination_mgr sk_wstream(stream);
1139
1140 // allocate these before set call setjmp
1141 SkAutoMalloc oneRow;
1142 SkAutoLockColors ctLocker;
1143
1144 cinfo.err = jpeg_std_error(&sk_err);
1145 sk_err.error_exit = skjpeg_error_exit;
1146 if (setjmp(sk_err.fJmpBuf)) {
1147 return false;
1148 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001149
mtklein@google.com8d725b22013-07-24 16:20:05 +00001150 // Keep after setjmp or mark volatile.
1151 const WriteScanline writer = ChooseWriter(bm);
1152 if (NULL == writer) {
1153 return false;
1154 }
1155
1156 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001157 cinfo.dest = &sk_wstream;
1158 cinfo.image_width = bm.width();
1159 cinfo.image_height = bm.height();
1160 cinfo.input_components = 3;
1161#ifdef WE_CONVERT_TO_YUV
1162 cinfo.in_color_space = JCS_YCbCr;
1163#else
1164 cinfo.in_color_space = JCS_RGB;
1165#endif
1166 cinfo.input_gamma = 1;
1167
1168 jpeg_set_defaults(&cinfo);
1169 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001170#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001171 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001172#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001173
1174 jpeg_start_compress(&cinfo, TRUE);
1175
1176 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001177 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001178
1179 const SkPMColor* colors = ctLocker.lockColors(bm);
1180 const void* srcRow = bm.getPixels();
1181
1182 while (cinfo.next_scanline < cinfo.image_height) {
1183 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1184
1185 writer(oneRowP, srcRow, width, colors);
1186 row_pointer[0] = oneRowP;
1187 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1188 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1189 }
1190
1191 jpeg_finish_compress(&cinfo);
1192 jpeg_destroy_compress(&cinfo);
1193
1194 return true;
1195 }
1196};
1197
1198///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001199DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1200DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1201///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001202
scroggo@google.comb5571b32013-09-25 21:34:24 +00001203static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001204 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001205 static const size_t HEADER_SIZE = sizeof(gHeader);
1206
1207 char buffer[HEADER_SIZE];
1208 size_t len = stream->read(buffer, HEADER_SIZE);
1209
1210 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001211 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001212 }
1213 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001214 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001215 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001216 return true;
1217}
1218
scroggo@google.comb5571b32013-09-25 21:34:24 +00001219
1220static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001221 if (is_jpeg(stream)) {
1222 return SkNEW(SkJPEGImageDecoder);
1223 }
1224 return NULL;
1225}
1226
scroggo@google.comb5571b32013-09-25 21:34:24 +00001227static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001228 if (is_jpeg(stream)) {
1229 return SkImageDecoder::kJPEG_Format;
1230 }
1231 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001232}
1233
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001234static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001235 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1236}
1237
mtklein@google.combd6343b2013-09-04 17:20:18 +00001238static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1239static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1240static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);