blob: 082adca04af610792b74aca6d8fdbe330c64144a [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
scroggo2a120802014-10-22 12:07:00 -0700241 virtual 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
sugoib227e372014-10-16 13:10:57 -0700348static bool return_false(const jpeg_decompress_struct& cinfo,
349 const SkBitmap& bm, const char caller[]) {
scroggo2a120802014-10-22 12:07:00 -0700350 print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
351 return false;
sugoib227e372014-10-16 13:10:57 -0700352}
353
scroggo2a120802014-10-22 12:07:00 -0700354static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo,
355 const SkBitmap& bm, const char caller[]) {
356 print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
357 return SkImageDecoder::kFailure;
358}
359
360///////////////////////////////////////////////////////////////////////////////
361
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000362// Convert a scanline of CMYK samples to RGBX in place. Note that this
363// method moves the "scanline" pointer in its processing
364static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
365 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000366 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000367 // from easyrgb.com):
368 // CMYK -> CMY
369 // C = ( C * (1 - K) + K ) // for each CMY component
370 // CMY -> RGB
371 // R = ( 1 - C ) * 255 // for each RGB component
372 // Unfortunately we are seeing inverted CMYK so all the original terms
373 // are 1-. This yields:
374 // CMYK -> CMY
375 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
376 // The conversion from CMY->RGB remains the same
377 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
378 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
379 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
380 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
381 scanline[3] = 255;
382 }
383}
384
scroggo@google.com590a5af2013-08-07 21:09:13 +0000385/**
386 * Common code for setting the error manager.
387 */
388static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
389 SkASSERT(cinfo != NULL);
390 SkASSERT(errorManager != NULL);
391 cinfo->err = jpeg_std_error(errorManager);
392 errorManager->error_exit = skjpeg_error_exit;
393}
394
395/**
396 * Common code for turning off upsampling and smoothing. Turning these
397 * off helps performance without showing noticable differences in the
398 * resulting bitmap.
399 */
400static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
401 SkASSERT(cinfo != NULL);
402 /* this gives about 30% performance improvement. In theory it may
403 reduce the visual quality, in practice I'm not seeing a difference
404 */
405 cinfo->do_fancy_upsampling = 0;
406
407 /* this gives another few percents */
408 cinfo->do_block_smoothing = 0;
409}
410
411/**
412 * Common code for setting the dct method.
413 */
414static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
415 SkASSERT(cinfo != NULL);
416#ifdef DCT_IFAST_SUPPORTED
417 if (decoder.getPreferQualityOverSpeed()) {
418 cinfo->dct_method = JDCT_ISLOW;
419 } else {
420 cinfo->dct_method = JDCT_IFAST;
421 }
422#else
423 cinfo->dct_method = JDCT_ISLOW;
424#endif
425}
426
reed6c225732014-06-09 19:52:07 -0700427SkColorType SkJPEGImageDecoder::getBitmapColorType(jpeg_decompress_struct* cinfo) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000428 SkASSERT(cinfo != NULL);
429
430 SrcDepth srcDepth = k32Bit_SrcDepth;
431 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
432 srcDepth = k8BitGray_SrcDepth;
433 }
434
reed6c225732014-06-09 19:52:07 -0700435 SkColorType colorType = this->getPrefColorType(srcDepth, /*hasAlpha*/ false);
436 switch (colorType) {
437 case kAlpha_8_SkColorType:
438 // Only respect A8 colortype if the original is grayscale,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000439 // in which case we will treat the grayscale as alpha
440 // values.
441 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
reed6c225732014-06-09 19:52:07 -0700442 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000443 }
444 break;
reed6c225732014-06-09 19:52:07 -0700445 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000446 // Fall through.
reed6c225732014-06-09 19:52:07 -0700447 case kARGB_4444_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000448 // Fall through.
reed6c225732014-06-09 19:52:07 -0700449 case kRGB_565_SkColorType:
450 // These are acceptable destination colortypes.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000451 break;
452 default:
reed6c225732014-06-09 19:52:07 -0700453 // Force all other colortypes to 8888.
454 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000455 break;
456 }
457
458 switch (cinfo->jpeg_color_space) {
459 case JCS_CMYK:
460 // Fall through.
461 case JCS_YCCK:
462 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
463 // so libjpeg will give us CMYK samples back and we will later
464 // manually convert them to RGB
465 cinfo->out_color_space = JCS_CMYK;
466 break;
467 case JCS_GRAYSCALE:
reed6c225732014-06-09 19:52:07 -0700468 if (kAlpha_8_SkColorType == colorType) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000469 cinfo->out_color_space = JCS_GRAYSCALE;
470 break;
471 }
472 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
reed6c225732014-06-09 19:52:07 -0700473 // colortype. Fall through to set to the default.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000474 default:
475 cinfo->out_color_space = JCS_RGB;
476 break;
477 }
reed6c225732014-06-09 19:52:07 -0700478 return colorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000479}
480
scroggo@google.com590a5af2013-08-07 21:09:13 +0000481/**
reed6c225732014-06-09 19:52:07 -0700482 * Based on the colortype and dither mode, adjust out_color_space and
483 * dither_mode of cinfo. Only does work in ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000484 */
485static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
reed6c225732014-06-09 19:52:07 -0700486 SkColorType colorType,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000487 const SkImageDecoder& decoder) {
488 SkASSERT(cinfo != NULL);
reed6c225732014-06-09 19:52:07 -0700489#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000490 cinfo->dither_mode = JDITHER_NONE;
491 if (JCS_CMYK == cinfo->out_color_space) {
492 return;
493 }
reed6c225732014-06-09 19:52:07 -0700494 switch (colorType) {
495 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000496 cinfo->out_color_space = JCS_RGBA_8888;
497 break;
reed6c225732014-06-09 19:52:07 -0700498 case kRGB_565_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000499 cinfo->out_color_space = JCS_RGB_565;
500 if (decoder.getDitherImage()) {
501 cinfo->dither_mode = JDITHER_ORDERED;
502 }
503 break;
504 default:
505 break;
506 }
scroggo@google.com590a5af2013-08-07 21:09:13 +0000507#endif
reed6c225732014-06-09 19:52:07 -0700508}
scroggo@google.com590a5af2013-08-07 21:09:13 +0000509
halcanary@google.comfed30372013-10-04 12:46:45 +0000510/**
511 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
512 Used when decoding fails partway through reading scanlines to fill
513 remaining lines. */
514static void fill_below_level(int y, SkBitmap* bitmap) {
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000515 SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
halcanary@google.comfed30372013-10-04 12:46:45 +0000516 SkCanvas canvas(*bitmap);
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000517 canvas.clipRect(SkRect::Make(rect));
halcanary@google.comfed30372013-10-04 12:46:45 +0000518 canvas.drawColor(SK_ColorWHITE);
519}
520
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000521/**
522 * Get the config and bytes per pixel of the source data. Return
523 * whether the data is supported.
524 */
525static bool get_src_config(const jpeg_decompress_struct& cinfo,
526 SkScaledBitmapSampler::SrcConfig* sc,
527 int* srcBytesPerPixel) {
528 SkASSERT(sc != NULL && srcBytesPerPixel != NULL);
529 if (JCS_CMYK == cinfo.out_color_space) {
530 // In this case we will manually convert the CMYK values to RGB
531 *sc = SkScaledBitmapSampler::kRGBX;
532 // The CMYK work-around relies on 4 components per pixel here
533 *srcBytesPerPixel = 4;
534 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
535 *sc = SkScaledBitmapSampler::kRGB;
536 *srcBytesPerPixel = 3;
537#ifdef ANDROID_RGB
538 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
539 *sc = SkScaledBitmapSampler::kRGBX;
540 *srcBytesPerPixel = 4;
541 } else if (JCS_RGB_565 == cinfo.out_color_space) {
542 *sc = SkScaledBitmapSampler::kRGB_565;
543 *srcBytesPerPixel = 2;
544#endif
545 } else if (1 == cinfo.out_color_components &&
546 JCS_GRAYSCALE == cinfo.out_color_space) {
547 *sc = SkScaledBitmapSampler::kGray;
548 *srcBytesPerPixel = 1;
549 } else {
550 return false;
551 }
552 return true;
553}
halcanary@google.comfed30372013-10-04 12:46:45 +0000554
scroggo2a120802014-10-22 12:07:00 -0700555SkImageDecoder::Result SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000556#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000557 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000558#endif
559
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000560 JPEGAutoClean autoClean;
561
562 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000563 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000564
scroggo@google.com590a5af2013-08-07 21:09:13 +0000565 skjpeg_error_mgr errorManager;
566 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000567
568 // All objects need to be instantiated before this setjmp call so that
569 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000570 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700571 return return_failure(cinfo, *bm, "setjmp");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000572 }
573
scroggo@google.com590a5af2013-08-07 21:09:13 +0000574 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000575 autoClean.set(&cinfo);
576
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000577 int status = jpeg_read_header(&cinfo, true);
578 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700579 return return_failure(cinfo, *bm, "read_header");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000580 }
581
582 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
583 can) much faster that we, just use their num/denom api to approximate
584 the size.
585 */
586 int sampleSize = this->getSampleSize();
587
scroggo@google.com590a5af2013-08-07 21:09:13 +0000588 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000589
scroggo@google.com590a5af2013-08-07 21:09:13 +0000590 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000591 cinfo.scale_denom = sampleSize;
592
scroggo@google.com590a5af2013-08-07 21:09:13 +0000593 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000594
reed6c225732014-06-09 19:52:07 -0700595 const SkColorType colorType = this->getBitmapColorType(&cinfo);
596 const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ?
597 kPremul_SkAlphaType : kOpaque_SkAlphaType;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000598
reed6c225732014-06-09 19:52:07 -0700599 adjust_out_color_space_and_dither(&cinfo, colorType, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000600
djsollen@google.com11399402013-03-20 17:45:27 +0000601 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000602 // Assume an A8 bitmap is not opaque to avoid the check of each
603 // individual pixel. It is very unlikely to be opaque, since
604 // an opaque A8 bitmap would not be very interesting.
605 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700606 bool success = bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
607 colorType, alphaType));
608 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000609 }
610
611 /* image_width and image_height are the original dimensions, available
612 after jpeg_read_header(). To see the scaled dimensions, we have to call
613 jpeg_start_decompress(), and then read output_width and output_height.
614 */
615 if (!jpeg_start_decompress(&cinfo)) {
616 /* If we failed here, we may still have enough information to return
617 to the caller if they just wanted (subsampled bounds). If sampleSize
618 was 1, then we would have already returned. Thus we just check if
619 we're in kDecodeBounds_Mode, and that we have valid output sizes.
620
621 One reason to fail here is that we have insufficient stream data
622 to complete the setup. However, output dimensions seem to get
623 computed very early, which is why this special check can pay off.
624 */
djsollen@google.com11399402013-03-20 17:45:27 +0000625 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000626 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
627 recompute_sampleSize(sampleSize, cinfo));
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000628 // Assume an A8 bitmap is not opaque to avoid the check of each
629 // individual pixel. It is very unlikely to be opaque, since
630 // an opaque A8 bitmap would not be very interesting.
631 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700632 bool success = bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
633 colorType, alphaType));
634 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000635 } else {
scroggo2a120802014-10-22 12:07:00 -0700636 return return_failure(cinfo, *bm, "start_decompress");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000637 }
638 }
639 sampleSize = recompute_sampleSize(sampleSize, cinfo);
640
reed5926b862014-06-11 10:33:13 -0700641#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed6c225732014-06-09 19:52:07 -0700642 // should we allow the Chooser (if present) to pick a colortype for us???
643 if (!this->chooseFromOneChoice(colorType, cinfo.output_width, cinfo.output_height)) {
scroggo2a120802014-10-22 12:07:00 -0700644 return return_failure(cinfo, *bm, "chooseFromOneChoice");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000645 }
reed5926b862014-06-11 10:33:13 -0700646#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000647
djsollen@google.com11399402013-03-20 17:45:27 +0000648 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000649 // Assume an A8 bitmap is not opaque to avoid the check of each
650 // individual pixel. It is very unlikely to be opaque, since
651 // an opaque A8 bitmap would not be very interesting.
652 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700653 bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
654 colorType, alphaType));
scroggo@google.combc69ce92013-07-09 15:45:14 +0000655 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo2a120802014-10-22 12:07:00 -0700656 return kSuccess;
scroggo@google.combc69ce92013-07-09 15:45:14 +0000657 }
658 if (!this->allocPixelRef(bm, NULL)) {
scroggo2a120802014-10-22 12:07:00 -0700659 return return_failure(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000660 }
661
662 SkAutoLockPixels alp(*bm);
663
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000664#ifdef ANDROID_RGB
665 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
666 a significant performance boost.
667 */
668 if (sampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700669 ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||
670 (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000671 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000672 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000673 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000674
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000675 while (cinfo.output_scanline < cinfo.output_height) {
676 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000677 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000678 // if row_count == 0, then we didn't get a scanline,
679 // so return early. We will return a partial image.
680 fill_below_level(cinfo.output_scanline, bm);
681 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700682 jpeg_finish_decompress(&cinfo);
683 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000684 }
685 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700686 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000687 }
688 rowptr += bpr;
689 }
690 jpeg_finish_decompress(&cinfo);
scroggo2a120802014-10-22 12:07:00 -0700691 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000692 }
693#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000694
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000695 // check for supported formats
696 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000697 int srcBytesPerPixel;
698
699 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
scroggo2a120802014-10-22 12:07:00 -0700700 return return_failure(cinfo, *bm, "jpeg colorspace");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000701 }
702
scroggo@google.com8d239242013-10-01 17:27:15 +0000703 if (!sampler.begin(bm, sc, *this)) {
scroggo2a120802014-10-22 12:07:00 -0700704 return return_failure(cinfo, *bm, "sampler.begin");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000705 }
706
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000707 SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000708 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000709
710 // Possibly skip initial rows [sampler.srcY0]
711 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
scroggo2a120802014-10-22 12:07:00 -0700712 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000713 }
714
715 // now loop through scanlines until y == bm->height() - 1
716 for (int y = 0;; y++) {
717 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
718 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
719 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000720 // if row_count == 0, then we didn't get a scanline,
721 // so return early. We will return a partial image.
722 fill_below_level(y, bm);
723 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700724 jpeg_finish_decompress(&cinfo);
725 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000726 }
727 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700728 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000729 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000730
731 if (JCS_CMYK == cinfo.out_color_space) {
732 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
733 }
734
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000735 sampler.next(srcRow);
736 if (bm->height() - 1 == y) {
737 // we're done
738 break;
739 }
740
741 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
scroggo2a120802014-10-22 12:07:00 -0700742 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000743 }
744 }
745
746 // we formally skip the rest, so we don't get a complaint from libjpeg
747 if (!skip_src_rows(&cinfo, srcRow,
748 cinfo.output_height - cinfo.output_scanline)) {
scroggo2a120802014-10-22 12:07:00 -0700749 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000750 }
751 jpeg_finish_decompress(&cinfo);
752
scroggo2a120802014-10-22 12:07:00 -0700753 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000754}
755
scroggo2a120802014-10-22 12:07:00 -0700756///////////////////////////////////////////////////////////////////////////////
757
sugoib227e372014-10-16 13:10:57 -0700758enum SizeType {
759 kSizeForMemoryAllocation_SizeType,
760 kActualSize_SizeType
761};
762
763static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component,
764 SizeType sizeType) {
765 if (sizeType == kSizeForMemoryAllocation_SizeType) {
766 return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE,
767 info.cur_comp_info[component]->height_in_blocks * DCTSIZE);
768 }
769 return SkISize::Make(info.cur_comp_info[component]->downsampled_width,
770 info.cur_comp_info[component]->downsampled_height);
771}
772
sugoib227e372014-10-16 13:10:57 -0700773static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
774 SizeType sizeType) {
775 for (int i = 0; i < 3; ++i) {
776 componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
777 }
778}
779
780static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) {
781 // U size and V size have to be the same if we're calling output_raw_data()
782 SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType);
783 SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType));
784
785 JSAMPARRAY bufferraw[3];
786 JSAMPROW bufferraw2[32];
787 bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16)
788 bufferraw[1] = &bufferraw2[16]; // U channel rows (8)
789 bufferraw[2] = &bufferraw2[24]; // V channel rows (8)
790 int yWidth = cinfo.output_width;
791 int yHeight = cinfo.output_height;
792 int yMaxH = yHeight - 1;
793 int v = cinfo.cur_comp_info[0]->v_samp_factor;
794 int uvMaxH = uvSize.height() - 1;
795 JSAMPROW outputY = static_cast<JSAMPROW>(planes[0]);
796 JSAMPROW outputU = static_cast<JSAMPROW>(planes[1]);
797 JSAMPROW outputV = static_cast<JSAMPROW>(planes[2]);
798 size_t rowBytesY = rowBytes[0];
799 size_t rowBytesU = rowBytes[1];
800 size_t rowBytesV = rowBytes[2];
801
802 int yScanlinesToRead = DCTSIZE * v;
803 SkAutoMalloc lastRowStorage(yWidth * 8);
804 JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get();
805 JSAMPROW uLastRow = yLastRow + 2 * yWidth;
806 JSAMPROW vLastRow = uLastRow + 2 * yWidth;
807 JSAMPROW dummyRow = vLastRow + 2 * yWidth;
808
809 while (cinfo.output_scanline < cinfo.output_height) {
810 // Request 8 or 16 scanlines: returns 0 or more scanlines.
811 bool hasYLastRow(false), hasUVLastRow(false);
812 // Assign 8 or 16 rows of memory to read the Y channel.
813 for (int i = 0; i < yScanlinesToRead; ++i) {
814 int scanline = (cinfo.output_scanline + i);
815 if (scanline < yMaxH) {
816 bufferraw2[i] = &outputY[scanline * rowBytesY];
817 } else if (scanline == yMaxH) {
818 bufferraw2[i] = yLastRow;
819 hasYLastRow = true;
820 } else {
821 bufferraw2[i] = dummyRow;
822 }
823 }
824 int scaledScanline = cinfo.output_scanline / v;
825 // Assign 8 rows of memory to read the U and V channels.
826 for (int i = 0; i < 8; ++i) {
827 int scanline = (scaledScanline + i);
828 if (scanline < uvMaxH) {
829 bufferraw2[16 + i] = &outputU[scanline * rowBytesU];
830 bufferraw2[24 + i] = &outputV[scanline * rowBytesV];
831 } else if (scanline == uvMaxH) {
832 bufferraw2[16 + i] = uLastRow;
833 bufferraw2[24 + i] = vLastRow;
834 hasUVLastRow = true;
835 } else {
836 bufferraw2[16 + i] = dummyRow;
837 bufferraw2[24 + i] = dummyRow;
838 }
839 }
840 JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead);
841
scroggo2a120802014-10-22 12:07:00 -0700842 if (scanlinesRead == 0) {
sugoib227e372014-10-16 13:10:57 -0700843 return false;
scroggo2a120802014-10-22 12:07:00 -0700844 }
sugoib227e372014-10-16 13:10:57 -0700845
846 if (hasYLastRow) {
847 memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth);
848 }
849 if (hasUVLastRow) {
850 memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width());
851 memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width());
852 }
853 }
854
855 cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height);
856
857 return true;
858}
859
860bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
861 void* planes[3], size_t rowBytes[3],
862 SkYUVColorSpace* colorSpace) {
863#ifdef TIME_DECODE
864 SkAutoTime atm("JPEG YUV8 Decode");
865#endif
866
867 if (this->getSampleSize() != 1) {
868 return false; // Resizing not supported
869 }
870
871 JPEGAutoClean autoClean;
872
873 jpeg_decompress_struct cinfo;
874 skjpeg_source_mgr srcManager(stream, this);
875
876 skjpeg_error_mgr errorManager;
877 set_error_mgr(&cinfo, &errorManager);
878
879 // All objects need to be instantiated before this setjmp call so that
880 // they will be cleaned up properly if an error occurs.
881 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700882 return return_false(cinfo, "setjmp YUV8");
sugoib227e372014-10-16 13:10:57 -0700883 }
884
885 initialize_info(&cinfo, &srcManager);
886 autoClean.set(&cinfo);
887
888 int status = jpeg_read_header(&cinfo, true);
889 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700890 return return_false(cinfo, "read_header YUV8");
sugoib227e372014-10-16 13:10:57 -0700891 }
892
893 if (cinfo.jpeg_color_space != JCS_YCbCr) {
894 // It's not an error to not be encoded in YUV, so no need to use return_false()
895 return false;
896 }
897
898 cinfo.out_color_space = JCS_YCbCr;
899 cinfo.raw_data_out = TRUE;
900
901 if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only
902 update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType);
903 return true;
904 }
905
906 set_dct_method(*this, &cinfo);
907
908 SkASSERT(1 == cinfo.scale_num);
909 cinfo.scale_denom = 1;
910
911 turn_off_visual_optimizations(&cinfo);
912
913#ifdef ANDROID_RGB
914 cinfo.dither_mode = JDITHER_NONE;
915#endif
916
917 /* image_width and image_height are the original dimensions, available
918 after jpeg_read_header(). To see the scaled dimensions, we have to call
919 jpeg_start_decompress(), and then read output_width and output_height.
920 */
921 if (!jpeg_start_decompress(&cinfo)) {
scroggo2a120802014-10-22 12:07:00 -0700922 return return_false(cinfo, "start_decompress YUV8");
sugoib227e372014-10-16 13:10:57 -0700923 }
924
925 if (!output_raw_data(cinfo, planes, rowBytes)) {
scroggo2a120802014-10-22 12:07:00 -0700926 return return_false(cinfo, "output_raw_data");
sugoib227e372014-10-16 13:10:57 -0700927 }
928
929 update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
930 jpeg_finish_decompress(&cinfo);
931
932 if (NULL != colorSpace) {
933 *colorSpace = kJPEG_SkYUVColorSpace;
934 }
935
936 return true;
937}
938
scroggo2a120802014-10-22 12:07:00 -0700939///////////////////////////////////////////////////////////////////////////////
940
scroggo@google.comd79277f2013-08-07 19:53:53 +0000941#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000942bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000943
scroggo@google.coma1a51542013-08-07 21:02:32 +0000944 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000945 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000946
947 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000948 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000949
950 // All objects need to be instantiated before this setjmp call so that
951 // they will be cleaned up properly if an error occurs.
952 if (setjmp(sk_err.fJmpBuf)) {
953 return false;
954 }
955
956 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000957 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000958 return false;
959 }
960
scroggo@google.coma1a51542013-08-07 21:02:32 +0000961 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000962 return false;
963 }
964
965 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000966 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000967
968 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000969 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000970 return false;
971 }
972
scroggo@google.com590a5af2013-08-07 21:09:13 +0000973 // FIXME: This sets cinfo->out_color_space, which we may change later
974 // based on the config in onDecodeSubset. This should be fine, since
975 // jpeg_init_read_tile_scanline will check out_color_space again after
976 // that change (when it calls jinit_color_deconverter).
reed6c225732014-06-09 19:52:07 -0700977 (void) this->getBitmapColorType(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000978
979 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000980
981 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000982 if (!imageIndex->startTileDecompress()) {
983 return false;
984 }
djsollen@google.com11399402013-03-20 17:45:27 +0000985
scroggo@google.coma1a51542013-08-07 21:02:32 +0000986 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000987 fImageWidth = cinfo->output_width;
988 fImageHeight = cinfo->output_height;
989
990 if (width) {
991 *width = fImageWidth;
992 }
993 if (height) {
994 *height = fImageHeight;
995 }
djsollen@google.com11399402013-03-20 17:45:27 +0000996
997 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000998 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +0000999
1000 return true;
1001}
1002
scroggo@google.com7e6fcee2013-05-03 20:14:28 +00001003bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +00001004 if (NULL == fImageIndex) {
1005 return false;
1006 }
1007 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
1008
1009 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
1010 if (!rect.intersect(region)) {
1011 // If the requested region is entirely outside the image return false
1012 return false;
1013 }
1014
1015
1016 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +00001017 set_error_mgr(cinfo, &errorManager);
1018
djsollen@google.com11399402013-03-20 17:45:27 +00001019 if (setjmp(errorManager.fJmpBuf)) {
1020 return false;
1021 }
1022
1023 int requestedSampleSize = this->getSampleSize();
1024 cinfo->scale_denom = requestedSampleSize;
1025
scroggo@google.com590a5af2013-08-07 21:09:13 +00001026 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +00001027
reed6c225732014-06-09 19:52:07 -07001028 const SkColorType colorType = this->getBitmapColorType(cinfo);
1029 adjust_out_color_space_and_dither(cinfo, colorType, *this);
djsollen@google.com11399402013-03-20 17:45:27 +00001030
1031 int startX = rect.fLeft;
1032 int startY = rect.fTop;
1033 int width = rect.width();
1034 int height = rect.height();
1035
1036 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
1037 &startX, &startY, &width, &height);
1038 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
1039 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
1040
1041 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
1042
1043 SkBitmap bitmap;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001044 // Assume an A8 bitmap is not opaque to avoid the check of each
1045 // individual pixel. It is very unlikely to be opaque, since
1046 // an opaque A8 bitmap would not be very interesting.
1047 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -07001048 bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType,
1049 kAlpha_8_SkColorType == colorType ?
1050 kPremul_SkAlphaType : kOpaque_SkAlphaType));
djsollen@google.com11399402013-03-20 17:45:27 +00001051
1052 // Check ahead of time if the swap(dest, src) is possible or not.
1053 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
1054 // swap happening. If no, then we will use alloc to allocate pixels to
1055 // prevent garbage collection.
1056 int w = rect.width() / actualSampleSize;
1057 int h = rect.height() / actualSampleSize;
1058 bool swapOnly = (rect == region) && bm->isNull() &&
1059 (w == bitmap.width()) && (h == bitmap.height()) &&
1060 ((startX - rect.x()) / actualSampleSize == 0) &&
1061 ((startY - rect.y()) / actualSampleSize == 0);
1062 if (swapOnly) {
1063 if (!this->allocPixelRef(&bitmap, NULL)) {
1064 return return_false(*cinfo, bitmap, "allocPixelRef");
1065 }
1066 } else {
reed84825042014-09-02 12:50:45 -07001067 if (!bitmap.tryAllocPixels()) {
djsollen@google.com11399402013-03-20 17:45:27 +00001068 return return_false(*cinfo, bitmap, "allocPixels");
1069 }
1070 }
1071
1072 SkAutoLockPixels alp(bitmap);
1073
1074#ifdef ANDROID_RGB
1075 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
1076 a significant performance boost.
1077 */
1078 if (skiaSampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -07001079 ((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||
1080 (kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565)))
djsollen@google.com11399402013-03-20 17:45:27 +00001081 {
1082 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
1083 INT32 const bpr = bitmap.rowBytes();
1084 int rowTotalCount = 0;
1085
1086 while (rowTotalCount < height) {
1087 int rowCount = jpeg_read_tile_scanline(cinfo,
1088 fImageIndex->huffmanIndex(),
1089 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001090 // if rowCount == 0, then we didn't get a scanline, so abort.
1091 // onDecodeSubset() relies on onBuildTileIndex(), which
1092 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001093 if (0 == rowCount) {
1094 return return_false(*cinfo, bitmap, "read_scanlines");
1095 }
1096 if (this->shouldCancelDecode()) {
1097 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1098 }
1099 rowTotalCount += rowCount;
1100 rowptr += bpr;
1101 }
1102
1103 if (swapOnly) {
1104 bm->swap(bitmap);
1105 } else {
1106 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1107 region.width(), region.height(), startX, startY);
1108 }
1109 return true;
1110 }
1111#endif
1112
1113 // check for supported formats
1114 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001115 int srcBytesPerPixel;
1116
1117 if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001118 return return_false(*cinfo, *bm, "jpeg colorspace");
1119 }
1120
scroggo@google.com8d239242013-10-01 17:27:15 +00001121 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001122 return return_false(*cinfo, bitmap, "sampler.begin");
1123 }
1124
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001125 SkAutoMalloc srcStorage(width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +00001126 uint8_t* srcRow = (uint8_t*)srcStorage.get();
1127
1128 // Possibly skip initial rows [sampler.srcY0]
1129 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
1130 return return_false(*cinfo, bitmap, "skip rows");
1131 }
1132
1133 // now loop through scanlines until y == bitmap->height() - 1
1134 for (int y = 0;; y++) {
1135 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
1136 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001137 // if row_count == 0, then we didn't get a scanline, so abort.
1138 // onDecodeSubset() relies on onBuildTileIndex(), which
1139 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001140 if (0 == row_count) {
1141 return return_false(*cinfo, bitmap, "read_scanlines");
1142 }
1143 if (this->shouldCancelDecode()) {
1144 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1145 }
1146
1147 if (JCS_CMYK == cinfo->out_color_space) {
1148 convert_CMYK_to_RGB(srcRow, width);
1149 }
1150
1151 sampler.next(srcRow);
1152 if (bitmap.height() - 1 == y) {
1153 // we're done
1154 break;
1155 }
1156
1157 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
1158 sampler.srcDY() - 1)) {
1159 return return_false(*cinfo, bitmap, "skip rows");
1160 }
1161 }
1162 if (swapOnly) {
1163 bm->swap(bitmap);
1164 } else {
1165 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1166 region.width(), region.height(), startX, startY);
1167 }
1168 return true;
1169}
1170#endif
1171
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001172///////////////////////////////////////////////////////////////////////////////
1173
1174#include "SkColorPriv.h"
1175
1176// taken from jcolor.c in libjpeg
1177#if 0 // 16bit - precise but slow
1178 #define CYR 19595 // 0.299
1179 #define CYG 38470 // 0.587
1180 #define CYB 7471 // 0.114
1181
1182 #define CUR -11059 // -0.16874
1183 #define CUG -21709 // -0.33126
1184 #define CUB 32768 // 0.5
1185
1186 #define CVR 32768 // 0.5
1187 #define CVG -27439 // -0.41869
1188 #define CVB -5329 // -0.08131
1189
1190 #define CSHIFT 16
1191#else // 8bit - fast, slightly less precise
1192 #define CYR 77 // 0.299
1193 #define CYG 150 // 0.587
1194 #define CYB 29 // 0.114
1195
1196 #define CUR -43 // -0.16874
1197 #define CUG -85 // -0.33126
1198 #define CUB 128 // 0.5
1199
1200 #define CVR 128 // 0.5
1201 #define CVG -107 // -0.41869
1202 #define CVB -21 // -0.08131
1203
1204 #define CSHIFT 8
1205#endif
1206
1207static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
1208 int r = SkGetPackedR32(c);
1209 int g = SkGetPackedG32(c);
1210 int b = SkGetPackedB32(c);
1211
1212 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
1213 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
1214 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
1215
1216 dst[0] = SkToU8(y);
1217 dst[1] = SkToU8(u + 128);
1218 dst[2] = SkToU8(v + 128);
1219}
1220
1221static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
1222 int r = SkGetPackedR4444(c);
1223 int g = SkGetPackedG4444(c);
1224 int b = SkGetPackedB4444(c);
1225
1226 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
1227 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
1228 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
1229
1230 dst[0] = SkToU8(y);
1231 dst[1] = SkToU8(u + 128);
1232 dst[2] = SkToU8(v + 128);
1233}
1234
1235static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
1236 int r = SkGetPackedR16(c);
1237 int g = SkGetPackedG16(c);
1238 int b = SkGetPackedB16(c);
1239
1240 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
1241 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
1242 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
1243
1244 dst[0] = SkToU8(y);
1245 dst[1] = SkToU8(u + 128);
1246 dst[2] = SkToU8(v + 128);
1247}
1248
1249///////////////////////////////////////////////////////////////////////////////
1250
1251typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1252 const void* SK_RESTRICT src, int width,
1253 const SkPMColor* SK_RESTRICT ctable);
1254
1255static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1256 const void* SK_RESTRICT srcRow, int width,
1257 const SkPMColor*) {
1258 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1259 while (--width >= 0) {
1260#ifdef WE_CONVERT_TO_YUV
1261 rgb2yuv_32(dst, *src++);
1262#else
1263 uint32_t c = *src++;
1264 dst[0] = SkGetPackedR32(c);
1265 dst[1] = SkGetPackedG32(c);
1266 dst[2] = SkGetPackedB32(c);
1267#endif
1268 dst += 3;
1269 }
1270}
1271
1272static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1273 const void* SK_RESTRICT srcRow, int width,
1274 const SkPMColor*) {
1275 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1276 while (--width >= 0) {
1277#ifdef WE_CONVERT_TO_YUV
1278 rgb2yuv_4444(dst, *src++);
1279#else
1280 SkPMColor16 c = *src++;
1281 dst[0] = SkPacked4444ToR32(c);
1282 dst[1] = SkPacked4444ToG32(c);
1283 dst[2] = SkPacked4444ToB32(c);
1284#endif
1285 dst += 3;
1286 }
1287}
1288
1289static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1290 const void* SK_RESTRICT srcRow, int width,
1291 const SkPMColor*) {
1292 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1293 while (--width >= 0) {
1294#ifdef WE_CONVERT_TO_YUV
1295 rgb2yuv_16(dst, *src++);
1296#else
1297 uint16_t c = *src++;
1298 dst[0] = SkPacked16ToR32(c);
1299 dst[1] = SkPacked16ToG32(c);
1300 dst[2] = SkPacked16ToB32(c);
1301#endif
1302 dst += 3;
1303 }
1304}
1305
1306static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1307 const void* SK_RESTRICT srcRow, int width,
1308 const SkPMColor* SK_RESTRICT ctable) {
1309 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1310 while (--width >= 0) {
1311#ifdef WE_CONVERT_TO_YUV
1312 rgb2yuv_32(dst, ctable[*src++]);
1313#else
1314 uint32_t c = ctable[*src++];
1315 dst[0] = SkGetPackedR32(c);
1316 dst[1] = SkGetPackedG32(c);
1317 dst[2] = SkGetPackedB32(c);
1318#endif
1319 dst += 3;
1320 }
1321}
1322
1323static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -07001324 switch (bm.colorType()) {
1325 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001326 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -07001327 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001328 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -07001329 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001330 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -07001331 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001332 return Write_Index_YUV;
1333 default:
1334 return NULL;
1335 }
1336}
1337
1338class SkJPEGImageEncoder : public SkImageEncoder {
1339protected:
1340 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1341#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001342 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001343#endif
1344
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001345 SkAutoLockPixels alp(bm);
1346 if (NULL == bm.getPixels()) {
1347 return false;
1348 }
1349
1350 jpeg_compress_struct cinfo;
1351 skjpeg_error_mgr sk_err;
1352 skjpeg_destination_mgr sk_wstream(stream);
1353
1354 // allocate these before set call setjmp
1355 SkAutoMalloc oneRow;
1356 SkAutoLockColors ctLocker;
1357
1358 cinfo.err = jpeg_std_error(&sk_err);
1359 sk_err.error_exit = skjpeg_error_exit;
1360 if (setjmp(sk_err.fJmpBuf)) {
1361 return false;
1362 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001363
mtklein@google.com8d725b22013-07-24 16:20:05 +00001364 // Keep after setjmp or mark volatile.
1365 const WriteScanline writer = ChooseWriter(bm);
1366 if (NULL == writer) {
1367 return false;
1368 }
1369
1370 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001371 cinfo.dest = &sk_wstream;
1372 cinfo.image_width = bm.width();
1373 cinfo.image_height = bm.height();
1374 cinfo.input_components = 3;
1375#ifdef WE_CONVERT_TO_YUV
1376 cinfo.in_color_space = JCS_YCbCr;
1377#else
1378 cinfo.in_color_space = JCS_RGB;
1379#endif
1380 cinfo.input_gamma = 1;
1381
1382 jpeg_set_defaults(&cinfo);
1383 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001384#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001385 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001386#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001387
1388 jpeg_start_compress(&cinfo, TRUE);
1389
1390 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001391 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001392
1393 const SkPMColor* colors = ctLocker.lockColors(bm);
1394 const void* srcRow = bm.getPixels();
1395
1396 while (cinfo.next_scanline < cinfo.image_height) {
1397 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1398
1399 writer(oneRowP, srcRow, width, colors);
1400 row_pointer[0] = oneRowP;
1401 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1402 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1403 }
1404
1405 jpeg_finish_compress(&cinfo);
1406 jpeg_destroy_compress(&cinfo);
1407
1408 return true;
1409 }
1410};
1411
1412///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001413DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1414DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1415///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001416
scroggo@google.comb5571b32013-09-25 21:34:24 +00001417static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001418 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001419 static const size_t HEADER_SIZE = sizeof(gHeader);
1420
1421 char buffer[HEADER_SIZE];
1422 size_t len = stream->read(buffer, HEADER_SIZE);
1423
1424 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001425 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001426 }
1427 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001428 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001429 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001430 return true;
1431}
1432
scroggo@google.comb5571b32013-09-25 21:34:24 +00001433
1434static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001435 if (is_jpeg(stream)) {
1436 return SkNEW(SkJPEGImageDecoder);
1437 }
1438 return NULL;
1439}
1440
scroggo@google.comb5571b32013-09-25 21:34:24 +00001441static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001442 if (is_jpeg(stream)) {
1443 return SkImageDecoder::kJPEG_Format;
1444 }
1445 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001446}
1447
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001448static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001449 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1450}
1451
mtklein@google.combd6343b2013-09-04 17:20:18 +00001452static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1453static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1454static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);