blob: 21e96be2d3049e091a48ef1728bf7554522ad684 [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;
sugoib227e372014-10-16 13:10:57 -0700242 virtual bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
243 void* planes[3], size_t rowBytes[3],
244 SkYUVColorSpace* colorSpace) SK_OVERRIDE;
djsollen@google.com11399402013-03-20 17:45:27 +0000245
246private:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000247#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000248 SkJPEGImageIndex* fImageIndex;
249 int fImageWidth;
250 int fImageHeight;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000251#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000252
scroggo@google.com590a5af2013-08-07 21:09:13 +0000253 /**
reed6c225732014-06-09 19:52:07 -0700254 * Determine the appropriate bitmap colortype and out_color_space based on
scroggo@google.com590a5af2013-08-07 21:09:13 +0000255 * both the preference of the caller and the jpeg_color_space on the
256 * jpeg_decompress_struct passed in.
257 * Must be called after jpeg_read_header.
258 */
reed6c225732014-06-09 19:52:07 -0700259 SkColorType getBitmapColorType(jpeg_decompress_struct*);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000260
djsollen@google.com11399402013-03-20 17:45:27 +0000261 typedef SkImageDecoder INHERITED;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000262};
263
264//////////////////////////////////////////////////////////////////////////
265
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000266/* Automatically clean up after throwing an exception */
267class JPEGAutoClean {
268public:
269 JPEGAutoClean(): cinfo_ptr(NULL) {}
270 ~JPEGAutoClean() {
271 if (cinfo_ptr) {
272 jpeg_destroy_decompress(cinfo_ptr);
273 }
274 }
275 void set(jpeg_decompress_struct* info) {
276 cinfo_ptr = info;
277 }
278private:
279 jpeg_decompress_struct* cinfo_ptr;
280};
281
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000282///////////////////////////////////////////////////////////////////////////////
283
284/* If we need to better match the request, we might examine the image and
285 output dimensions, and determine if the downsampling jpeg provided is
286 not sufficient. If so, we can recompute a modified sampleSize value to
287 make up the difference.
288
289 To skip this additional scaling, just set sampleSize = 1; below.
290 */
291static int recompute_sampleSize(int sampleSize,
292 const jpeg_decompress_struct& cinfo) {
293 return sampleSize * cinfo.output_width / cinfo.image_width;
294}
295
296static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
297 /* These are initialized to 0, so if they have non-zero values, we assume
298 they are "valid" (i.e. have been computed by libjpeg)
299 */
djsollen@google.com11399402013-03-20 17:45:27 +0000300 return 0 != cinfo.output_width && 0 != cinfo.output_height;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000301}
302
djsollen@google.com11399402013-03-20 17:45:27 +0000303static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000304 for (int i = 0; i < count; i++) {
305 JSAMPLE* rowptr = (JSAMPLE*)buffer;
306 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
djsollen@google.com11399402013-03-20 17:45:27 +0000307 if (1 != row_count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000308 return false;
309 }
310 }
311 return true;
312}
313
scroggo@google.comd79277f2013-08-07 19:53:53 +0000314#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000315static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
316 huffman_index *index, void* buffer, int count) {
317 for (int i = 0; i < count; i++) {
318 JSAMPLE* rowptr = (JSAMPLE*)buffer;
319 int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
320 if (1 != row_count) {
321 return false;
322 }
323 }
324 return true;
325}
326#endif
327
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000328// This guy exists just to aid in debugging, as it allows debuggers to just
329// set a break-point in one place to see all error exists.
330static bool return_false(const jpeg_decompress_struct& cinfo,
sugoib227e372014-10-16 13:10:57 -0700331 int width, int height, const char caller[]) {
halcanary@google.com04b57f82013-10-14 20:08:48 +0000332 if (!(c_suppressJPEGImageDecoderErrors)) {
333 char buffer[JMSG_LENGTH_MAX];
334 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
335 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
sugoib227e372014-10-16 13:10:57 -0700336 cinfo.err->msg_code, buffer, caller, width, height);
halcanary@google.com04b57f82013-10-14 20:08:48 +0000337 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000338 return false; // must always return false
339}
340
sugoib227e372014-10-16 13:10:57 -0700341static bool return_false(const jpeg_decompress_struct& cinfo,
342 const SkBitmap& bm, const char caller[]) {
343 return return_false(cinfo, bm.width(), bm.height(), caller);
344}
345
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000346// Convert a scanline of CMYK samples to RGBX in place. Note that this
347// method moves the "scanline" pointer in its processing
348static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
349 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000350 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000351 // from easyrgb.com):
352 // CMYK -> CMY
353 // C = ( C * (1 - K) + K ) // for each CMY component
354 // CMY -> RGB
355 // R = ( 1 - C ) * 255 // for each RGB component
356 // Unfortunately we are seeing inverted CMYK so all the original terms
357 // are 1-. This yields:
358 // CMYK -> CMY
359 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
360 // The conversion from CMY->RGB remains the same
361 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
362 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
363 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
364 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
365 scanline[3] = 255;
366 }
367}
368
scroggo@google.com590a5af2013-08-07 21:09:13 +0000369/**
370 * Common code for setting the error manager.
371 */
372static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
373 SkASSERT(cinfo != NULL);
374 SkASSERT(errorManager != NULL);
375 cinfo->err = jpeg_std_error(errorManager);
376 errorManager->error_exit = skjpeg_error_exit;
377}
378
379/**
380 * Common code for turning off upsampling and smoothing. Turning these
381 * off helps performance without showing noticable differences in the
382 * resulting bitmap.
383 */
384static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
385 SkASSERT(cinfo != NULL);
386 /* this gives about 30% performance improvement. In theory it may
387 reduce the visual quality, in practice I'm not seeing a difference
388 */
389 cinfo->do_fancy_upsampling = 0;
390
391 /* this gives another few percents */
392 cinfo->do_block_smoothing = 0;
393}
394
395/**
396 * Common code for setting the dct method.
397 */
398static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
399 SkASSERT(cinfo != NULL);
400#ifdef DCT_IFAST_SUPPORTED
401 if (decoder.getPreferQualityOverSpeed()) {
402 cinfo->dct_method = JDCT_ISLOW;
403 } else {
404 cinfo->dct_method = JDCT_IFAST;
405 }
406#else
407 cinfo->dct_method = JDCT_ISLOW;
408#endif
409}
410
reed6c225732014-06-09 19:52:07 -0700411SkColorType SkJPEGImageDecoder::getBitmapColorType(jpeg_decompress_struct* cinfo) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000412 SkASSERT(cinfo != NULL);
413
414 SrcDepth srcDepth = k32Bit_SrcDepth;
415 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
416 srcDepth = k8BitGray_SrcDepth;
417 }
418
reed6c225732014-06-09 19:52:07 -0700419 SkColorType colorType = this->getPrefColorType(srcDepth, /*hasAlpha*/ false);
420 switch (colorType) {
421 case kAlpha_8_SkColorType:
422 // Only respect A8 colortype if the original is grayscale,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000423 // in which case we will treat the grayscale as alpha
424 // values.
425 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
reed6c225732014-06-09 19:52:07 -0700426 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000427 }
428 break;
reed6c225732014-06-09 19:52:07 -0700429 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000430 // Fall through.
reed6c225732014-06-09 19:52:07 -0700431 case kARGB_4444_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000432 // Fall through.
reed6c225732014-06-09 19:52:07 -0700433 case kRGB_565_SkColorType:
434 // These are acceptable destination colortypes.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000435 break;
436 default:
reed6c225732014-06-09 19:52:07 -0700437 // Force all other colortypes to 8888.
438 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000439 break;
440 }
441
442 switch (cinfo->jpeg_color_space) {
443 case JCS_CMYK:
444 // Fall through.
445 case JCS_YCCK:
446 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
447 // so libjpeg will give us CMYK samples back and we will later
448 // manually convert them to RGB
449 cinfo->out_color_space = JCS_CMYK;
450 break;
451 case JCS_GRAYSCALE:
reed6c225732014-06-09 19:52:07 -0700452 if (kAlpha_8_SkColorType == colorType) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000453 cinfo->out_color_space = JCS_GRAYSCALE;
454 break;
455 }
456 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
reed6c225732014-06-09 19:52:07 -0700457 // colortype. Fall through to set to the default.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000458 default:
459 cinfo->out_color_space = JCS_RGB;
460 break;
461 }
reed6c225732014-06-09 19:52:07 -0700462 return colorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000463}
464
scroggo@google.com590a5af2013-08-07 21:09:13 +0000465/**
reed6c225732014-06-09 19:52:07 -0700466 * Based on the colortype and dither mode, adjust out_color_space and
467 * dither_mode of cinfo. Only does work in ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000468 */
469static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
reed6c225732014-06-09 19:52:07 -0700470 SkColorType colorType,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000471 const SkImageDecoder& decoder) {
472 SkASSERT(cinfo != NULL);
reed6c225732014-06-09 19:52:07 -0700473#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000474 cinfo->dither_mode = JDITHER_NONE;
475 if (JCS_CMYK == cinfo->out_color_space) {
476 return;
477 }
reed6c225732014-06-09 19:52:07 -0700478 switch (colorType) {
479 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000480 cinfo->out_color_space = JCS_RGBA_8888;
481 break;
reed6c225732014-06-09 19:52:07 -0700482 case kRGB_565_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000483 cinfo->out_color_space = JCS_RGB_565;
484 if (decoder.getDitherImage()) {
485 cinfo->dither_mode = JDITHER_ORDERED;
486 }
487 break;
488 default:
489 break;
490 }
scroggo@google.com590a5af2013-08-07 21:09:13 +0000491#endif
reed6c225732014-06-09 19:52:07 -0700492}
scroggo@google.com590a5af2013-08-07 21:09:13 +0000493
halcanary@google.comfed30372013-10-04 12:46:45 +0000494
495/**
496 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
497 Used when decoding fails partway through reading scanlines to fill
498 remaining lines. */
499static void fill_below_level(int y, SkBitmap* bitmap) {
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000500 SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
halcanary@google.comfed30372013-10-04 12:46:45 +0000501 SkCanvas canvas(*bitmap);
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000502 canvas.clipRect(SkRect::Make(rect));
halcanary@google.comfed30372013-10-04 12:46:45 +0000503 canvas.drawColor(SK_ColorWHITE);
504}
505
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000506/**
507 * Get the config and bytes per pixel of the source data. Return
508 * whether the data is supported.
509 */
510static bool get_src_config(const jpeg_decompress_struct& cinfo,
511 SkScaledBitmapSampler::SrcConfig* sc,
512 int* srcBytesPerPixel) {
513 SkASSERT(sc != NULL && srcBytesPerPixel != NULL);
514 if (JCS_CMYK == cinfo.out_color_space) {
515 // In this case we will manually convert the CMYK values to RGB
516 *sc = SkScaledBitmapSampler::kRGBX;
517 // The CMYK work-around relies on 4 components per pixel here
518 *srcBytesPerPixel = 4;
519 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
520 *sc = SkScaledBitmapSampler::kRGB;
521 *srcBytesPerPixel = 3;
522#ifdef ANDROID_RGB
523 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
524 *sc = SkScaledBitmapSampler::kRGBX;
525 *srcBytesPerPixel = 4;
526 } else if (JCS_RGB_565 == cinfo.out_color_space) {
527 *sc = SkScaledBitmapSampler::kRGB_565;
528 *srcBytesPerPixel = 2;
529#endif
530 } else if (1 == cinfo.out_color_components &&
531 JCS_GRAYSCALE == cinfo.out_color_space) {
532 *sc = SkScaledBitmapSampler::kGray;
533 *srcBytesPerPixel = 1;
534 } else {
535 return false;
536 }
537 return true;
538}
halcanary@google.comfed30372013-10-04 12:46:45 +0000539
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000540bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
541#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000542 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000543#endif
544
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000545 JPEGAutoClean autoClean;
546
547 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000548 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000549
scroggo@google.com590a5af2013-08-07 21:09:13 +0000550 skjpeg_error_mgr errorManager;
551 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000552
553 // All objects need to be instantiated before this setjmp call so that
554 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000555 if (setjmp(errorManager.fJmpBuf)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000556 return return_false(cinfo, *bm, "setjmp");
557 }
558
scroggo@google.com590a5af2013-08-07 21:09:13 +0000559 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000560 autoClean.set(&cinfo);
561
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000562 int status = jpeg_read_header(&cinfo, true);
563 if (status != JPEG_HEADER_OK) {
564 return return_false(cinfo, *bm, "read_header");
565 }
566
567 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
568 can) much faster that we, just use their num/denom api to approximate
569 the size.
570 */
571 int sampleSize = this->getSampleSize();
572
scroggo@google.com590a5af2013-08-07 21:09:13 +0000573 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000574
scroggo@google.com590a5af2013-08-07 21:09:13 +0000575 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000576 cinfo.scale_denom = sampleSize;
577
scroggo@google.com590a5af2013-08-07 21:09:13 +0000578 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000579
reed6c225732014-06-09 19:52:07 -0700580 const SkColorType colorType = this->getBitmapColorType(&cinfo);
581 const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ?
582 kPremul_SkAlphaType : kOpaque_SkAlphaType;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000583
reed6c225732014-06-09 19:52:07 -0700584 adjust_out_color_space_and_dither(&cinfo, colorType, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000585
djsollen@google.com11399402013-03-20 17:45:27 +0000586 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000587 // Assume an A8 bitmap is not opaque to avoid the check of each
588 // individual pixel. It is very unlikely to be opaque, since
589 // an opaque A8 bitmap would not be very interesting.
590 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700591 return bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
592 colorType, alphaType));
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000593 }
594
595 /* image_width and image_height are the original dimensions, available
596 after jpeg_read_header(). To see the scaled dimensions, we have to call
597 jpeg_start_decompress(), and then read output_width and output_height.
598 */
599 if (!jpeg_start_decompress(&cinfo)) {
600 /* If we failed here, we may still have enough information to return
601 to the caller if they just wanted (subsampled bounds). If sampleSize
602 was 1, then we would have already returned. Thus we just check if
603 we're in kDecodeBounds_Mode, and that we have valid output sizes.
604
605 One reason to fail here is that we have insufficient stream data
606 to complete the setup. However, output dimensions seem to get
607 computed very early, which is why this special check can pay off.
608 */
djsollen@google.com11399402013-03-20 17:45:27 +0000609 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000610 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
611 recompute_sampleSize(sampleSize, cinfo));
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000612 // Assume an A8 bitmap is not opaque to avoid the check of each
613 // individual pixel. It is very unlikely to be opaque, since
614 // an opaque A8 bitmap would not be very interesting.
615 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700616 return bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
617 colorType, alphaType));
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000618 } else {
619 return return_false(cinfo, *bm, "start_decompress");
620 }
621 }
622 sampleSize = recompute_sampleSize(sampleSize, cinfo);
623
reed5926b862014-06-11 10:33:13 -0700624#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed6c225732014-06-09 19:52:07 -0700625 // should we allow the Chooser (if present) to pick a colortype for us???
626 if (!this->chooseFromOneChoice(colorType, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000627 return return_false(cinfo, *bm, "chooseFromOneChoice");
628 }
reed5926b862014-06-11 10:33:13 -0700629#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000630
djsollen@google.com11399402013-03-20 17:45:27 +0000631 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000632 // Assume an A8 bitmap is not opaque to avoid the check of each
633 // individual pixel. It is very unlikely to be opaque, since
634 // an opaque A8 bitmap would not be very interesting.
635 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700636 bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
637 colorType, alphaType));
scroggo@google.combc69ce92013-07-09 15:45:14 +0000638 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
639 return true;
640 }
641 if (!this->allocPixelRef(bm, NULL)) {
642 return return_false(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000643 }
644
645 SkAutoLockPixels alp(*bm);
646
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000647#ifdef ANDROID_RGB
648 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
649 a significant performance boost.
650 */
651 if (sampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700652 ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||
653 (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000654 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000655 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000656 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000657
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000658 while (cinfo.output_scanline < cinfo.output_height) {
659 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000660 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000661 // if row_count == 0, then we didn't get a scanline,
662 // so return early. We will return a partial image.
663 fill_below_level(cinfo.output_scanline, bm);
664 cinfo.output_scanline = cinfo.output_height;
665 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000666 }
667 if (this->shouldCancelDecode()) {
668 return return_false(cinfo, *bm, "shouldCancelDecode");
669 }
670 rowptr += bpr;
671 }
672 jpeg_finish_decompress(&cinfo);
673 return true;
674 }
675#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000676
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000677 // check for supported formats
678 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000679 int srcBytesPerPixel;
680
681 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000682 return return_false(cinfo, *bm, "jpeg colorspace");
683 }
684
scroggo@google.com8d239242013-10-01 17:27:15 +0000685 if (!sampler.begin(bm, sc, *this)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000686 return return_false(cinfo, *bm, "sampler.begin");
687 }
688
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000689 SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000690 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000691
692 // Possibly skip initial rows [sampler.srcY0]
693 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
694 return return_false(cinfo, *bm, "skip rows");
695 }
696
697 // now loop through scanlines until y == bm->height() - 1
698 for (int y = 0;; y++) {
699 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
700 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
701 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000702 // if row_count == 0, then we didn't get a scanline,
703 // so return early. We will return a partial image.
704 fill_below_level(y, bm);
705 cinfo.output_scanline = cinfo.output_height;
706 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000707 }
708 if (this->shouldCancelDecode()) {
709 return return_false(cinfo, *bm, "shouldCancelDecode");
710 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000711
712 if (JCS_CMYK == cinfo.out_color_space) {
713 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
714 }
715
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000716 sampler.next(srcRow);
717 if (bm->height() - 1 == y) {
718 // we're done
719 break;
720 }
721
722 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
723 return return_false(cinfo, *bm, "skip rows");
724 }
725 }
726
727 // we formally skip the rest, so we don't get a complaint from libjpeg
728 if (!skip_src_rows(&cinfo, srcRow,
729 cinfo.output_height - cinfo.output_scanline)) {
730 return return_false(cinfo, *bm, "skip rows");
731 }
732 jpeg_finish_decompress(&cinfo);
733
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000734 return true;
735}
736
sugoib227e372014-10-16 13:10:57 -0700737enum SizeType {
738 kSizeForMemoryAllocation_SizeType,
739 kActualSize_SizeType
740};
741
742static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component,
743 SizeType sizeType) {
744 if (sizeType == kSizeForMemoryAllocation_SizeType) {
745 return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE,
746 info.cur_comp_info[component]->height_in_blocks * DCTSIZE);
747 }
748 return SkISize::Make(info.cur_comp_info[component]->downsampled_width,
749 info.cur_comp_info[component]->downsampled_height);
750}
751
752// Enum for YUV decoding
753enum YUVSubsampling {
754 kUNKNOWN_YUVSubsampling,
755 k410_YUVSubsampling,
756 k411_YUVSubsampling,
757 k420_YUVSubsampling,
758 k422_YUVSubsampling,
759 k440_YUVSubsampling,
760 k444_YUVSubsampling
761};
762
763static YUVSubsampling yuv_subsampling(const jpeg_decompress_struct& info) {
764 if ((DCTSIZE == 8)
765 && (info.num_components == 3)
766 && (info.comps_in_scan >= info.num_components)
767 && (info.scale_denom <= 8)
768 && (info.cur_comp_info[0])
769 && (info.cur_comp_info[1])
770 && (info.cur_comp_info[2])
771 && (info.cur_comp_info[1]->h_samp_factor == 1)
772 && (info.cur_comp_info[1]->v_samp_factor == 1)
773 && (info.cur_comp_info[2]->h_samp_factor == 1)
774 && (info.cur_comp_info[2]->v_samp_factor == 1))
775 {
776 int h = info.cur_comp_info[0]->h_samp_factor;
777 int v = info.cur_comp_info[0]->v_samp_factor;
778 // 4:4:4 : (h == 1) && (v == 1)
779 // 4:4:0 : (h == 1) && (v == 2)
780 // 4:2:2 : (h == 2) && (v == 1)
781 // 4:2:0 : (h == 2) && (v == 2)
782 // 4:1:1 : (h == 4) && (v == 1)
783 // 4:1:0 : (h == 4) && (v == 2)
784 if (v == 1) {
785 switch (h) {
786 case 1:
787 return k444_YUVSubsampling;
788 case 2:
789 return k422_YUVSubsampling;
790 case 4:
791 return k411_YUVSubsampling;
792 default:
793 break;
794 }
795 } else if (v == 2) {
796 switch (h) {
797 case 1:
798 return k440_YUVSubsampling;
799 case 2:
800 return k420_YUVSubsampling;
801 case 4:
802 return k410_YUVSubsampling;
803 default:
804 break;
805 }
806 }
807 }
808
809 return kUNKNOWN_YUVSubsampling;
810}
811
812static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
813 SizeType sizeType) {
814 for (int i = 0; i < 3; ++i) {
815 componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
816 }
817}
818
819static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) {
820 // U size and V size have to be the same if we're calling output_raw_data()
821 SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType);
822 SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType));
823
824 JSAMPARRAY bufferraw[3];
825 JSAMPROW bufferraw2[32];
826 bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16)
827 bufferraw[1] = &bufferraw2[16]; // U channel rows (8)
828 bufferraw[2] = &bufferraw2[24]; // V channel rows (8)
829 int yWidth = cinfo.output_width;
830 int yHeight = cinfo.output_height;
831 int yMaxH = yHeight - 1;
832 int v = cinfo.cur_comp_info[0]->v_samp_factor;
833 int uvMaxH = uvSize.height() - 1;
834 JSAMPROW outputY = static_cast<JSAMPROW>(planes[0]);
835 JSAMPROW outputU = static_cast<JSAMPROW>(planes[1]);
836 JSAMPROW outputV = static_cast<JSAMPROW>(planes[2]);
837 size_t rowBytesY = rowBytes[0];
838 size_t rowBytesU = rowBytes[1];
839 size_t rowBytesV = rowBytes[2];
840
841 int yScanlinesToRead = DCTSIZE * v;
842 SkAutoMalloc lastRowStorage(yWidth * 8);
843 JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get();
844 JSAMPROW uLastRow = yLastRow + 2 * yWidth;
845 JSAMPROW vLastRow = uLastRow + 2 * yWidth;
846 JSAMPROW dummyRow = vLastRow + 2 * yWidth;
847
848 while (cinfo.output_scanline < cinfo.output_height) {
849 // Request 8 or 16 scanlines: returns 0 or more scanlines.
850 bool hasYLastRow(false), hasUVLastRow(false);
851 // Assign 8 or 16 rows of memory to read the Y channel.
852 for (int i = 0; i < yScanlinesToRead; ++i) {
853 int scanline = (cinfo.output_scanline + i);
854 if (scanline < yMaxH) {
855 bufferraw2[i] = &outputY[scanline * rowBytesY];
856 } else if (scanline == yMaxH) {
857 bufferraw2[i] = yLastRow;
858 hasYLastRow = true;
859 } else {
860 bufferraw2[i] = dummyRow;
861 }
862 }
863 int scaledScanline = cinfo.output_scanline / v;
864 // Assign 8 rows of memory to read the U and V channels.
865 for (int i = 0; i < 8; ++i) {
866 int scanline = (scaledScanline + i);
867 if (scanline < uvMaxH) {
868 bufferraw2[16 + i] = &outputU[scanline * rowBytesU];
869 bufferraw2[24 + i] = &outputV[scanline * rowBytesV];
870 } else if (scanline == uvMaxH) {
871 bufferraw2[16 + i] = uLastRow;
872 bufferraw2[24 + i] = vLastRow;
873 hasUVLastRow = true;
874 } else {
875 bufferraw2[16 + i] = dummyRow;
876 bufferraw2[24 + i] = dummyRow;
877 }
878 }
879 JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead);
880
881 if (scanlinesRead == 0)
882 return false;
883
884 if (hasYLastRow) {
885 memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth);
886 }
887 if (hasUVLastRow) {
888 memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width());
889 memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width());
890 }
891 }
892
893 cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height);
894
895 return true;
896}
897
898bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
899 void* planes[3], size_t rowBytes[3],
900 SkYUVColorSpace* colorSpace) {
901#ifdef TIME_DECODE
902 SkAutoTime atm("JPEG YUV8 Decode");
903#endif
904
905 if (this->getSampleSize() != 1) {
906 return false; // Resizing not supported
907 }
908
909 JPEGAutoClean autoClean;
910
911 jpeg_decompress_struct cinfo;
912 skjpeg_source_mgr srcManager(stream, this);
913
914 skjpeg_error_mgr errorManager;
915 set_error_mgr(&cinfo, &errorManager);
916
917 // All objects need to be instantiated before this setjmp call so that
918 // they will be cleaned up properly if an error occurs.
919 if (setjmp(errorManager.fJmpBuf)) {
920 return return_false(cinfo, 0, 0, "setjmp YUV8");
921 }
922
923 initialize_info(&cinfo, &srcManager);
924 autoClean.set(&cinfo);
925
926 int status = jpeg_read_header(&cinfo, true);
927 if (status != JPEG_HEADER_OK) {
928 return return_false(cinfo, 0, 0, "read_header YUV8");
929 }
930
931 if (cinfo.jpeg_color_space != JCS_YCbCr) {
932 // It's not an error to not be encoded in YUV, so no need to use return_false()
933 return false;
934 }
935
936 cinfo.out_color_space = JCS_YCbCr;
937 cinfo.raw_data_out = TRUE;
938
939 if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only
940 update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType);
941 return true;
942 }
943
944 set_dct_method(*this, &cinfo);
945
946 SkASSERT(1 == cinfo.scale_num);
947 cinfo.scale_denom = 1;
948
949 turn_off_visual_optimizations(&cinfo);
950
951#ifdef ANDROID_RGB
952 cinfo.dither_mode = JDITHER_NONE;
953#endif
954
955 /* image_width and image_height are the original dimensions, available
956 after jpeg_read_header(). To see the scaled dimensions, we have to call
957 jpeg_start_decompress(), and then read output_width and output_height.
958 */
959 if (!jpeg_start_decompress(&cinfo)) {
960 return return_false(cinfo, 0, 0, "start_decompress YUV8");
961 }
962
963 if (!output_raw_data(cinfo, planes, rowBytes)) {
964 return return_false(cinfo, 0, 0, "output_raw_data");
965 }
966
967 update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
968 jpeg_finish_decompress(&cinfo);
969
970 if (NULL != colorSpace) {
971 *colorSpace = kJPEG_SkYUVColorSpace;
972 }
973
974 return true;
975}
976
scroggo@google.comd79277f2013-08-07 19:53:53 +0000977#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000978bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000979
scroggo@google.coma1a51542013-08-07 21:02:32 +0000980 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000981 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000982
983 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000984 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000985
986 // All objects need to be instantiated before this setjmp call so that
987 // they will be cleaned up properly if an error occurs.
988 if (setjmp(sk_err.fJmpBuf)) {
989 return false;
990 }
991
992 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000993 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000994 return false;
995 }
996
scroggo@google.coma1a51542013-08-07 21:02:32 +0000997 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000998 return false;
999 }
1000
1001 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +00001002 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +00001003
1004 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +00001005 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +00001006 return false;
1007 }
1008
scroggo@google.com590a5af2013-08-07 21:09:13 +00001009 // FIXME: This sets cinfo->out_color_space, which we may change later
1010 // based on the config in onDecodeSubset. This should be fine, since
1011 // jpeg_init_read_tile_scanline will check out_color_space again after
1012 // that change (when it calls jinit_color_deconverter).
reed6c225732014-06-09 19:52:07 -07001013 (void) this->getBitmapColorType(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +00001014
1015 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +00001016
1017 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +00001018 if (!imageIndex->startTileDecompress()) {
1019 return false;
1020 }
djsollen@google.com11399402013-03-20 17:45:27 +00001021
scroggo@google.coma1a51542013-08-07 21:02:32 +00001022 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +00001023 fImageWidth = cinfo->output_width;
1024 fImageHeight = cinfo->output_height;
1025
1026 if (width) {
1027 *width = fImageWidth;
1028 }
1029 if (height) {
1030 *height = fImageHeight;
1031 }
djsollen@google.com11399402013-03-20 17:45:27 +00001032
1033 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +00001034 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +00001035
1036 return true;
1037}
1038
scroggo@google.com7e6fcee2013-05-03 20:14:28 +00001039bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +00001040 if (NULL == fImageIndex) {
1041 return false;
1042 }
1043 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
1044
1045 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
1046 if (!rect.intersect(region)) {
1047 // If the requested region is entirely outside the image return false
1048 return false;
1049 }
1050
1051
1052 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +00001053 set_error_mgr(cinfo, &errorManager);
1054
djsollen@google.com11399402013-03-20 17:45:27 +00001055 if (setjmp(errorManager.fJmpBuf)) {
1056 return false;
1057 }
1058
1059 int requestedSampleSize = this->getSampleSize();
1060 cinfo->scale_denom = requestedSampleSize;
1061
scroggo@google.com590a5af2013-08-07 21:09:13 +00001062 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +00001063
reed6c225732014-06-09 19:52:07 -07001064 const SkColorType colorType = this->getBitmapColorType(cinfo);
1065 adjust_out_color_space_and_dither(cinfo, colorType, *this);
djsollen@google.com11399402013-03-20 17:45:27 +00001066
1067 int startX = rect.fLeft;
1068 int startY = rect.fTop;
1069 int width = rect.width();
1070 int height = rect.height();
1071
1072 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
1073 &startX, &startY, &width, &height);
1074 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
1075 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
1076
1077 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
1078
1079 SkBitmap bitmap;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001080 // Assume an A8 bitmap is not opaque to avoid the check of each
1081 // individual pixel. It is very unlikely to be opaque, since
1082 // an opaque A8 bitmap would not be very interesting.
1083 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -07001084 bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType,
1085 kAlpha_8_SkColorType == colorType ?
1086 kPremul_SkAlphaType : kOpaque_SkAlphaType));
djsollen@google.com11399402013-03-20 17:45:27 +00001087
1088 // Check ahead of time if the swap(dest, src) is possible or not.
1089 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
1090 // swap happening. If no, then we will use alloc to allocate pixels to
1091 // prevent garbage collection.
1092 int w = rect.width() / actualSampleSize;
1093 int h = rect.height() / actualSampleSize;
1094 bool swapOnly = (rect == region) && bm->isNull() &&
1095 (w == bitmap.width()) && (h == bitmap.height()) &&
1096 ((startX - rect.x()) / actualSampleSize == 0) &&
1097 ((startY - rect.y()) / actualSampleSize == 0);
1098 if (swapOnly) {
1099 if (!this->allocPixelRef(&bitmap, NULL)) {
1100 return return_false(*cinfo, bitmap, "allocPixelRef");
1101 }
1102 } else {
reed84825042014-09-02 12:50:45 -07001103 if (!bitmap.tryAllocPixels()) {
djsollen@google.com11399402013-03-20 17:45:27 +00001104 return return_false(*cinfo, bitmap, "allocPixels");
1105 }
1106 }
1107
1108 SkAutoLockPixels alp(bitmap);
1109
1110#ifdef ANDROID_RGB
1111 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
1112 a significant performance boost.
1113 */
1114 if (skiaSampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -07001115 ((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||
1116 (kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565)))
djsollen@google.com11399402013-03-20 17:45:27 +00001117 {
1118 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
1119 INT32 const bpr = bitmap.rowBytes();
1120 int rowTotalCount = 0;
1121
1122 while (rowTotalCount < height) {
1123 int rowCount = jpeg_read_tile_scanline(cinfo,
1124 fImageIndex->huffmanIndex(),
1125 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001126 // if rowCount == 0, then we didn't get a scanline, so abort.
1127 // onDecodeSubset() relies on onBuildTileIndex(), which
1128 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001129 if (0 == rowCount) {
1130 return return_false(*cinfo, bitmap, "read_scanlines");
1131 }
1132 if (this->shouldCancelDecode()) {
1133 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1134 }
1135 rowTotalCount += rowCount;
1136 rowptr += bpr;
1137 }
1138
1139 if (swapOnly) {
1140 bm->swap(bitmap);
1141 } else {
1142 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1143 region.width(), region.height(), startX, startY);
1144 }
1145 return true;
1146 }
1147#endif
1148
1149 // check for supported formats
1150 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001151 int srcBytesPerPixel;
1152
1153 if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001154 return return_false(*cinfo, *bm, "jpeg colorspace");
1155 }
1156
scroggo@google.com8d239242013-10-01 17:27:15 +00001157 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001158 return return_false(*cinfo, bitmap, "sampler.begin");
1159 }
1160
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001161 SkAutoMalloc srcStorage(width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +00001162 uint8_t* srcRow = (uint8_t*)srcStorage.get();
1163
1164 // Possibly skip initial rows [sampler.srcY0]
1165 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
1166 return return_false(*cinfo, bitmap, "skip rows");
1167 }
1168
1169 // now loop through scanlines until y == bitmap->height() - 1
1170 for (int y = 0;; y++) {
1171 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
1172 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001173 // if row_count == 0, then we didn't get a scanline, so abort.
1174 // onDecodeSubset() relies on onBuildTileIndex(), which
1175 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001176 if (0 == row_count) {
1177 return return_false(*cinfo, bitmap, "read_scanlines");
1178 }
1179 if (this->shouldCancelDecode()) {
1180 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1181 }
1182
1183 if (JCS_CMYK == cinfo->out_color_space) {
1184 convert_CMYK_to_RGB(srcRow, width);
1185 }
1186
1187 sampler.next(srcRow);
1188 if (bitmap.height() - 1 == y) {
1189 // we're done
1190 break;
1191 }
1192
1193 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
1194 sampler.srcDY() - 1)) {
1195 return return_false(*cinfo, bitmap, "skip rows");
1196 }
1197 }
1198 if (swapOnly) {
1199 bm->swap(bitmap);
1200 } else {
1201 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1202 region.width(), region.height(), startX, startY);
1203 }
1204 return true;
1205}
1206#endif
1207
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001208///////////////////////////////////////////////////////////////////////////////
1209
1210#include "SkColorPriv.h"
1211
1212// taken from jcolor.c in libjpeg
1213#if 0 // 16bit - precise but slow
1214 #define CYR 19595 // 0.299
1215 #define CYG 38470 // 0.587
1216 #define CYB 7471 // 0.114
1217
1218 #define CUR -11059 // -0.16874
1219 #define CUG -21709 // -0.33126
1220 #define CUB 32768 // 0.5
1221
1222 #define CVR 32768 // 0.5
1223 #define CVG -27439 // -0.41869
1224 #define CVB -5329 // -0.08131
1225
1226 #define CSHIFT 16
1227#else // 8bit - fast, slightly less precise
1228 #define CYR 77 // 0.299
1229 #define CYG 150 // 0.587
1230 #define CYB 29 // 0.114
1231
1232 #define CUR -43 // -0.16874
1233 #define CUG -85 // -0.33126
1234 #define CUB 128 // 0.5
1235
1236 #define CVR 128 // 0.5
1237 #define CVG -107 // -0.41869
1238 #define CVB -21 // -0.08131
1239
1240 #define CSHIFT 8
1241#endif
1242
1243static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
1244 int r = SkGetPackedR32(c);
1245 int g = SkGetPackedG32(c);
1246 int b = SkGetPackedB32(c);
1247
1248 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
1249 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
1250 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
1251
1252 dst[0] = SkToU8(y);
1253 dst[1] = SkToU8(u + 128);
1254 dst[2] = SkToU8(v + 128);
1255}
1256
1257static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
1258 int r = SkGetPackedR4444(c);
1259 int g = SkGetPackedG4444(c);
1260 int b = SkGetPackedB4444(c);
1261
1262 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
1263 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
1264 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
1265
1266 dst[0] = SkToU8(y);
1267 dst[1] = SkToU8(u + 128);
1268 dst[2] = SkToU8(v + 128);
1269}
1270
1271static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
1272 int r = SkGetPackedR16(c);
1273 int g = SkGetPackedG16(c);
1274 int b = SkGetPackedB16(c);
1275
1276 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
1277 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
1278 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
1279
1280 dst[0] = SkToU8(y);
1281 dst[1] = SkToU8(u + 128);
1282 dst[2] = SkToU8(v + 128);
1283}
1284
1285///////////////////////////////////////////////////////////////////////////////
1286
1287typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1288 const void* SK_RESTRICT src, int width,
1289 const SkPMColor* SK_RESTRICT ctable);
1290
1291static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1292 const void* SK_RESTRICT srcRow, int width,
1293 const SkPMColor*) {
1294 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1295 while (--width >= 0) {
1296#ifdef WE_CONVERT_TO_YUV
1297 rgb2yuv_32(dst, *src++);
1298#else
1299 uint32_t c = *src++;
1300 dst[0] = SkGetPackedR32(c);
1301 dst[1] = SkGetPackedG32(c);
1302 dst[2] = SkGetPackedB32(c);
1303#endif
1304 dst += 3;
1305 }
1306}
1307
1308static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1309 const void* SK_RESTRICT srcRow, int width,
1310 const SkPMColor*) {
1311 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1312 while (--width >= 0) {
1313#ifdef WE_CONVERT_TO_YUV
1314 rgb2yuv_4444(dst, *src++);
1315#else
1316 SkPMColor16 c = *src++;
1317 dst[0] = SkPacked4444ToR32(c);
1318 dst[1] = SkPacked4444ToG32(c);
1319 dst[2] = SkPacked4444ToB32(c);
1320#endif
1321 dst += 3;
1322 }
1323}
1324
1325static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1326 const void* SK_RESTRICT srcRow, int width,
1327 const SkPMColor*) {
1328 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1329 while (--width >= 0) {
1330#ifdef WE_CONVERT_TO_YUV
1331 rgb2yuv_16(dst, *src++);
1332#else
1333 uint16_t c = *src++;
1334 dst[0] = SkPacked16ToR32(c);
1335 dst[1] = SkPacked16ToG32(c);
1336 dst[2] = SkPacked16ToB32(c);
1337#endif
1338 dst += 3;
1339 }
1340}
1341
1342static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1343 const void* SK_RESTRICT srcRow, int width,
1344 const SkPMColor* SK_RESTRICT ctable) {
1345 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1346 while (--width >= 0) {
1347#ifdef WE_CONVERT_TO_YUV
1348 rgb2yuv_32(dst, ctable[*src++]);
1349#else
1350 uint32_t c = ctable[*src++];
1351 dst[0] = SkGetPackedR32(c);
1352 dst[1] = SkGetPackedG32(c);
1353 dst[2] = SkGetPackedB32(c);
1354#endif
1355 dst += 3;
1356 }
1357}
1358
1359static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -07001360 switch (bm.colorType()) {
1361 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001362 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -07001363 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001364 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -07001365 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001366 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -07001367 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001368 return Write_Index_YUV;
1369 default:
1370 return NULL;
1371 }
1372}
1373
1374class SkJPEGImageEncoder : public SkImageEncoder {
1375protected:
1376 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1377#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001378 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001379#endif
1380
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001381 SkAutoLockPixels alp(bm);
1382 if (NULL == bm.getPixels()) {
1383 return false;
1384 }
1385
1386 jpeg_compress_struct cinfo;
1387 skjpeg_error_mgr sk_err;
1388 skjpeg_destination_mgr sk_wstream(stream);
1389
1390 // allocate these before set call setjmp
1391 SkAutoMalloc oneRow;
1392 SkAutoLockColors ctLocker;
1393
1394 cinfo.err = jpeg_std_error(&sk_err);
1395 sk_err.error_exit = skjpeg_error_exit;
1396 if (setjmp(sk_err.fJmpBuf)) {
1397 return false;
1398 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001399
mtklein@google.com8d725b22013-07-24 16:20:05 +00001400 // Keep after setjmp or mark volatile.
1401 const WriteScanline writer = ChooseWriter(bm);
1402 if (NULL == writer) {
1403 return false;
1404 }
1405
1406 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001407 cinfo.dest = &sk_wstream;
1408 cinfo.image_width = bm.width();
1409 cinfo.image_height = bm.height();
1410 cinfo.input_components = 3;
1411#ifdef WE_CONVERT_TO_YUV
1412 cinfo.in_color_space = JCS_YCbCr;
1413#else
1414 cinfo.in_color_space = JCS_RGB;
1415#endif
1416 cinfo.input_gamma = 1;
1417
1418 jpeg_set_defaults(&cinfo);
1419 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001420#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001421 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001422#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001423
1424 jpeg_start_compress(&cinfo, TRUE);
1425
1426 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001427 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001428
1429 const SkPMColor* colors = ctLocker.lockColors(bm);
1430 const void* srcRow = bm.getPixels();
1431
1432 while (cinfo.next_scanline < cinfo.image_height) {
1433 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1434
1435 writer(oneRowP, srcRow, width, colors);
1436 row_pointer[0] = oneRowP;
1437 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1438 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1439 }
1440
1441 jpeg_finish_compress(&cinfo);
1442 jpeg_destroy_compress(&cinfo);
1443
1444 return true;
1445 }
1446};
1447
1448///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001449DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1450DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1451///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001452
scroggo@google.comb5571b32013-09-25 21:34:24 +00001453static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001454 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001455 static const size_t HEADER_SIZE = sizeof(gHeader);
1456
1457 char buffer[HEADER_SIZE];
1458 size_t len = stream->read(buffer, HEADER_SIZE);
1459
1460 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001461 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001462 }
1463 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001464 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001465 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001466 return true;
1467}
1468
scroggo@google.comb5571b32013-09-25 21:34:24 +00001469
1470static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001471 if (is_jpeg(stream)) {
1472 return SkNEW(SkJPEGImageDecoder);
1473 }
1474 return NULL;
1475}
1476
scroggo@google.comb5571b32013-09-25 21:34:24 +00001477static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001478 if (is_jpeg(stream)) {
1479 return SkImageDecoder::kJPEG_Format;
1480 }
1481 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001482}
1483
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001484static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001485 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1486}
1487
mtklein@google.combd6343b2013-09-04 17:20:18 +00001488static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1489static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1490static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);