blob: 64309514184330fa7e2e36aec6c355c38b430fb0 [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
42#else // !defined(SK_DEBUG)
43#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS true
halcanary@google.comfed30372013-10-04 12:46:45 +000044#endif // defined(SK_DEBUG)
halcanary@google.com2a103182013-10-14 12:49:15 +000045SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderWarnings,
46 "images.jpeg.suppressDecoderWarnings",
47 DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS,
48 "Suppress most JPG warnings when calling decode functions.");
halcanary@google.comfed30372013-10-04 12:46:45 +000049
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000050//////////////////////////////////////////////////////////////////////////
51//////////////////////////////////////////////////////////////////////////
52
scroggo@google.com590a5af2013-08-07 21:09:13 +000053static void overwrite_mem_buffer_size(jpeg_decompress_struct* cinfo) {
djsollen@google.com11399402013-03-20 17:45:27 +000054#ifdef SK_BUILD_FOR_ANDROID
55 /* Check if the device indicates that it has a large amount of system memory
56 * if so, increase the memory allocation to 30MB instead of the default 5MB.
57 */
58#ifdef ANDROID_LARGE_MEMORY_DEVICE
59 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
60#else
61 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
62#endif
63#endif // SK_BUILD_FOR_ANDROID
64}
65
66//////////////////////////////////////////////////////////////////////////
67//////////////////////////////////////////////////////////////////////////
68
halcanary@google.comfed30372013-10-04 12:46:45 +000069static void do_nothing_emit_message(jpeg_common_struct*, int) {
70 /* do nothing */
71}
72
scroggo@google.com590a5af2013-08-07 21:09:13 +000073static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* src_mgr) {
74 SkASSERT(cinfo != NULL);
75 SkASSERT(src_mgr != NULL);
76 jpeg_create_decompress(cinfo);
77 overwrite_mem_buffer_size(cinfo);
78 cinfo->src = src_mgr;
halcanary@google.comfed30372013-10-04 12:46:45 +000079 /* To suppress warnings with a SK_DEBUG binary, set the
80 * environment variable "skia_images_jpeg_suppressDecoderWarnings"
81 * to "true". Inside a program that links to skia:
82 * SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); */
83 if (c_suppressJPEGImageDecoderWarnings) {
84 cinfo->err->emit_message = &do_nothing_emit_message;
85 }
scroggo@google.com590a5af2013-08-07 21:09:13 +000086}
87
scroggo@google.coma1a51542013-08-07 21:02:32 +000088#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +000089class SkJPEGImageIndex {
90public:
scroggo@google.comb5571b32013-09-25 21:34:24 +000091 SkJPEGImageIndex(SkStreamRewindable* stream, SkImageDecoder* decoder)
scroggo@google.coma1a51542013-08-07 21:02:32 +000092 : fSrcMgr(stream, decoder)
93 , fInfoInitialized(false)
94 , fHuffmanCreated(false)
95 , fDecompressStarted(false)
96 {
97 SkDEBUGCODE(fReadHeaderSucceeded = false;)
98 }
djsollen@google.com11399402013-03-20 17:45:27 +000099
100 ~SkJPEGImageIndex() {
scroggo@google.coma1a51542013-08-07 21:02:32 +0000101 if (fHuffmanCreated) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000102 // Set to false before calling the libjpeg function, in case
103 // the libjpeg function calls longjmp. Our setjmp handler may
104 // attempt to delete this SkJPEGImageIndex, thus entering this
105 // destructor again. Setting fHuffmanCreated to false first
106 // prevents an infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000107 fHuffmanCreated = false;
108 jpeg_destroy_huffman_index(&fHuffmanIndex);
109 }
110 if (fDecompressStarted) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000111 // Like fHuffmanCreated, set to false before calling libjpeg
112 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000113 fDecompressStarted = false;
114 jpeg_finish_decompress(&fCInfo);
115 }
116 if (fInfoInitialized) {
117 this->destroyInfo();
118 }
djsollen@google.com11399402013-03-20 17:45:27 +0000119 }
120
121 /**
scroggo@google.coma1a51542013-08-07 21:02:32 +0000122 * Destroy the cinfo struct.
123 * After this call, if a huffman index was already built, it
124 * can be used after calling initializeInfoAndReadHeader
125 * again. Must not be called after startTileDecompress except
126 * in the destructor.
djsollen@google.com11399402013-03-20 17:45:27 +0000127 */
scroggo@google.coma1a51542013-08-07 21:02:32 +0000128 void destroyInfo() {
129 SkASSERT(fInfoInitialized);
130 SkASSERT(!fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000131 // Like fHuffmanCreated, set to false before calling libjpeg
132 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000133 fInfoInitialized = false;
134 jpeg_destroy_decompress(&fCInfo);
135 SkDEBUGCODE(fReadHeaderSucceeded = false;)
136 }
137
138 /**
139 * Initialize the cinfo struct.
140 * Calls jpeg_create_decompress, makes customizations, and
141 * finally calls jpeg_read_header. Returns true if jpeg_read_header
142 * returns JPEG_HEADER_OK.
143 * If cinfo was already initialized, destroyInfo must be called to
144 * destroy the old one. Must not be called after startTileDecompress.
145 */
146 bool initializeInfoAndReadHeader() {
147 SkASSERT(!fInfoInitialized && !fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000148 initialize_info(&fCInfo, &fSrcMgr);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000149 fInfoInitialized = true;
150 const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true));
151 SkDEBUGCODE(fReadHeaderSucceeded = success;)
152 return success;
djsollen@google.com11399402013-03-20 17:45:27 +0000153 }
154
155 jpeg_decompress_struct* cinfo() { return &fCInfo; }
156
djsollen@google.com11399402013-03-20 17:45:27 +0000157 huffman_index* huffmanIndex() { return &fHuffmanIndex; }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000158
159 /**
160 * Build the index to be used for tile based decoding.
161 * Must only be called after a successful call to
162 * initializeInfoAndReadHeader and must not be called more
163 * than once.
164 */
165 bool buildHuffmanIndex() {
166 SkASSERT(fReadHeaderSucceeded);
167 SkASSERT(!fHuffmanCreated);
168 jpeg_create_huffman_index(&fCInfo, &fHuffmanIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000169 SkASSERT(1 == fCInfo.scale_num && 1 == fCInfo.scale_denom);
scroggo@google.com57a52982013-08-27 20:42:22 +0000170 fHuffmanCreated = jpeg_build_huffman_index(&fCInfo, &fHuffmanIndex);
171 return fHuffmanCreated;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000172 }
173
174 /**
175 * Start tile based decoding. Must only be called after a
176 * successful call to buildHuffmanIndex, and must only be
177 * called once.
178 */
179 bool startTileDecompress() {
180 SkASSERT(fHuffmanCreated);
181 SkASSERT(fReadHeaderSucceeded);
182 SkASSERT(!fDecompressStarted);
183 if (jpeg_start_tile_decompress(&fCInfo)) {
184 fDecompressStarted = true;
185 return true;
186 }
187 return false;
188 }
djsollen@google.com11399402013-03-20 17:45:27 +0000189
190private:
191 skjpeg_source_mgr fSrcMgr;
192 jpeg_decompress_struct fCInfo;
djsollen@google.com11399402013-03-20 17:45:27 +0000193 huffman_index fHuffmanIndex;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000194 bool fInfoInitialized;
195 bool fHuffmanCreated;
196 bool fDecompressStarted;
197 SkDEBUGCODE(bool fReadHeaderSucceeded;)
djsollen@google.com11399402013-03-20 17:45:27 +0000198};
scroggo@google.coma1a51542013-08-07 21:02:32 +0000199#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000200
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000201class SkJPEGImageDecoder : public SkImageDecoder {
202public:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000203#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000204 SkJPEGImageDecoder() {
205 fImageIndex = NULL;
206 fImageWidth = 0;
207 fImageHeight = 0;
208 }
209
210 virtual ~SkJPEGImageDecoder() {
211 SkDELETE(fImageIndex);
212 }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000213#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000214
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000215 virtual Format getFormat() const {
216 return kJPEG_Format;
217 }
218
219protected:
scroggo@google.comd79277f2013-08-07 19:53:53 +0000220#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000221 virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000222 virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
djsollen@google.com11399402013-03-20 17:45:27 +0000223#endif
224 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
225
226private:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000227#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000228 SkJPEGImageIndex* fImageIndex;
229 int fImageWidth;
230 int fImageHeight;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000231#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000232
scroggo@google.com590a5af2013-08-07 21:09:13 +0000233 /**
234 * Determine the appropriate bitmap config and out_color_space based on
235 * both the preference of the caller and the jpeg_color_space on the
236 * jpeg_decompress_struct passed in.
237 * Must be called after jpeg_read_header.
238 */
239 SkBitmap::Config getBitmapConfig(jpeg_decompress_struct*);
240
djsollen@google.com11399402013-03-20 17:45:27 +0000241 typedef SkImageDecoder INHERITED;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000242};
243
244//////////////////////////////////////////////////////////////////////////
245
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000246/* Automatically clean up after throwing an exception */
247class JPEGAutoClean {
248public:
249 JPEGAutoClean(): cinfo_ptr(NULL) {}
250 ~JPEGAutoClean() {
251 if (cinfo_ptr) {
252 jpeg_destroy_decompress(cinfo_ptr);
253 }
254 }
255 void set(jpeg_decompress_struct* info) {
256 cinfo_ptr = info;
257 }
258private:
259 jpeg_decompress_struct* cinfo_ptr;
260};
261
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000262///////////////////////////////////////////////////////////////////////////////
263
264/* If we need to better match the request, we might examine the image and
265 output dimensions, and determine if the downsampling jpeg provided is
266 not sufficient. If so, we can recompute a modified sampleSize value to
267 make up the difference.
268
269 To skip this additional scaling, just set sampleSize = 1; below.
270 */
271static int recompute_sampleSize(int sampleSize,
272 const jpeg_decompress_struct& cinfo) {
273 return sampleSize * cinfo.output_width / cinfo.image_width;
274}
275
276static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
277 /* These are initialized to 0, so if they have non-zero values, we assume
278 they are "valid" (i.e. have been computed by libjpeg)
279 */
djsollen@google.com11399402013-03-20 17:45:27 +0000280 return 0 != cinfo.output_width && 0 != cinfo.output_height;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000281}
282
djsollen@google.com11399402013-03-20 17:45:27 +0000283static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000284 for (int i = 0; i < count; i++) {
285 JSAMPLE* rowptr = (JSAMPLE*)buffer;
286 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
djsollen@google.com11399402013-03-20 17:45:27 +0000287 if (1 != row_count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000288 return false;
289 }
290 }
291 return true;
292}
293
scroggo@google.comd79277f2013-08-07 19:53:53 +0000294#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000295static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
296 huffman_index *index, void* buffer, int count) {
297 for (int i = 0; i < count; i++) {
298 JSAMPLE* rowptr = (JSAMPLE*)buffer;
299 int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
300 if (1 != row_count) {
301 return false;
302 }
303 }
304 return true;
305}
306#endif
307
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000308// This guy exists just to aid in debugging, as it allows debuggers to just
309// set a break-point in one place to see all error exists.
310static bool return_false(const jpeg_decompress_struct& cinfo,
scroggo@google.com228f2b82013-09-25 21:27:31 +0000311 const SkBitmap& bm, const char caller[]) {
djsollen@google.com11399402013-03-20 17:45:27 +0000312#ifdef SK_DEBUG
scroggo@google.com228f2b82013-09-25 21:27:31 +0000313 char buffer[JMSG_LENGTH_MAX];
314 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
scroggo@google.come8f34712013-07-01 14:44:54 +0000315 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n", cinfo.err->msg_code,
scroggo@google.com228f2b82013-09-25 21:27:31 +0000316 buffer, caller, bm.width(), bm.height());
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000317#endif
318 return false; // must always return false
319}
320
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000321// Convert a scanline of CMYK samples to RGBX in place. Note that this
322// method moves the "scanline" pointer in its processing
323static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
324 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000325 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000326 // from easyrgb.com):
327 // CMYK -> CMY
328 // C = ( C * (1 - K) + K ) // for each CMY component
329 // CMY -> RGB
330 // R = ( 1 - C ) * 255 // for each RGB component
331 // Unfortunately we are seeing inverted CMYK so all the original terms
332 // are 1-. This yields:
333 // CMYK -> CMY
334 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
335 // The conversion from CMY->RGB remains the same
336 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
337 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
338 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
339 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
340 scanline[3] = 255;
341 }
342}
343
scroggo@google.com590a5af2013-08-07 21:09:13 +0000344/**
345 * Common code for setting the error manager.
346 */
347static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
348 SkASSERT(cinfo != NULL);
349 SkASSERT(errorManager != NULL);
350 cinfo->err = jpeg_std_error(errorManager);
351 errorManager->error_exit = skjpeg_error_exit;
352}
353
354/**
355 * Common code for turning off upsampling and smoothing. Turning these
356 * off helps performance without showing noticable differences in the
357 * resulting bitmap.
358 */
359static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
360 SkASSERT(cinfo != NULL);
361 /* this gives about 30% performance improvement. In theory it may
362 reduce the visual quality, in practice I'm not seeing a difference
363 */
364 cinfo->do_fancy_upsampling = 0;
365
366 /* this gives another few percents */
367 cinfo->do_block_smoothing = 0;
368}
369
370/**
371 * Common code for setting the dct method.
372 */
373static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
374 SkASSERT(cinfo != NULL);
375#ifdef DCT_IFAST_SUPPORTED
376 if (decoder.getPreferQualityOverSpeed()) {
377 cinfo->dct_method = JDCT_ISLOW;
378 } else {
379 cinfo->dct_method = JDCT_IFAST;
380 }
381#else
382 cinfo->dct_method = JDCT_ISLOW;
383#endif
384}
385
386SkBitmap::Config SkJPEGImageDecoder::getBitmapConfig(jpeg_decompress_struct* cinfo) {
387 SkASSERT(cinfo != NULL);
388
389 SrcDepth srcDepth = k32Bit_SrcDepth;
390 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
391 srcDepth = k8BitGray_SrcDepth;
392 }
393
394 SkBitmap::Config config = this->getPrefConfig(srcDepth, /*hasAlpha*/ false);
395 switch (config) {
396 case SkBitmap::kA8_Config:
397 // Only respect A8 config if the original is grayscale,
398 // in which case we will treat the grayscale as alpha
399 // values.
400 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
401 config = SkBitmap::kARGB_8888_Config;
402 }
403 break;
404 case SkBitmap::kARGB_8888_Config:
405 // Fall through.
406 case SkBitmap::kARGB_4444_Config:
407 // Fall through.
408 case SkBitmap::kRGB_565_Config:
409 // These are acceptable destination configs.
410 break;
411 default:
412 // Force all other configs to 8888.
413 config = SkBitmap::kARGB_8888_Config;
414 break;
415 }
416
417 switch (cinfo->jpeg_color_space) {
418 case JCS_CMYK:
419 // Fall through.
420 case JCS_YCCK:
421 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
422 // so libjpeg will give us CMYK samples back and we will later
423 // manually convert them to RGB
424 cinfo->out_color_space = JCS_CMYK;
425 break;
426 case JCS_GRAYSCALE:
427 if (SkBitmap::kA8_Config == config) {
428 cinfo->out_color_space = JCS_GRAYSCALE;
429 break;
430 }
431 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
432 // config. Fall through to set to the default.
433 default:
434 cinfo->out_color_space = JCS_RGB;
435 break;
436 }
437 return config;
438}
439
440#ifdef ANDROID_RGB
441/**
442 * Based on the config and dither mode, adjust out_color_space and
443 * dither_mode of cinfo.
444 */
445static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
446 SkBitmap::Config config,
447 const SkImageDecoder& decoder) {
448 SkASSERT(cinfo != NULL);
449 cinfo->dither_mode = JDITHER_NONE;
450 if (JCS_CMYK == cinfo->out_color_space) {
451 return;
452 }
453 switch(config) {
454 case SkBitmap::kARGB_8888_Config:
455 cinfo->out_color_space = JCS_RGBA_8888;
456 break;
457 case SkBitmap::kRGB_565_Config:
458 cinfo->out_color_space = JCS_RGB_565;
459 if (decoder.getDitherImage()) {
460 cinfo->dither_mode = JDITHER_ORDERED;
461 }
462 break;
463 default:
464 break;
465 }
466}
467#endif
468
halcanary@google.comfed30372013-10-04 12:46:45 +0000469
470/**
471 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
472 Used when decoding fails partway through reading scanlines to fill
473 remaining lines. */
474static void fill_below_level(int y, SkBitmap* bitmap) {
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000475 SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
halcanary@google.comfed30372013-10-04 12:46:45 +0000476 SkCanvas canvas(*bitmap);
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000477 canvas.clipRect(SkRect::Make(rect));
halcanary@google.comfed30372013-10-04 12:46:45 +0000478 canvas.drawColor(SK_ColorWHITE);
479}
480
481
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000482bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
483#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000484 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000485#endif
486
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000487 JPEGAutoClean autoClean;
488
489 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000490 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000491
scroggo@google.com590a5af2013-08-07 21:09:13 +0000492 skjpeg_error_mgr errorManager;
493 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000494
495 // All objects need to be instantiated before this setjmp call so that
496 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000497 if (setjmp(errorManager.fJmpBuf)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000498 return return_false(cinfo, *bm, "setjmp");
499 }
500
scroggo@google.com590a5af2013-08-07 21:09:13 +0000501 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000502 autoClean.set(&cinfo);
503
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000504 int status = jpeg_read_header(&cinfo, true);
505 if (status != JPEG_HEADER_OK) {
506 return return_false(cinfo, *bm, "read_header");
507 }
508
509 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
510 can) much faster that we, just use their num/denom api to approximate
511 the size.
512 */
513 int sampleSize = this->getSampleSize();
514
scroggo@google.com590a5af2013-08-07 21:09:13 +0000515 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000516
scroggo@google.com590a5af2013-08-07 21:09:13 +0000517 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000518 cinfo.scale_denom = sampleSize;
519
scroggo@google.com590a5af2013-08-07 21:09:13 +0000520 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000521
scroggo@google.com590a5af2013-08-07 21:09:13 +0000522 const SkBitmap::Config config = this->getBitmapConfig(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000523
524#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000525 adjust_out_color_space_and_dither(&cinfo, config, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000526#endif
527
djsollen@google.com11399402013-03-20 17:45:27 +0000528 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000529 bm->setConfig(config, cinfo.image_width, cinfo.image_height);
scroggo@google.com1f6b9952013-07-18 19:37:46 +0000530 bm->setIsOpaque(config != SkBitmap::kA8_Config);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000531 return true;
532 }
533
534 /* image_width and image_height are the original dimensions, available
535 after jpeg_read_header(). To see the scaled dimensions, we have to call
536 jpeg_start_decompress(), and then read output_width and output_height.
537 */
538 if (!jpeg_start_decompress(&cinfo)) {
539 /* If we failed here, we may still have enough information to return
540 to the caller if they just wanted (subsampled bounds). If sampleSize
541 was 1, then we would have already returned. Thus we just check if
542 we're in kDecodeBounds_Mode, and that we have valid output sizes.
543
544 One reason to fail here is that we have insufficient stream data
545 to complete the setup. However, output dimensions seem to get
546 computed very early, which is why this special check can pay off.
547 */
djsollen@google.com11399402013-03-20 17:45:27 +0000548 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000549 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
550 recompute_sampleSize(sampleSize, cinfo));
551 bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
scroggo@google.com1f6b9952013-07-18 19:37:46 +0000552 bm->setIsOpaque(config != SkBitmap::kA8_Config);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000553 return true;
554 } else {
555 return return_false(cinfo, *bm, "start_decompress");
556 }
557 }
558 sampleSize = recompute_sampleSize(sampleSize, cinfo);
559
560 // should we allow the Chooser (if present) to pick a config for us???
djsollen@google.com11399402013-03-20 17:45:27 +0000561 if (!this->chooseFromOneChoice(config, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000562 return return_false(cinfo, *bm, "chooseFromOneChoice");
563 }
564
djsollen@google.com11399402013-03-20 17:45:27 +0000565 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000566 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
scroggo@google.com1f6b9952013-07-18 19:37:46 +0000567 bm->setIsOpaque(config != SkBitmap::kA8_Config);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000568 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
569 return true;
570 }
571 if (!this->allocPixelRef(bm, NULL)) {
572 return return_false(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000573 }
574
575 SkAutoLockPixels alp(*bm);
576
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000577#ifdef ANDROID_RGB
578 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
579 a significant performance boost.
580 */
581 if (sampleSize == 1 &&
rmistry@google.comd6176b02012-08-23 18:14:13 +0000582 ((config == SkBitmap::kARGB_8888_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000583 cinfo.out_color_space == JCS_RGBA_8888) ||
rmistry@google.comd6176b02012-08-23 18:14:13 +0000584 (config == SkBitmap::kRGB_565_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000585 cinfo.out_color_space == JCS_RGB_565)))
586 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000587 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000588 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000589
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000590 while (cinfo.output_scanline < cinfo.output_height) {
591 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000592 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000593 // if row_count == 0, then we didn't get a scanline,
594 // so return early. We will return a partial image.
595 fill_below_level(cinfo.output_scanline, bm);
596 cinfo.output_scanline = cinfo.output_height;
597 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000598 }
599 if (this->shouldCancelDecode()) {
600 return return_false(cinfo, *bm, "shouldCancelDecode");
601 }
602 rowptr += bpr;
603 }
604 jpeg_finish_decompress(&cinfo);
605 return true;
606 }
607#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000608
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000609 // check for supported formats
610 SkScaledBitmapSampler::SrcConfig sc;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000611 if (JCS_CMYK == cinfo.out_color_space) {
612 // In this case we will manually convert the CMYK values to RGB
613 sc = SkScaledBitmapSampler::kRGBX;
614 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000615 sc = SkScaledBitmapSampler::kRGB;
616#ifdef ANDROID_RGB
617 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
618 sc = SkScaledBitmapSampler::kRGBX;
619 } else if (JCS_RGB_565 == cinfo.out_color_space) {
620 sc = SkScaledBitmapSampler::kRGB_565;
621#endif
622 } else if (1 == cinfo.out_color_components &&
623 JCS_GRAYSCALE == cinfo.out_color_space) {
624 sc = SkScaledBitmapSampler::kGray;
625 } else {
626 return return_false(cinfo, *bm, "jpeg colorspace");
627 }
628
scroggo@google.com8d239242013-10-01 17:27:15 +0000629 if (!sampler.begin(bm, sc, *this)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000630 return return_false(cinfo, *bm, "sampler.begin");
631 }
632
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000633 // The CMYK work-around relies on 4 components per pixel here
djsollen@google.com11399402013-03-20 17:45:27 +0000634 SkAutoMalloc srcStorage(cinfo.output_width * 4);
635 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000636
637 // Possibly skip initial rows [sampler.srcY0]
638 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
639 return return_false(cinfo, *bm, "skip rows");
640 }
641
642 // now loop through scanlines until y == bm->height() - 1
643 for (int y = 0;; y++) {
644 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
645 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
646 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000647 // if row_count == 0, then we didn't get a scanline,
648 // so return early. We will return a partial image.
649 fill_below_level(y, bm);
650 cinfo.output_scanline = cinfo.output_height;
651 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000652 }
653 if (this->shouldCancelDecode()) {
654 return return_false(cinfo, *bm, "shouldCancelDecode");
655 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000656
657 if (JCS_CMYK == cinfo.out_color_space) {
658 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
659 }
660
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000661 sampler.next(srcRow);
662 if (bm->height() - 1 == y) {
663 // we're done
664 break;
665 }
666
667 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
668 return return_false(cinfo, *bm, "skip rows");
669 }
670 }
671
672 // we formally skip the rest, so we don't get a complaint from libjpeg
673 if (!skip_src_rows(&cinfo, srcRow,
674 cinfo.output_height - cinfo.output_scanline)) {
675 return return_false(cinfo, *bm, "skip rows");
676 }
677 jpeg_finish_decompress(&cinfo);
678
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000679 return true;
680}
681
scroggo@google.comd79277f2013-08-07 19:53:53 +0000682#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000683bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000684
scroggo@google.coma1a51542013-08-07 21:02:32 +0000685 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000686 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000687
688 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000689 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000690
691 // All objects need to be instantiated before this setjmp call so that
692 // they will be cleaned up properly if an error occurs.
693 if (setjmp(sk_err.fJmpBuf)) {
694 return false;
695 }
696
697 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000698 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000699 return false;
700 }
701
scroggo@google.coma1a51542013-08-07 21:02:32 +0000702 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000703 return false;
704 }
705
706 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000707 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000708
709 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000710 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000711 return false;
712 }
713
scroggo@google.com590a5af2013-08-07 21:09:13 +0000714 // FIXME: This sets cinfo->out_color_space, which we may change later
715 // based on the config in onDecodeSubset. This should be fine, since
716 // jpeg_init_read_tile_scanline will check out_color_space again after
717 // that change (when it calls jinit_color_deconverter).
718 (void) this->getBitmapConfig(cinfo);
719
720 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000721
722 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000723 if (!imageIndex->startTileDecompress()) {
724 return false;
725 }
djsollen@google.com11399402013-03-20 17:45:27 +0000726
scroggo@google.coma1a51542013-08-07 21:02:32 +0000727 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000728 fImageWidth = cinfo->output_width;
729 fImageHeight = cinfo->output_height;
730
731 if (width) {
732 *width = fImageWidth;
733 }
734 if (height) {
735 *height = fImageHeight;
736 }
djsollen@google.com11399402013-03-20 17:45:27 +0000737
738 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000739 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +0000740
741 return true;
742}
743
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000744bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000745 if (NULL == fImageIndex) {
746 return false;
747 }
748 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
749
750 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
751 if (!rect.intersect(region)) {
752 // If the requested region is entirely outside the image return false
753 return false;
754 }
755
756
757 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000758 set_error_mgr(cinfo, &errorManager);
759
djsollen@google.com11399402013-03-20 17:45:27 +0000760 if (setjmp(errorManager.fJmpBuf)) {
761 return false;
762 }
763
764 int requestedSampleSize = this->getSampleSize();
765 cinfo->scale_denom = requestedSampleSize;
766
scroggo@google.com590a5af2013-08-07 21:09:13 +0000767 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000768
scroggo@google.com590a5af2013-08-07 21:09:13 +0000769 const SkBitmap::Config config = this->getBitmapConfig(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000770#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000771 adjust_out_color_space_and_dither(cinfo, config, *this);
djsollen@google.com11399402013-03-20 17:45:27 +0000772#endif
773
774 int startX = rect.fLeft;
775 int startY = rect.fTop;
776 int width = rect.width();
777 int height = rect.height();
778
779 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
780 &startX, &startY, &width, &height);
781 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
782 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
783
784 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
785
786 SkBitmap bitmap;
787 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
788 bitmap.setIsOpaque(true);
789
790 // Check ahead of time if the swap(dest, src) is possible or not.
791 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
792 // swap happening. If no, then we will use alloc to allocate pixels to
793 // prevent garbage collection.
794 int w = rect.width() / actualSampleSize;
795 int h = rect.height() / actualSampleSize;
796 bool swapOnly = (rect == region) && bm->isNull() &&
797 (w == bitmap.width()) && (h == bitmap.height()) &&
798 ((startX - rect.x()) / actualSampleSize == 0) &&
799 ((startY - rect.y()) / actualSampleSize == 0);
800 if (swapOnly) {
801 if (!this->allocPixelRef(&bitmap, NULL)) {
802 return return_false(*cinfo, bitmap, "allocPixelRef");
803 }
804 } else {
805 if (!bitmap.allocPixels()) {
806 return return_false(*cinfo, bitmap, "allocPixels");
807 }
808 }
809
810 SkAutoLockPixels alp(bitmap);
811
812#ifdef ANDROID_RGB
813 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
814 a significant performance boost.
815 */
816 if (skiaSampleSize == 1 &&
817 ((config == SkBitmap::kARGB_8888_Config &&
818 cinfo->out_color_space == JCS_RGBA_8888) ||
819 (config == SkBitmap::kRGB_565_Config &&
820 cinfo->out_color_space == JCS_RGB_565)))
821 {
822 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
823 INT32 const bpr = bitmap.rowBytes();
824 int rowTotalCount = 0;
825
826 while (rowTotalCount < height) {
827 int rowCount = jpeg_read_tile_scanline(cinfo,
828 fImageIndex->huffmanIndex(),
829 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +0000830 // if rowCount == 0, then we didn't get a scanline, so abort.
831 // onDecodeSubset() relies on onBuildTileIndex(), which
832 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +0000833 if (0 == rowCount) {
834 return return_false(*cinfo, bitmap, "read_scanlines");
835 }
836 if (this->shouldCancelDecode()) {
837 return return_false(*cinfo, bitmap, "shouldCancelDecode");
838 }
839 rowTotalCount += rowCount;
840 rowptr += bpr;
841 }
842
843 if (swapOnly) {
844 bm->swap(bitmap);
845 } else {
846 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
847 region.width(), region.height(), startX, startY);
848 }
849 return true;
850 }
851#endif
852
853 // check for supported formats
854 SkScaledBitmapSampler::SrcConfig sc;
855 if (JCS_CMYK == cinfo->out_color_space) {
856 // In this case we will manually convert the CMYK values to RGB
857 sc = SkScaledBitmapSampler::kRGBX;
858 } else if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_space) {
859 sc = SkScaledBitmapSampler::kRGB;
860#ifdef ANDROID_RGB
861 } else if (JCS_RGBA_8888 == cinfo->out_color_space) {
862 sc = SkScaledBitmapSampler::kRGBX;
863 } else if (JCS_RGB_565 == cinfo->out_color_space) {
864 sc = SkScaledBitmapSampler::kRGB_565;
865#endif
866 } else if (1 == cinfo->out_color_components &&
867 JCS_GRAYSCALE == cinfo->out_color_space) {
868 sc = SkScaledBitmapSampler::kGray;
869 } else {
870 return return_false(*cinfo, *bm, "jpeg colorspace");
871 }
872
scroggo@google.com8d239242013-10-01 17:27:15 +0000873 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +0000874 return return_false(*cinfo, bitmap, "sampler.begin");
875 }
876
877 // The CMYK work-around relies on 4 components per pixel here
878 SkAutoMalloc srcStorage(width * 4);
879 uint8_t* srcRow = (uint8_t*)srcStorage.get();
880
881 // Possibly skip initial rows [sampler.srcY0]
882 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
883 return return_false(*cinfo, bitmap, "skip rows");
884 }
885
886 // now loop through scanlines until y == bitmap->height() - 1
887 for (int y = 0;; y++) {
888 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
889 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +0000890 // if row_count == 0, then we didn't get a scanline, so abort.
891 // onDecodeSubset() relies on onBuildTileIndex(), which
892 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +0000893 if (0 == row_count) {
894 return return_false(*cinfo, bitmap, "read_scanlines");
895 }
896 if (this->shouldCancelDecode()) {
897 return return_false(*cinfo, bitmap, "shouldCancelDecode");
898 }
899
900 if (JCS_CMYK == cinfo->out_color_space) {
901 convert_CMYK_to_RGB(srcRow, width);
902 }
903
904 sampler.next(srcRow);
905 if (bitmap.height() - 1 == y) {
906 // we're done
907 break;
908 }
909
910 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
911 sampler.srcDY() - 1)) {
912 return return_false(*cinfo, bitmap, "skip rows");
913 }
914 }
915 if (swapOnly) {
916 bm->swap(bitmap);
917 } else {
918 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
919 region.width(), region.height(), startX, startY);
920 }
921 return true;
922}
923#endif
924
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000925///////////////////////////////////////////////////////////////////////////////
926
927#include "SkColorPriv.h"
928
929// taken from jcolor.c in libjpeg
930#if 0 // 16bit - precise but slow
931 #define CYR 19595 // 0.299
932 #define CYG 38470 // 0.587
933 #define CYB 7471 // 0.114
934
935 #define CUR -11059 // -0.16874
936 #define CUG -21709 // -0.33126
937 #define CUB 32768 // 0.5
938
939 #define CVR 32768 // 0.5
940 #define CVG -27439 // -0.41869
941 #define CVB -5329 // -0.08131
942
943 #define CSHIFT 16
944#else // 8bit - fast, slightly less precise
945 #define CYR 77 // 0.299
946 #define CYG 150 // 0.587
947 #define CYB 29 // 0.114
948
949 #define CUR -43 // -0.16874
950 #define CUG -85 // -0.33126
951 #define CUB 128 // 0.5
952
953 #define CVR 128 // 0.5
954 #define CVG -107 // -0.41869
955 #define CVB -21 // -0.08131
956
957 #define CSHIFT 8
958#endif
959
960static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
961 int r = SkGetPackedR32(c);
962 int g = SkGetPackedG32(c);
963 int b = SkGetPackedB32(c);
964
965 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
966 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
967 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
968
969 dst[0] = SkToU8(y);
970 dst[1] = SkToU8(u + 128);
971 dst[2] = SkToU8(v + 128);
972}
973
974static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
975 int r = SkGetPackedR4444(c);
976 int g = SkGetPackedG4444(c);
977 int b = SkGetPackedB4444(c);
978
979 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
980 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
981 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
982
983 dst[0] = SkToU8(y);
984 dst[1] = SkToU8(u + 128);
985 dst[2] = SkToU8(v + 128);
986}
987
988static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
989 int r = SkGetPackedR16(c);
990 int g = SkGetPackedG16(c);
991 int b = SkGetPackedB16(c);
992
993 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
994 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
995 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
996
997 dst[0] = SkToU8(y);
998 dst[1] = SkToU8(u + 128);
999 dst[2] = SkToU8(v + 128);
1000}
1001
1002///////////////////////////////////////////////////////////////////////////////
1003
1004typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1005 const void* SK_RESTRICT src, int width,
1006 const SkPMColor* SK_RESTRICT ctable);
1007
1008static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1009 const void* SK_RESTRICT srcRow, int width,
1010 const SkPMColor*) {
1011 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1012 while (--width >= 0) {
1013#ifdef WE_CONVERT_TO_YUV
1014 rgb2yuv_32(dst, *src++);
1015#else
1016 uint32_t c = *src++;
1017 dst[0] = SkGetPackedR32(c);
1018 dst[1] = SkGetPackedG32(c);
1019 dst[2] = SkGetPackedB32(c);
1020#endif
1021 dst += 3;
1022 }
1023}
1024
1025static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1026 const void* SK_RESTRICT srcRow, int width,
1027 const SkPMColor*) {
1028 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1029 while (--width >= 0) {
1030#ifdef WE_CONVERT_TO_YUV
1031 rgb2yuv_4444(dst, *src++);
1032#else
1033 SkPMColor16 c = *src++;
1034 dst[0] = SkPacked4444ToR32(c);
1035 dst[1] = SkPacked4444ToG32(c);
1036 dst[2] = SkPacked4444ToB32(c);
1037#endif
1038 dst += 3;
1039 }
1040}
1041
1042static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1043 const void* SK_RESTRICT srcRow, int width,
1044 const SkPMColor*) {
1045 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1046 while (--width >= 0) {
1047#ifdef WE_CONVERT_TO_YUV
1048 rgb2yuv_16(dst, *src++);
1049#else
1050 uint16_t c = *src++;
1051 dst[0] = SkPacked16ToR32(c);
1052 dst[1] = SkPacked16ToG32(c);
1053 dst[2] = SkPacked16ToB32(c);
1054#endif
1055 dst += 3;
1056 }
1057}
1058
1059static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1060 const void* SK_RESTRICT srcRow, int width,
1061 const SkPMColor* SK_RESTRICT ctable) {
1062 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1063 while (--width >= 0) {
1064#ifdef WE_CONVERT_TO_YUV
1065 rgb2yuv_32(dst, ctable[*src++]);
1066#else
1067 uint32_t c = ctable[*src++];
1068 dst[0] = SkGetPackedR32(c);
1069 dst[1] = SkGetPackedG32(c);
1070 dst[2] = SkGetPackedB32(c);
1071#endif
1072 dst += 3;
1073 }
1074}
1075
1076static WriteScanline ChooseWriter(const SkBitmap& bm) {
1077 switch (bm.config()) {
1078 case SkBitmap::kARGB_8888_Config:
1079 return Write_32_YUV;
1080 case SkBitmap::kRGB_565_Config:
1081 return Write_16_YUV;
1082 case SkBitmap::kARGB_4444_Config:
1083 return Write_4444_YUV;
1084 case SkBitmap::kIndex8_Config:
1085 return Write_Index_YUV;
1086 default:
1087 return NULL;
1088 }
1089}
1090
1091class SkJPEGImageEncoder : public SkImageEncoder {
1092protected:
1093 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1094#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001095 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001096#endif
1097
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001098 SkAutoLockPixels alp(bm);
1099 if (NULL == bm.getPixels()) {
1100 return false;
1101 }
1102
1103 jpeg_compress_struct cinfo;
1104 skjpeg_error_mgr sk_err;
1105 skjpeg_destination_mgr sk_wstream(stream);
1106
1107 // allocate these before set call setjmp
1108 SkAutoMalloc oneRow;
1109 SkAutoLockColors ctLocker;
1110
1111 cinfo.err = jpeg_std_error(&sk_err);
1112 sk_err.error_exit = skjpeg_error_exit;
1113 if (setjmp(sk_err.fJmpBuf)) {
1114 return false;
1115 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001116
mtklein@google.com8d725b22013-07-24 16:20:05 +00001117 // Keep after setjmp or mark volatile.
1118 const WriteScanline writer = ChooseWriter(bm);
1119 if (NULL == writer) {
1120 return false;
1121 }
1122
1123 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001124 cinfo.dest = &sk_wstream;
1125 cinfo.image_width = bm.width();
1126 cinfo.image_height = bm.height();
1127 cinfo.input_components = 3;
1128#ifdef WE_CONVERT_TO_YUV
1129 cinfo.in_color_space = JCS_YCbCr;
1130#else
1131 cinfo.in_color_space = JCS_RGB;
1132#endif
1133 cinfo.input_gamma = 1;
1134
1135 jpeg_set_defaults(&cinfo);
1136 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001137#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001138 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001139#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001140
1141 jpeg_start_compress(&cinfo, TRUE);
1142
1143 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001144 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001145
1146 const SkPMColor* colors = ctLocker.lockColors(bm);
1147 const void* srcRow = bm.getPixels();
1148
1149 while (cinfo.next_scanline < cinfo.image_height) {
1150 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1151
1152 writer(oneRowP, srcRow, width, colors);
1153 row_pointer[0] = oneRowP;
1154 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1155 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1156 }
1157
1158 jpeg_finish_compress(&cinfo);
1159 jpeg_destroy_compress(&cinfo);
1160
1161 return true;
1162 }
1163};
1164
1165///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001166DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1167DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1168///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001169
scroggo@google.comb5571b32013-09-25 21:34:24 +00001170static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001171 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001172 static const size_t HEADER_SIZE = sizeof(gHeader);
1173
1174 char buffer[HEADER_SIZE];
1175 size_t len = stream->read(buffer, HEADER_SIZE);
1176
1177 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001178 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001179 }
1180 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001181 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001182 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001183 return true;
1184}
1185
scroggo@google.comb5571b32013-09-25 21:34:24 +00001186
1187static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001188 if (is_jpeg(stream)) {
1189 return SkNEW(SkJPEGImageDecoder);
1190 }
1191 return NULL;
1192}
1193
scroggo@google.comb5571b32013-09-25 21:34:24 +00001194static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001195 if (is_jpeg(stream)) {
1196 return SkImageDecoder::kJPEG_Format;
1197 }
1198 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001199}
1200
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001201static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001202 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1203}
1204
mtklein@google.combd6343b2013-09-04 17:20:18 +00001205static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1206static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1207static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);