blob: 99401e6b627cdc9fd023b304a6c40789c3fbec3d [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
reed5926b862014-06-11 10:33:13 -0700616#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed6c225732014-06-09 19:52:07 -0700617 // should we allow the Chooser (if present) to pick a colortype for us???
618 if (!this->chooseFromOneChoice(colorType, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000619 return return_false(cinfo, *bm, "chooseFromOneChoice");
620 }
reed5926b862014-06-11 10:33:13 -0700621#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000622
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.
reed6c225732014-06-09 19:52:07 -0700628 bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
629 colorType, alphaType));
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 &&
reed6c225732014-06-09 19:52:07 -0700644 ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||
645 (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000646 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000647 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000648 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000649
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000650 while (cinfo.output_scanline < cinfo.output_height) {
651 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000652 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000653 // if row_count == 0, then we didn't get a scanline,
654 // so return early. We will return a partial image.
655 fill_below_level(cinfo.output_scanline, bm);
656 cinfo.output_scanline = cinfo.output_height;
657 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000658 }
659 if (this->shouldCancelDecode()) {
660 return return_false(cinfo, *bm, "shouldCancelDecode");
661 }
662 rowptr += bpr;
663 }
664 jpeg_finish_decompress(&cinfo);
665 return true;
666 }
667#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000668
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000669 // check for supported formats
670 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000671 int srcBytesPerPixel;
672
673 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000674 return return_false(cinfo, *bm, "jpeg colorspace");
675 }
676
scroggo@google.com8d239242013-10-01 17:27:15 +0000677 if (!sampler.begin(bm, sc, *this)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000678 return return_false(cinfo, *bm, "sampler.begin");
679 }
680
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000681 SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000682 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000683
684 // Possibly skip initial rows [sampler.srcY0]
685 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
686 return return_false(cinfo, *bm, "skip rows");
687 }
688
689 // now loop through scanlines until y == bm->height() - 1
690 for (int y = 0;; y++) {
691 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
692 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
693 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000694 // if row_count == 0, then we didn't get a scanline,
695 // so return early. We will return a partial image.
696 fill_below_level(y, bm);
697 cinfo.output_scanline = cinfo.output_height;
698 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000699 }
700 if (this->shouldCancelDecode()) {
701 return return_false(cinfo, *bm, "shouldCancelDecode");
702 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000703
704 if (JCS_CMYK == cinfo.out_color_space) {
705 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
706 }
707
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000708 sampler.next(srcRow);
709 if (bm->height() - 1 == y) {
710 // we're done
711 break;
712 }
713
714 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
715 return return_false(cinfo, *bm, "skip rows");
716 }
717 }
718
719 // we formally skip the rest, so we don't get a complaint from libjpeg
720 if (!skip_src_rows(&cinfo, srcRow,
721 cinfo.output_height - cinfo.output_scanline)) {
722 return return_false(cinfo, *bm, "skip rows");
723 }
724 jpeg_finish_decompress(&cinfo);
725
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000726 return true;
727}
728
scroggo@google.comd79277f2013-08-07 19:53:53 +0000729#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000730bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000731
scroggo@google.coma1a51542013-08-07 21:02:32 +0000732 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000733 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000734
735 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000736 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000737
738 // All objects need to be instantiated before this setjmp call so that
739 // they will be cleaned up properly if an error occurs.
740 if (setjmp(sk_err.fJmpBuf)) {
741 return false;
742 }
743
744 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000745 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000746 return false;
747 }
748
scroggo@google.coma1a51542013-08-07 21:02:32 +0000749 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000750 return false;
751 }
752
753 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000754 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000755
756 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000757 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000758 return false;
759 }
760
scroggo@google.com590a5af2013-08-07 21:09:13 +0000761 // FIXME: This sets cinfo->out_color_space, which we may change later
762 // based on the config in onDecodeSubset. This should be fine, since
763 // jpeg_init_read_tile_scanline will check out_color_space again after
764 // that change (when it calls jinit_color_deconverter).
reed6c225732014-06-09 19:52:07 -0700765 (void) this->getBitmapColorType(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000766
767 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000768
769 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000770 if (!imageIndex->startTileDecompress()) {
771 return false;
772 }
djsollen@google.com11399402013-03-20 17:45:27 +0000773
scroggo@google.coma1a51542013-08-07 21:02:32 +0000774 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000775 fImageWidth = cinfo->output_width;
776 fImageHeight = cinfo->output_height;
777
778 if (width) {
779 *width = fImageWidth;
780 }
781 if (height) {
782 *height = fImageHeight;
783 }
djsollen@google.com11399402013-03-20 17:45:27 +0000784
785 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000786 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +0000787
788 return true;
789}
790
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000791bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000792 if (NULL == fImageIndex) {
793 return false;
794 }
795 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
796
797 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
798 if (!rect.intersect(region)) {
799 // If the requested region is entirely outside the image return false
800 return false;
801 }
802
803
804 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000805 set_error_mgr(cinfo, &errorManager);
806
djsollen@google.com11399402013-03-20 17:45:27 +0000807 if (setjmp(errorManager.fJmpBuf)) {
808 return false;
809 }
810
811 int requestedSampleSize = this->getSampleSize();
812 cinfo->scale_denom = requestedSampleSize;
813
scroggo@google.com590a5af2013-08-07 21:09:13 +0000814 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000815
reed6c225732014-06-09 19:52:07 -0700816 const SkColorType colorType = this->getBitmapColorType(cinfo);
817 adjust_out_color_space_and_dither(cinfo, colorType, *this);
djsollen@google.com11399402013-03-20 17:45:27 +0000818
819 int startX = rect.fLeft;
820 int startY = rect.fTop;
821 int width = rect.width();
822 int height = rect.height();
823
824 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
825 &startX, &startY, &width, &height);
826 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
827 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
828
829 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
830
831 SkBitmap bitmap;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000832 // Assume an A8 bitmap is not opaque to avoid the check of each
833 // individual pixel. It is very unlikely to be opaque, since
834 // an opaque A8 bitmap would not be very interesting.
835 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700836 bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType,
837 kAlpha_8_SkColorType == colorType ?
838 kPremul_SkAlphaType : kOpaque_SkAlphaType));
djsollen@google.com11399402013-03-20 17:45:27 +0000839
840 // Check ahead of time if the swap(dest, src) is possible or not.
841 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
842 // swap happening. If no, then we will use alloc to allocate pixels to
843 // prevent garbage collection.
844 int w = rect.width() / actualSampleSize;
845 int h = rect.height() / actualSampleSize;
846 bool swapOnly = (rect == region) && bm->isNull() &&
847 (w == bitmap.width()) && (h == bitmap.height()) &&
848 ((startX - rect.x()) / actualSampleSize == 0) &&
849 ((startY - rect.y()) / actualSampleSize == 0);
850 if (swapOnly) {
851 if (!this->allocPixelRef(&bitmap, NULL)) {
852 return return_false(*cinfo, bitmap, "allocPixelRef");
853 }
854 } else {
reed84825042014-09-02 12:50:45 -0700855 if (!bitmap.tryAllocPixels()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000856 return return_false(*cinfo, bitmap, "allocPixels");
857 }
858 }
859
860 SkAutoLockPixels alp(bitmap);
861
862#ifdef ANDROID_RGB
863 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
864 a significant performance boost.
865 */
866 if (skiaSampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700867 ((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||
868 (kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565)))
djsollen@google.com11399402013-03-20 17:45:27 +0000869 {
870 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
871 INT32 const bpr = bitmap.rowBytes();
872 int rowTotalCount = 0;
873
874 while (rowTotalCount < height) {
875 int rowCount = jpeg_read_tile_scanline(cinfo,
876 fImageIndex->huffmanIndex(),
877 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +0000878 // if rowCount == 0, then we didn't get a scanline, so abort.
879 // onDecodeSubset() relies on onBuildTileIndex(), which
880 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +0000881 if (0 == rowCount) {
882 return return_false(*cinfo, bitmap, "read_scanlines");
883 }
884 if (this->shouldCancelDecode()) {
885 return return_false(*cinfo, bitmap, "shouldCancelDecode");
886 }
887 rowTotalCount += rowCount;
888 rowptr += bpr;
889 }
890
891 if (swapOnly) {
892 bm->swap(bitmap);
893 } else {
894 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
895 region.width(), region.height(), startX, startY);
896 }
897 return true;
898 }
899#endif
900
901 // check for supported formats
902 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000903 int srcBytesPerPixel;
904
905 if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
djsollen@google.com11399402013-03-20 17:45:27 +0000906 return return_false(*cinfo, *bm, "jpeg colorspace");
907 }
908
scroggo@google.com8d239242013-10-01 17:27:15 +0000909 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +0000910 return return_false(*cinfo, bitmap, "sampler.begin");
911 }
912
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000913 SkAutoMalloc srcStorage(width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000914 uint8_t* srcRow = (uint8_t*)srcStorage.get();
915
916 // Possibly skip initial rows [sampler.srcY0]
917 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
918 return return_false(*cinfo, bitmap, "skip rows");
919 }
920
921 // now loop through scanlines until y == bitmap->height() - 1
922 for (int y = 0;; y++) {
923 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
924 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +0000925 // if row_count == 0, then we didn't get a scanline, so abort.
926 // onDecodeSubset() relies on onBuildTileIndex(), which
927 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +0000928 if (0 == row_count) {
929 return return_false(*cinfo, bitmap, "read_scanlines");
930 }
931 if (this->shouldCancelDecode()) {
932 return return_false(*cinfo, bitmap, "shouldCancelDecode");
933 }
934
935 if (JCS_CMYK == cinfo->out_color_space) {
936 convert_CMYK_to_RGB(srcRow, width);
937 }
938
939 sampler.next(srcRow);
940 if (bitmap.height() - 1 == y) {
941 // we're done
942 break;
943 }
944
945 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
946 sampler.srcDY() - 1)) {
947 return return_false(*cinfo, bitmap, "skip rows");
948 }
949 }
950 if (swapOnly) {
951 bm->swap(bitmap);
952 } else {
953 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
954 region.width(), region.height(), startX, startY);
955 }
956 return true;
957}
958#endif
959
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000960///////////////////////////////////////////////////////////////////////////////
961
962#include "SkColorPriv.h"
963
964// taken from jcolor.c in libjpeg
965#if 0 // 16bit - precise but slow
966 #define CYR 19595 // 0.299
967 #define CYG 38470 // 0.587
968 #define CYB 7471 // 0.114
969
970 #define CUR -11059 // -0.16874
971 #define CUG -21709 // -0.33126
972 #define CUB 32768 // 0.5
973
974 #define CVR 32768 // 0.5
975 #define CVG -27439 // -0.41869
976 #define CVB -5329 // -0.08131
977
978 #define CSHIFT 16
979#else // 8bit - fast, slightly less precise
980 #define CYR 77 // 0.299
981 #define CYG 150 // 0.587
982 #define CYB 29 // 0.114
983
984 #define CUR -43 // -0.16874
985 #define CUG -85 // -0.33126
986 #define CUB 128 // 0.5
987
988 #define CVR 128 // 0.5
989 #define CVG -107 // -0.41869
990 #define CVB -21 // -0.08131
991
992 #define CSHIFT 8
993#endif
994
995static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
996 int r = SkGetPackedR32(c);
997 int g = SkGetPackedG32(c);
998 int b = SkGetPackedB32(c);
999
1000 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
1001 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
1002 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
1003
1004 dst[0] = SkToU8(y);
1005 dst[1] = SkToU8(u + 128);
1006 dst[2] = SkToU8(v + 128);
1007}
1008
1009static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
1010 int r = SkGetPackedR4444(c);
1011 int g = SkGetPackedG4444(c);
1012 int b = SkGetPackedB4444(c);
1013
1014 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
1015 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
1016 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
1017
1018 dst[0] = SkToU8(y);
1019 dst[1] = SkToU8(u + 128);
1020 dst[2] = SkToU8(v + 128);
1021}
1022
1023static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
1024 int r = SkGetPackedR16(c);
1025 int g = SkGetPackedG16(c);
1026 int b = SkGetPackedB16(c);
1027
1028 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
1029 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
1030 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
1031
1032 dst[0] = SkToU8(y);
1033 dst[1] = SkToU8(u + 128);
1034 dst[2] = SkToU8(v + 128);
1035}
1036
1037///////////////////////////////////////////////////////////////////////////////
1038
1039typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1040 const void* SK_RESTRICT src, int width,
1041 const SkPMColor* SK_RESTRICT ctable);
1042
1043static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1044 const void* SK_RESTRICT srcRow, int width,
1045 const SkPMColor*) {
1046 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1047 while (--width >= 0) {
1048#ifdef WE_CONVERT_TO_YUV
1049 rgb2yuv_32(dst, *src++);
1050#else
1051 uint32_t c = *src++;
1052 dst[0] = SkGetPackedR32(c);
1053 dst[1] = SkGetPackedG32(c);
1054 dst[2] = SkGetPackedB32(c);
1055#endif
1056 dst += 3;
1057 }
1058}
1059
1060static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1061 const void* SK_RESTRICT srcRow, int width,
1062 const SkPMColor*) {
1063 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1064 while (--width >= 0) {
1065#ifdef WE_CONVERT_TO_YUV
1066 rgb2yuv_4444(dst, *src++);
1067#else
1068 SkPMColor16 c = *src++;
1069 dst[0] = SkPacked4444ToR32(c);
1070 dst[1] = SkPacked4444ToG32(c);
1071 dst[2] = SkPacked4444ToB32(c);
1072#endif
1073 dst += 3;
1074 }
1075}
1076
1077static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1078 const void* SK_RESTRICT srcRow, int width,
1079 const SkPMColor*) {
1080 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1081 while (--width >= 0) {
1082#ifdef WE_CONVERT_TO_YUV
1083 rgb2yuv_16(dst, *src++);
1084#else
1085 uint16_t c = *src++;
1086 dst[0] = SkPacked16ToR32(c);
1087 dst[1] = SkPacked16ToG32(c);
1088 dst[2] = SkPacked16ToB32(c);
1089#endif
1090 dst += 3;
1091 }
1092}
1093
1094static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1095 const void* SK_RESTRICT srcRow, int width,
1096 const SkPMColor* SK_RESTRICT ctable) {
1097 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1098 while (--width >= 0) {
1099#ifdef WE_CONVERT_TO_YUV
1100 rgb2yuv_32(dst, ctable[*src++]);
1101#else
1102 uint32_t c = ctable[*src++];
1103 dst[0] = SkGetPackedR32(c);
1104 dst[1] = SkGetPackedG32(c);
1105 dst[2] = SkGetPackedB32(c);
1106#endif
1107 dst += 3;
1108 }
1109}
1110
1111static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -07001112 switch (bm.colorType()) {
1113 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001114 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -07001115 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001116 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -07001117 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001118 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -07001119 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001120 return Write_Index_YUV;
1121 default:
1122 return NULL;
1123 }
1124}
1125
1126class SkJPEGImageEncoder : public SkImageEncoder {
1127protected:
1128 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1129#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001130 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001131#endif
1132
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001133 SkAutoLockPixels alp(bm);
1134 if (NULL == bm.getPixels()) {
1135 return false;
1136 }
1137
1138 jpeg_compress_struct cinfo;
1139 skjpeg_error_mgr sk_err;
1140 skjpeg_destination_mgr sk_wstream(stream);
1141
1142 // allocate these before set call setjmp
1143 SkAutoMalloc oneRow;
1144 SkAutoLockColors ctLocker;
1145
1146 cinfo.err = jpeg_std_error(&sk_err);
1147 sk_err.error_exit = skjpeg_error_exit;
1148 if (setjmp(sk_err.fJmpBuf)) {
1149 return false;
1150 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001151
mtklein@google.com8d725b22013-07-24 16:20:05 +00001152 // Keep after setjmp or mark volatile.
1153 const WriteScanline writer = ChooseWriter(bm);
1154 if (NULL == writer) {
1155 return false;
1156 }
1157
1158 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001159 cinfo.dest = &sk_wstream;
1160 cinfo.image_width = bm.width();
1161 cinfo.image_height = bm.height();
1162 cinfo.input_components = 3;
1163#ifdef WE_CONVERT_TO_YUV
1164 cinfo.in_color_space = JCS_YCbCr;
1165#else
1166 cinfo.in_color_space = JCS_RGB;
1167#endif
1168 cinfo.input_gamma = 1;
1169
1170 jpeg_set_defaults(&cinfo);
1171 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001172#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001173 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001174#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001175
1176 jpeg_start_compress(&cinfo, TRUE);
1177
1178 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001179 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001180
1181 const SkPMColor* colors = ctLocker.lockColors(bm);
1182 const void* srcRow = bm.getPixels();
1183
1184 while (cinfo.next_scanline < cinfo.image_height) {
1185 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1186
1187 writer(oneRowP, srcRow, width, colors);
1188 row_pointer[0] = oneRowP;
1189 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1190 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1191 }
1192
1193 jpeg_finish_compress(&cinfo);
1194 jpeg_destroy_compress(&cinfo);
1195
1196 return true;
1197 }
1198};
1199
1200///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001201DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1202DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1203///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001204
scroggo@google.comb5571b32013-09-25 21:34:24 +00001205static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001206 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001207 static const size_t HEADER_SIZE = sizeof(gHeader);
1208
1209 char buffer[HEADER_SIZE];
1210 size_t len = stream->read(buffer, HEADER_SIZE);
1211
1212 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001213 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001214 }
1215 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001216 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001217 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001218 return true;
1219}
1220
scroggo@google.comb5571b32013-09-25 21:34:24 +00001221
1222static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001223 if (is_jpeg(stream)) {
1224 return SkNEW(SkJPEGImageDecoder);
1225 }
1226 return NULL;
1227}
1228
scroggo@google.comb5571b32013-09-25 21:34:24 +00001229static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001230 if (is_jpeg(stream)) {
1231 return SkImageDecoder::kJPEG_Format;
1232 }
1233 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001234}
1235
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001236static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001237 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1238}
1239
mtklein@google.combd6343b2013-09-04 17:20:18 +00001240static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1241static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1242static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);