blob: 8c9d26783ca40c82ab4be6ae7d5fc0569d2bdf65 [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
mtklein72c9faa2015-01-09 10:06:39 -0800232 Format getFormat() const SK_OVERRIDE {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000233 return kJPEG_Format;
234 }
235
236protected:
scroggo@google.comd79277f2013-08-07 19:53:53 +0000237#ifdef SK_BUILD_FOR_ANDROID
mtklein72c9faa2015-01-09 10:06:39 -0800238 bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
239 bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
djsollen@google.com11399402013-03-20 17:45:27 +0000240#endif
mtklein72c9faa2015-01-09 10:06:39 -0800241 Result 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
scroggo2a120802014-10-22 12:07:00 -0700328///////////////////////////////////////////////////////////////////////////////
329
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000330// This guy exists just to aid in debugging, as it allows debuggers to just
331// set a break-point in one place to see all error exists.
scroggo2a120802014-10-22 12:07:00 -0700332static void print_jpeg_decoder_errors(const jpeg_decompress_struct& cinfo,
sugoib227e372014-10-16 13:10:57 -0700333 int width, int height, const char caller[]) {
halcanary@google.com04b57f82013-10-14 20:08:48 +0000334 if (!(c_suppressJPEGImageDecoderErrors)) {
335 char buffer[JMSG_LENGTH_MAX];
336 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
337 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
sugoib227e372014-10-16 13:10:57 -0700338 cinfo.err->msg_code, buffer, caller, width, height);
halcanary@google.com04b57f82013-10-14 20:08:48 +0000339 }
scroggo2a120802014-10-22 12:07:00 -0700340}
341
342static bool return_false(const jpeg_decompress_struct& cinfo,
343 const char caller[]) {
344 print_jpeg_decoder_errors(cinfo, 0, 0, caller);
345 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000346}
347
mtkleindf78d0a2014-10-23 10:13:21 -0700348#ifdef SK_BUILD_FOR_ANDROID
sugoib227e372014-10-16 13:10:57 -0700349static bool return_false(const jpeg_decompress_struct& cinfo,
350 const SkBitmap& bm, const char caller[]) {
scroggo2a120802014-10-22 12:07:00 -0700351 print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
352 return false;
sugoib227e372014-10-16 13:10:57 -0700353}
mtkleindf78d0a2014-10-23 10:13:21 -0700354#endif
sugoib227e372014-10-16 13:10:57 -0700355
scroggo2a120802014-10-22 12:07:00 -0700356static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo,
357 const SkBitmap& bm, const char caller[]) {
358 print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
359 return SkImageDecoder::kFailure;
360}
361
362///////////////////////////////////////////////////////////////////////////////
363
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000364// Convert a scanline of CMYK samples to RGBX in place. Note that this
365// method moves the "scanline" pointer in its processing
366static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
367 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000368 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000369 // from easyrgb.com):
370 // CMYK -> CMY
371 // C = ( C * (1 - K) + K ) // for each CMY component
372 // CMY -> RGB
373 // R = ( 1 - C ) * 255 // for each RGB component
374 // Unfortunately we are seeing inverted CMYK so all the original terms
375 // are 1-. This yields:
376 // CMYK -> CMY
377 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
378 // The conversion from CMY->RGB remains the same
379 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
380 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
381 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
382 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
383 scanline[3] = 255;
384 }
385}
386
scroggo@google.com590a5af2013-08-07 21:09:13 +0000387/**
388 * Common code for setting the error manager.
389 */
390static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
391 SkASSERT(cinfo != NULL);
392 SkASSERT(errorManager != NULL);
393 cinfo->err = jpeg_std_error(errorManager);
394 errorManager->error_exit = skjpeg_error_exit;
395}
396
397/**
398 * Common code for turning off upsampling and smoothing. Turning these
399 * off helps performance without showing noticable differences in the
400 * resulting bitmap.
401 */
402static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
403 SkASSERT(cinfo != NULL);
404 /* this gives about 30% performance improvement. In theory it may
405 reduce the visual quality, in practice I'm not seeing a difference
406 */
407 cinfo->do_fancy_upsampling = 0;
408
409 /* this gives another few percents */
410 cinfo->do_block_smoothing = 0;
411}
412
413/**
414 * Common code for setting the dct method.
415 */
416static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
417 SkASSERT(cinfo != NULL);
418#ifdef DCT_IFAST_SUPPORTED
419 if (decoder.getPreferQualityOverSpeed()) {
420 cinfo->dct_method = JDCT_ISLOW;
421 } else {
422 cinfo->dct_method = JDCT_IFAST;
423 }
424#else
425 cinfo->dct_method = JDCT_ISLOW;
426#endif
427}
428
reed6c225732014-06-09 19:52:07 -0700429SkColorType SkJPEGImageDecoder::getBitmapColorType(jpeg_decompress_struct* cinfo) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000430 SkASSERT(cinfo != NULL);
431
432 SrcDepth srcDepth = k32Bit_SrcDepth;
433 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
434 srcDepth = k8BitGray_SrcDepth;
435 }
436
reed6c225732014-06-09 19:52:07 -0700437 SkColorType colorType = this->getPrefColorType(srcDepth, /*hasAlpha*/ false);
438 switch (colorType) {
439 case kAlpha_8_SkColorType:
440 // Only respect A8 colortype if the original is grayscale,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000441 // in which case we will treat the grayscale as alpha
442 // values.
443 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
reed6c225732014-06-09 19:52:07 -0700444 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000445 }
446 break;
reed6c225732014-06-09 19:52:07 -0700447 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000448 // Fall through.
reed6c225732014-06-09 19:52:07 -0700449 case kARGB_4444_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000450 // Fall through.
reed6c225732014-06-09 19:52:07 -0700451 case kRGB_565_SkColorType:
452 // These are acceptable destination colortypes.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000453 break;
454 default:
reed6c225732014-06-09 19:52:07 -0700455 // Force all other colortypes to 8888.
456 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000457 break;
458 }
459
460 switch (cinfo->jpeg_color_space) {
461 case JCS_CMYK:
462 // Fall through.
463 case JCS_YCCK:
464 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
465 // so libjpeg will give us CMYK samples back and we will later
466 // manually convert them to RGB
467 cinfo->out_color_space = JCS_CMYK;
468 break;
469 case JCS_GRAYSCALE:
reed6c225732014-06-09 19:52:07 -0700470 if (kAlpha_8_SkColorType == colorType) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000471 cinfo->out_color_space = JCS_GRAYSCALE;
472 break;
473 }
474 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
reed6c225732014-06-09 19:52:07 -0700475 // colortype. Fall through to set to the default.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000476 default:
477 cinfo->out_color_space = JCS_RGB;
478 break;
479 }
reed6c225732014-06-09 19:52:07 -0700480 return colorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000481}
482
scroggo@google.com590a5af2013-08-07 21:09:13 +0000483/**
reed6c225732014-06-09 19:52:07 -0700484 * Based on the colortype and dither mode, adjust out_color_space and
485 * dither_mode of cinfo. Only does work in ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000486 */
487static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
reed6c225732014-06-09 19:52:07 -0700488 SkColorType colorType,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000489 const SkImageDecoder& decoder) {
490 SkASSERT(cinfo != NULL);
reed6c225732014-06-09 19:52:07 -0700491#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000492 cinfo->dither_mode = JDITHER_NONE;
493 if (JCS_CMYK == cinfo->out_color_space) {
494 return;
495 }
reed6c225732014-06-09 19:52:07 -0700496 switch (colorType) {
497 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000498 cinfo->out_color_space = JCS_RGBA_8888;
499 break;
reed6c225732014-06-09 19:52:07 -0700500 case kRGB_565_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000501 cinfo->out_color_space = JCS_RGB_565;
502 if (decoder.getDitherImage()) {
503 cinfo->dither_mode = JDITHER_ORDERED;
504 }
505 break;
506 default:
507 break;
508 }
scroggo@google.com590a5af2013-08-07 21:09:13 +0000509#endif
reed6c225732014-06-09 19:52:07 -0700510}
scroggo@google.com590a5af2013-08-07 21:09:13 +0000511
halcanary@google.comfed30372013-10-04 12:46:45 +0000512/**
513 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
514 Used when decoding fails partway through reading scanlines to fill
515 remaining lines. */
516static void fill_below_level(int y, SkBitmap* bitmap) {
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000517 SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
halcanary@google.comfed30372013-10-04 12:46:45 +0000518 SkCanvas canvas(*bitmap);
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000519 canvas.clipRect(SkRect::Make(rect));
halcanary@google.comfed30372013-10-04 12:46:45 +0000520 canvas.drawColor(SK_ColorWHITE);
521}
522
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000523/**
524 * Get the config and bytes per pixel of the source data. Return
525 * whether the data is supported.
526 */
527static bool get_src_config(const jpeg_decompress_struct& cinfo,
528 SkScaledBitmapSampler::SrcConfig* sc,
529 int* srcBytesPerPixel) {
530 SkASSERT(sc != NULL && srcBytesPerPixel != NULL);
531 if (JCS_CMYK == cinfo.out_color_space) {
532 // In this case we will manually convert the CMYK values to RGB
533 *sc = SkScaledBitmapSampler::kRGBX;
534 // The CMYK work-around relies on 4 components per pixel here
535 *srcBytesPerPixel = 4;
536 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
537 *sc = SkScaledBitmapSampler::kRGB;
538 *srcBytesPerPixel = 3;
539#ifdef ANDROID_RGB
540 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
541 *sc = SkScaledBitmapSampler::kRGBX;
542 *srcBytesPerPixel = 4;
543 } else if (JCS_RGB_565 == cinfo.out_color_space) {
544 *sc = SkScaledBitmapSampler::kRGB_565;
545 *srcBytesPerPixel = 2;
546#endif
547 } else if (1 == cinfo.out_color_components &&
548 JCS_GRAYSCALE == cinfo.out_color_space) {
549 *sc = SkScaledBitmapSampler::kGray;
550 *srcBytesPerPixel = 1;
551 } else {
552 return false;
553 }
554 return true;
555}
halcanary@google.comfed30372013-10-04 12:46:45 +0000556
scroggo2a120802014-10-22 12:07:00 -0700557SkImageDecoder::Result SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000558#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000559 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000560#endif
561
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000562 JPEGAutoClean autoClean;
563
564 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000565 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000566
scroggo@google.com590a5af2013-08-07 21:09:13 +0000567 skjpeg_error_mgr errorManager;
568 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000569
570 // All objects need to be instantiated before this setjmp call so that
571 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000572 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700573 return return_failure(cinfo, *bm, "setjmp");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000574 }
575
scroggo@google.com590a5af2013-08-07 21:09:13 +0000576 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000577 autoClean.set(&cinfo);
578
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000579 int status = jpeg_read_header(&cinfo, true);
580 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700581 return return_failure(cinfo, *bm, "read_header");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000582 }
583
584 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
585 can) much faster that we, just use their num/denom api to approximate
586 the size.
587 */
588 int sampleSize = this->getSampleSize();
589
scroggo@google.com590a5af2013-08-07 21:09:13 +0000590 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000591
scroggo@google.com590a5af2013-08-07 21:09:13 +0000592 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000593 cinfo.scale_denom = sampleSize;
594
scroggo@google.com590a5af2013-08-07 21:09:13 +0000595 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000596
reed6c225732014-06-09 19:52:07 -0700597 const SkColorType colorType = this->getBitmapColorType(&cinfo);
598 const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ?
599 kPremul_SkAlphaType : kOpaque_SkAlphaType;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000600
reed6c225732014-06-09 19:52:07 -0700601 adjust_out_color_space_and_dither(&cinfo, colorType, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000602
djsollen@google.com11399402013-03-20 17:45:27 +0000603 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000604 // Assume an A8 bitmap is not opaque to avoid the check of each
605 // individual pixel. It is very unlikely to be opaque, since
606 // an opaque A8 bitmap would not be very interesting.
607 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700608 bool success = bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
609 colorType, alphaType));
610 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000611 }
612
613 /* image_width and image_height are the original dimensions, available
614 after jpeg_read_header(). To see the scaled dimensions, we have to call
615 jpeg_start_decompress(), and then read output_width and output_height.
616 */
617 if (!jpeg_start_decompress(&cinfo)) {
618 /* If we failed here, we may still have enough information to return
619 to the caller if they just wanted (subsampled bounds). If sampleSize
620 was 1, then we would have already returned. Thus we just check if
621 we're in kDecodeBounds_Mode, and that we have valid output sizes.
622
623 One reason to fail here is that we have insufficient stream data
624 to complete the setup. However, output dimensions seem to get
625 computed very early, which is why this special check can pay off.
626 */
djsollen@google.com11399402013-03-20 17:45:27 +0000627 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000628 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
629 recompute_sampleSize(sampleSize, cinfo));
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000630 // Assume an A8 bitmap is not opaque to avoid the check of each
631 // individual pixel. It is very unlikely to be opaque, since
632 // an opaque A8 bitmap would not be very interesting.
633 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700634 bool success = bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
635 colorType, alphaType));
636 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000637 } else {
scroggo2a120802014-10-22 12:07:00 -0700638 return return_failure(cinfo, *bm, "start_decompress");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000639 }
640 }
641 sampleSize = recompute_sampleSize(sampleSize, cinfo);
642
djsollen@google.com11399402013-03-20 17:45:27 +0000643 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000644 // Assume an A8 bitmap is not opaque to avoid the check of each
645 // individual pixel. It is very unlikely to be opaque, since
646 // an opaque A8 bitmap would not be very interesting.
647 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700648 bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
649 colorType, alphaType));
scroggo@google.combc69ce92013-07-09 15:45:14 +0000650 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo2a120802014-10-22 12:07:00 -0700651 return kSuccess;
scroggo@google.combc69ce92013-07-09 15:45:14 +0000652 }
653 if (!this->allocPixelRef(bm, NULL)) {
scroggo2a120802014-10-22 12:07:00 -0700654 return return_failure(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000655 }
656
657 SkAutoLockPixels alp(*bm);
658
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000659#ifdef ANDROID_RGB
660 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
661 a significant performance boost.
662 */
663 if (sampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700664 ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||
665 (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000666 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000667 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000668 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000669
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000670 while (cinfo.output_scanline < cinfo.output_height) {
671 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000672 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000673 // if row_count == 0, then we didn't get a scanline,
674 // so return early. We will return a partial image.
675 fill_below_level(cinfo.output_scanline, bm);
676 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700677 jpeg_finish_decompress(&cinfo);
678 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000679 }
680 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700681 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000682 }
683 rowptr += bpr;
684 }
685 jpeg_finish_decompress(&cinfo);
scroggo2a120802014-10-22 12:07:00 -0700686 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000687 }
688#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000689
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000690 // check for supported formats
691 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000692 int srcBytesPerPixel;
693
694 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
scroggo2a120802014-10-22 12:07:00 -0700695 return return_failure(cinfo, *bm, "jpeg colorspace");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000696 }
697
scroggo@google.com8d239242013-10-01 17:27:15 +0000698 if (!sampler.begin(bm, sc, *this)) {
scroggo2a120802014-10-22 12:07:00 -0700699 return return_failure(cinfo, *bm, "sampler.begin");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000700 }
701
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000702 SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000703 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000704
705 // Possibly skip initial rows [sampler.srcY0]
706 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
scroggo2a120802014-10-22 12:07:00 -0700707 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000708 }
709
710 // now loop through scanlines until y == bm->height() - 1
711 for (int y = 0;; y++) {
712 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
713 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
714 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000715 // if row_count == 0, then we didn't get a scanline,
716 // so return early. We will return a partial image.
717 fill_below_level(y, bm);
718 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700719 jpeg_finish_decompress(&cinfo);
scroggoc6b8ffa2014-12-17 06:55:02 -0800720 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000721 }
722 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700723 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000724 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000725
726 if (JCS_CMYK == cinfo.out_color_space) {
727 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
728 }
729
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000730 sampler.next(srcRow);
731 if (bm->height() - 1 == y) {
732 // we're done
733 break;
734 }
735
736 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
scroggo2a120802014-10-22 12:07:00 -0700737 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000738 }
739 }
740
741 // we formally skip the rest, so we don't get a complaint from libjpeg
742 if (!skip_src_rows(&cinfo, srcRow,
743 cinfo.output_height - cinfo.output_scanline)) {
scroggo2a120802014-10-22 12:07:00 -0700744 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000745 }
746 jpeg_finish_decompress(&cinfo);
747
scroggo2a120802014-10-22 12:07:00 -0700748 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000749}
750
scroggo2a120802014-10-22 12:07:00 -0700751///////////////////////////////////////////////////////////////////////////////
752
sugoib227e372014-10-16 13:10:57 -0700753enum SizeType {
754 kSizeForMemoryAllocation_SizeType,
755 kActualSize_SizeType
756};
757
758static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component,
759 SizeType sizeType) {
760 if (sizeType == kSizeForMemoryAllocation_SizeType) {
761 return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE,
762 info.cur_comp_info[component]->height_in_blocks * DCTSIZE);
763 }
764 return SkISize::Make(info.cur_comp_info[component]->downsampled_width,
765 info.cur_comp_info[component]->downsampled_height);
766}
767
sugoib227e372014-10-16 13:10:57 -0700768static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
769 SizeType sizeType) {
770 for (int i = 0; i < 3; ++i) {
771 componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
772 }
773}
774
775static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) {
776 // U size and V size have to be the same if we're calling output_raw_data()
777 SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType);
778 SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType));
779
780 JSAMPARRAY bufferraw[3];
781 JSAMPROW bufferraw2[32];
782 bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16)
783 bufferraw[1] = &bufferraw2[16]; // U channel rows (8)
784 bufferraw[2] = &bufferraw2[24]; // V channel rows (8)
785 int yWidth = cinfo.output_width;
786 int yHeight = cinfo.output_height;
787 int yMaxH = yHeight - 1;
788 int v = cinfo.cur_comp_info[0]->v_samp_factor;
789 int uvMaxH = uvSize.height() - 1;
790 JSAMPROW outputY = static_cast<JSAMPROW>(planes[0]);
791 JSAMPROW outputU = static_cast<JSAMPROW>(planes[1]);
792 JSAMPROW outputV = static_cast<JSAMPROW>(planes[2]);
793 size_t rowBytesY = rowBytes[0];
794 size_t rowBytesU = rowBytes[1];
795 size_t rowBytesV = rowBytes[2];
796
797 int yScanlinesToRead = DCTSIZE * v;
798 SkAutoMalloc lastRowStorage(yWidth * 8);
799 JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get();
800 JSAMPROW uLastRow = yLastRow + 2 * yWidth;
801 JSAMPROW vLastRow = uLastRow + 2 * yWidth;
802 JSAMPROW dummyRow = vLastRow + 2 * yWidth;
803
804 while (cinfo.output_scanline < cinfo.output_height) {
805 // Request 8 or 16 scanlines: returns 0 or more scanlines.
806 bool hasYLastRow(false), hasUVLastRow(false);
807 // Assign 8 or 16 rows of memory to read the Y channel.
808 for (int i = 0; i < yScanlinesToRead; ++i) {
809 int scanline = (cinfo.output_scanline + i);
810 if (scanline < yMaxH) {
811 bufferraw2[i] = &outputY[scanline * rowBytesY];
812 } else if (scanline == yMaxH) {
813 bufferraw2[i] = yLastRow;
814 hasYLastRow = true;
815 } else {
816 bufferraw2[i] = dummyRow;
817 }
818 }
819 int scaledScanline = cinfo.output_scanline / v;
820 // Assign 8 rows of memory to read the U and V channels.
821 for (int i = 0; i < 8; ++i) {
822 int scanline = (scaledScanline + i);
823 if (scanline < uvMaxH) {
824 bufferraw2[16 + i] = &outputU[scanline * rowBytesU];
825 bufferraw2[24 + i] = &outputV[scanline * rowBytesV];
826 } else if (scanline == uvMaxH) {
827 bufferraw2[16 + i] = uLastRow;
828 bufferraw2[24 + i] = vLastRow;
829 hasUVLastRow = true;
830 } else {
831 bufferraw2[16 + i] = dummyRow;
832 bufferraw2[24 + i] = dummyRow;
833 }
834 }
835 JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead);
836
scroggo2a120802014-10-22 12:07:00 -0700837 if (scanlinesRead == 0) {
sugoib227e372014-10-16 13:10:57 -0700838 return false;
scroggo2a120802014-10-22 12:07:00 -0700839 }
sugoib227e372014-10-16 13:10:57 -0700840
841 if (hasYLastRow) {
842 memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth);
843 }
844 if (hasUVLastRow) {
845 memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width());
846 memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width());
847 }
848 }
849
850 cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height);
851
852 return true;
853}
854
855bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
856 void* planes[3], size_t rowBytes[3],
857 SkYUVColorSpace* colorSpace) {
858#ifdef TIME_DECODE
859 SkAutoTime atm("JPEG YUV8 Decode");
860#endif
861
862 if (this->getSampleSize() != 1) {
863 return false; // Resizing not supported
864 }
865
866 JPEGAutoClean autoClean;
867
868 jpeg_decompress_struct cinfo;
869 skjpeg_source_mgr srcManager(stream, this);
870
871 skjpeg_error_mgr errorManager;
872 set_error_mgr(&cinfo, &errorManager);
873
874 // All objects need to be instantiated before this setjmp call so that
875 // they will be cleaned up properly if an error occurs.
876 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700877 return return_false(cinfo, "setjmp YUV8");
sugoib227e372014-10-16 13:10:57 -0700878 }
879
880 initialize_info(&cinfo, &srcManager);
881 autoClean.set(&cinfo);
882
883 int status = jpeg_read_header(&cinfo, true);
884 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700885 return return_false(cinfo, "read_header YUV8");
sugoib227e372014-10-16 13:10:57 -0700886 }
887
888 if (cinfo.jpeg_color_space != JCS_YCbCr) {
889 // It's not an error to not be encoded in YUV, so no need to use return_false()
890 return false;
891 }
892
893 cinfo.out_color_space = JCS_YCbCr;
894 cinfo.raw_data_out = TRUE;
895
896 if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only
897 update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType);
898 return true;
899 }
900
901 set_dct_method(*this, &cinfo);
902
903 SkASSERT(1 == cinfo.scale_num);
904 cinfo.scale_denom = 1;
905
906 turn_off_visual_optimizations(&cinfo);
907
908#ifdef ANDROID_RGB
909 cinfo.dither_mode = JDITHER_NONE;
910#endif
911
912 /* image_width and image_height are the original dimensions, available
913 after jpeg_read_header(). To see the scaled dimensions, we have to call
914 jpeg_start_decompress(), and then read output_width and output_height.
915 */
916 if (!jpeg_start_decompress(&cinfo)) {
scroggo2a120802014-10-22 12:07:00 -0700917 return return_false(cinfo, "start_decompress YUV8");
sugoib227e372014-10-16 13:10:57 -0700918 }
919
920 if (!output_raw_data(cinfo, planes, rowBytes)) {
scroggo2a120802014-10-22 12:07:00 -0700921 return return_false(cinfo, "output_raw_data");
sugoib227e372014-10-16 13:10:57 -0700922 }
923
924 update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
925 jpeg_finish_decompress(&cinfo);
926
927 if (NULL != colorSpace) {
928 *colorSpace = kJPEG_SkYUVColorSpace;
929 }
930
931 return true;
932}
933
scroggo2a120802014-10-22 12:07:00 -0700934///////////////////////////////////////////////////////////////////////////////
935
scroggo@google.comd79277f2013-08-07 19:53:53 +0000936#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000937bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000938
scroggo@google.coma1a51542013-08-07 21:02:32 +0000939 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000940 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000941
942 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000943 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000944
945 // All objects need to be instantiated before this setjmp call so that
946 // they will be cleaned up properly if an error occurs.
947 if (setjmp(sk_err.fJmpBuf)) {
948 return false;
949 }
950
951 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000952 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000953 return false;
954 }
955
scroggo@google.coma1a51542013-08-07 21:02:32 +0000956 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000957 return false;
958 }
959
960 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000961 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000962
963 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000964 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000965 return false;
966 }
967
scroggo@google.com590a5af2013-08-07 21:09:13 +0000968 // FIXME: This sets cinfo->out_color_space, which we may change later
969 // based on the config in onDecodeSubset. This should be fine, since
970 // jpeg_init_read_tile_scanline will check out_color_space again after
971 // that change (when it calls jinit_color_deconverter).
reed6c225732014-06-09 19:52:07 -0700972 (void) this->getBitmapColorType(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000973
974 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000975
976 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000977 if (!imageIndex->startTileDecompress()) {
978 return false;
979 }
djsollen@google.com11399402013-03-20 17:45:27 +0000980
scroggo@google.coma1a51542013-08-07 21:02:32 +0000981 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000982 fImageWidth = cinfo->output_width;
983 fImageHeight = cinfo->output_height;
984
985 if (width) {
986 *width = fImageWidth;
987 }
988 if (height) {
989 *height = fImageHeight;
990 }
djsollen@google.com11399402013-03-20 17:45:27 +0000991
992 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000993 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +0000994
995 return true;
996}
997
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000998bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000999 if (NULL == fImageIndex) {
1000 return false;
1001 }
1002 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
1003
1004 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
1005 if (!rect.intersect(region)) {
1006 // If the requested region is entirely outside the image return false
1007 return false;
1008 }
1009
1010
1011 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +00001012 set_error_mgr(cinfo, &errorManager);
1013
djsollen@google.com11399402013-03-20 17:45:27 +00001014 if (setjmp(errorManager.fJmpBuf)) {
1015 return false;
1016 }
1017
1018 int requestedSampleSize = this->getSampleSize();
1019 cinfo->scale_denom = requestedSampleSize;
1020
scroggo@google.com590a5af2013-08-07 21:09:13 +00001021 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +00001022
reed6c225732014-06-09 19:52:07 -07001023 const SkColorType colorType = this->getBitmapColorType(cinfo);
1024 adjust_out_color_space_and_dither(cinfo, colorType, *this);
djsollen@google.com11399402013-03-20 17:45:27 +00001025
1026 int startX = rect.fLeft;
1027 int startY = rect.fTop;
1028 int width = rect.width();
1029 int height = rect.height();
1030
1031 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
1032 &startX, &startY, &width, &height);
1033 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
1034 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
1035
1036 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
1037
1038 SkBitmap bitmap;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001039 // Assume an A8 bitmap is not opaque to avoid the check of each
1040 // individual pixel. It is very unlikely to be opaque, since
1041 // an opaque A8 bitmap would not be very interesting.
1042 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -07001043 bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType,
1044 kAlpha_8_SkColorType == colorType ?
1045 kPremul_SkAlphaType : kOpaque_SkAlphaType));
djsollen@google.com11399402013-03-20 17:45:27 +00001046
1047 // Check ahead of time if the swap(dest, src) is possible or not.
1048 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
1049 // swap happening. If no, then we will use alloc to allocate pixels to
1050 // prevent garbage collection.
1051 int w = rect.width() / actualSampleSize;
1052 int h = rect.height() / actualSampleSize;
1053 bool swapOnly = (rect == region) && bm->isNull() &&
1054 (w == bitmap.width()) && (h == bitmap.height()) &&
1055 ((startX - rect.x()) / actualSampleSize == 0) &&
1056 ((startY - rect.y()) / actualSampleSize == 0);
1057 if (swapOnly) {
1058 if (!this->allocPixelRef(&bitmap, NULL)) {
1059 return return_false(*cinfo, bitmap, "allocPixelRef");
1060 }
1061 } else {
reed84825042014-09-02 12:50:45 -07001062 if (!bitmap.tryAllocPixels()) {
djsollen@google.com11399402013-03-20 17:45:27 +00001063 return return_false(*cinfo, bitmap, "allocPixels");
1064 }
1065 }
1066
1067 SkAutoLockPixels alp(bitmap);
1068
1069#ifdef ANDROID_RGB
1070 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
1071 a significant performance boost.
1072 */
1073 if (skiaSampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -07001074 ((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||
1075 (kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565)))
djsollen@google.com11399402013-03-20 17:45:27 +00001076 {
1077 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
1078 INT32 const bpr = bitmap.rowBytes();
1079 int rowTotalCount = 0;
1080
1081 while (rowTotalCount < height) {
1082 int rowCount = jpeg_read_tile_scanline(cinfo,
1083 fImageIndex->huffmanIndex(),
1084 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001085 // if rowCount == 0, then we didn't get a scanline, so abort.
1086 // onDecodeSubset() relies on onBuildTileIndex(), which
1087 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001088 if (0 == rowCount) {
1089 return return_false(*cinfo, bitmap, "read_scanlines");
1090 }
1091 if (this->shouldCancelDecode()) {
1092 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1093 }
1094 rowTotalCount += rowCount;
1095 rowptr += bpr;
1096 }
1097
1098 if (swapOnly) {
1099 bm->swap(bitmap);
1100 } else {
1101 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1102 region.width(), region.height(), startX, startY);
1103 }
1104 return true;
1105 }
1106#endif
1107
1108 // check for supported formats
1109 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001110 int srcBytesPerPixel;
1111
1112 if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001113 return return_false(*cinfo, *bm, "jpeg colorspace");
1114 }
1115
scroggo@google.com8d239242013-10-01 17:27:15 +00001116 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001117 return return_false(*cinfo, bitmap, "sampler.begin");
1118 }
1119
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001120 SkAutoMalloc srcStorage(width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +00001121 uint8_t* srcRow = (uint8_t*)srcStorage.get();
1122
1123 // Possibly skip initial rows [sampler.srcY0]
1124 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
1125 return return_false(*cinfo, bitmap, "skip rows");
1126 }
1127
1128 // now loop through scanlines until y == bitmap->height() - 1
1129 for (int y = 0;; y++) {
1130 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
1131 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001132 // if row_count == 0, then we didn't get a scanline, so abort.
1133 // onDecodeSubset() relies on onBuildTileIndex(), which
1134 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001135 if (0 == row_count) {
1136 return return_false(*cinfo, bitmap, "read_scanlines");
1137 }
1138 if (this->shouldCancelDecode()) {
1139 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1140 }
1141
1142 if (JCS_CMYK == cinfo->out_color_space) {
1143 convert_CMYK_to_RGB(srcRow, width);
1144 }
1145
1146 sampler.next(srcRow);
1147 if (bitmap.height() - 1 == y) {
1148 // we're done
1149 break;
1150 }
1151
1152 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
1153 sampler.srcDY() - 1)) {
1154 return return_false(*cinfo, bitmap, "skip rows");
1155 }
1156 }
1157 if (swapOnly) {
1158 bm->swap(bitmap);
1159 } else {
1160 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1161 region.width(), region.height(), startX, startY);
1162 }
1163 return true;
1164}
1165#endif
1166
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001167///////////////////////////////////////////////////////////////////////////////
1168
1169#include "SkColorPriv.h"
1170
1171// taken from jcolor.c in libjpeg
1172#if 0 // 16bit - precise but slow
1173 #define CYR 19595 // 0.299
1174 #define CYG 38470 // 0.587
1175 #define CYB 7471 // 0.114
1176
1177 #define CUR -11059 // -0.16874
1178 #define CUG -21709 // -0.33126
1179 #define CUB 32768 // 0.5
1180
1181 #define CVR 32768 // 0.5
1182 #define CVG -27439 // -0.41869
1183 #define CVB -5329 // -0.08131
1184
1185 #define CSHIFT 16
1186#else // 8bit - fast, slightly less precise
1187 #define CYR 77 // 0.299
1188 #define CYG 150 // 0.587
1189 #define CYB 29 // 0.114
1190
1191 #define CUR -43 // -0.16874
1192 #define CUG -85 // -0.33126
1193 #define CUB 128 // 0.5
1194
1195 #define CVR 128 // 0.5
1196 #define CVG -107 // -0.41869
1197 #define CVB -21 // -0.08131
1198
1199 #define CSHIFT 8
1200#endif
1201
1202static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
1203 int r = SkGetPackedR32(c);
1204 int g = SkGetPackedG32(c);
1205 int b = SkGetPackedB32(c);
1206
1207 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
1208 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
1209 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
1210
1211 dst[0] = SkToU8(y);
1212 dst[1] = SkToU8(u + 128);
1213 dst[2] = SkToU8(v + 128);
1214}
1215
1216static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
1217 int r = SkGetPackedR4444(c);
1218 int g = SkGetPackedG4444(c);
1219 int b = SkGetPackedB4444(c);
1220
1221 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
1222 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
1223 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
1224
1225 dst[0] = SkToU8(y);
1226 dst[1] = SkToU8(u + 128);
1227 dst[2] = SkToU8(v + 128);
1228}
1229
1230static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
1231 int r = SkGetPackedR16(c);
1232 int g = SkGetPackedG16(c);
1233 int b = SkGetPackedB16(c);
1234
1235 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
1236 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
1237 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
1238
1239 dst[0] = SkToU8(y);
1240 dst[1] = SkToU8(u + 128);
1241 dst[2] = SkToU8(v + 128);
1242}
1243
1244///////////////////////////////////////////////////////////////////////////////
1245
1246typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1247 const void* SK_RESTRICT src, int width,
1248 const SkPMColor* SK_RESTRICT ctable);
1249
1250static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1251 const void* SK_RESTRICT srcRow, int width,
1252 const SkPMColor*) {
1253 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1254 while (--width >= 0) {
1255#ifdef WE_CONVERT_TO_YUV
1256 rgb2yuv_32(dst, *src++);
1257#else
1258 uint32_t c = *src++;
1259 dst[0] = SkGetPackedR32(c);
1260 dst[1] = SkGetPackedG32(c);
1261 dst[2] = SkGetPackedB32(c);
1262#endif
1263 dst += 3;
1264 }
1265}
1266
1267static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1268 const void* SK_RESTRICT srcRow, int width,
1269 const SkPMColor*) {
1270 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1271 while (--width >= 0) {
1272#ifdef WE_CONVERT_TO_YUV
1273 rgb2yuv_4444(dst, *src++);
1274#else
1275 SkPMColor16 c = *src++;
1276 dst[0] = SkPacked4444ToR32(c);
1277 dst[1] = SkPacked4444ToG32(c);
1278 dst[2] = SkPacked4444ToB32(c);
1279#endif
1280 dst += 3;
1281 }
1282}
1283
1284static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1285 const void* SK_RESTRICT srcRow, int width,
1286 const SkPMColor*) {
1287 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1288 while (--width >= 0) {
1289#ifdef WE_CONVERT_TO_YUV
1290 rgb2yuv_16(dst, *src++);
1291#else
1292 uint16_t c = *src++;
1293 dst[0] = SkPacked16ToR32(c);
1294 dst[1] = SkPacked16ToG32(c);
1295 dst[2] = SkPacked16ToB32(c);
1296#endif
1297 dst += 3;
1298 }
1299}
1300
1301static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1302 const void* SK_RESTRICT srcRow, int width,
1303 const SkPMColor* SK_RESTRICT ctable) {
1304 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1305 while (--width >= 0) {
1306#ifdef WE_CONVERT_TO_YUV
1307 rgb2yuv_32(dst, ctable[*src++]);
1308#else
1309 uint32_t c = ctable[*src++];
1310 dst[0] = SkGetPackedR32(c);
1311 dst[1] = SkGetPackedG32(c);
1312 dst[2] = SkGetPackedB32(c);
1313#endif
1314 dst += 3;
1315 }
1316}
1317
1318static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -07001319 switch (bm.colorType()) {
1320 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001321 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -07001322 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001323 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -07001324 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001325 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -07001326 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001327 return Write_Index_YUV;
1328 default:
1329 return NULL;
1330 }
1331}
1332
1333class SkJPEGImageEncoder : public SkImageEncoder {
1334protected:
1335 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1336#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001337 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001338#endif
1339
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001340 SkAutoLockPixels alp(bm);
1341 if (NULL == bm.getPixels()) {
1342 return false;
1343 }
1344
1345 jpeg_compress_struct cinfo;
1346 skjpeg_error_mgr sk_err;
1347 skjpeg_destination_mgr sk_wstream(stream);
1348
1349 // allocate these before set call setjmp
1350 SkAutoMalloc oneRow;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001351
1352 cinfo.err = jpeg_std_error(&sk_err);
1353 sk_err.error_exit = skjpeg_error_exit;
1354 if (setjmp(sk_err.fJmpBuf)) {
1355 return false;
1356 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001357
mtklein@google.com8d725b22013-07-24 16:20:05 +00001358 // Keep after setjmp or mark volatile.
1359 const WriteScanline writer = ChooseWriter(bm);
1360 if (NULL == writer) {
1361 return false;
1362 }
1363
1364 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001365 cinfo.dest = &sk_wstream;
1366 cinfo.image_width = bm.width();
1367 cinfo.image_height = bm.height();
1368 cinfo.input_components = 3;
1369#ifdef WE_CONVERT_TO_YUV
1370 cinfo.in_color_space = JCS_YCbCr;
1371#else
1372 cinfo.in_color_space = JCS_RGB;
1373#endif
1374 cinfo.input_gamma = 1;
1375
1376 jpeg_set_defaults(&cinfo);
1377 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001378#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001379 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001380#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001381
1382 jpeg_start_compress(&cinfo, TRUE);
1383
1384 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001385 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001386
mtklein775b8192014-12-02 09:11:25 -08001387 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : NULL;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001388 const void* srcRow = bm.getPixels();
1389
1390 while (cinfo.next_scanline < cinfo.image_height) {
1391 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1392
1393 writer(oneRowP, srcRow, width, colors);
1394 row_pointer[0] = oneRowP;
1395 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1396 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1397 }
1398
1399 jpeg_finish_compress(&cinfo);
1400 jpeg_destroy_compress(&cinfo);
1401
1402 return true;
1403 }
1404};
1405
1406///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001407DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1408DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1409///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001410
scroggo@google.comb5571b32013-09-25 21:34:24 +00001411static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001412 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001413 static const size_t HEADER_SIZE = sizeof(gHeader);
1414
1415 char buffer[HEADER_SIZE];
1416 size_t len = stream->read(buffer, HEADER_SIZE);
1417
1418 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001419 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001420 }
1421 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001422 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001423 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001424 return true;
1425}
1426
scroggo@google.comb5571b32013-09-25 21:34:24 +00001427
1428static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001429 if (is_jpeg(stream)) {
1430 return SkNEW(SkJPEGImageDecoder);
1431 }
1432 return NULL;
1433}
1434
scroggo@google.comb5571b32013-09-25 21:34:24 +00001435static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001436 if (is_jpeg(stream)) {
1437 return SkImageDecoder::kJPEG_Format;
1438 }
1439 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001440}
1441
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001442static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001443 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1444}
1445
mtklein@google.combd6343b2013-09-04 17:20:18 +00001446static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1447static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1448static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);