blob: 77d1c5feff2fdf038e12f20efe6d8e8a30a22c7e [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
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
reed5926b862014-06-11 10:33:13 -0700643#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed6c225732014-06-09 19:52:07 -0700644 // should we allow the Chooser (if present) to pick a colortype for us???
645 if (!this->chooseFromOneChoice(colorType, cinfo.output_width, cinfo.output_height)) {
scroggo2a120802014-10-22 12:07:00 -0700646 return return_failure(cinfo, *bm, "chooseFromOneChoice");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000647 }
reed5926b862014-06-11 10:33:13 -0700648#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000649
djsollen@google.com11399402013-03-20 17:45:27 +0000650 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000651 // Assume an A8 bitmap is not opaque to avoid the check of each
652 // individual pixel. It is very unlikely to be opaque, since
653 // an opaque A8 bitmap would not be very interesting.
654 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700655 bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
656 colorType, alphaType));
scroggo@google.combc69ce92013-07-09 15:45:14 +0000657 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo2a120802014-10-22 12:07:00 -0700658 return kSuccess;
scroggo@google.combc69ce92013-07-09 15:45:14 +0000659 }
660 if (!this->allocPixelRef(bm, NULL)) {
scroggo2a120802014-10-22 12:07:00 -0700661 return return_failure(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000662 }
663
664 SkAutoLockPixels alp(*bm);
665
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000666#ifdef ANDROID_RGB
667 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
668 a significant performance boost.
669 */
670 if (sampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700671 ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||
672 (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000673 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000674 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000675 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000676
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000677 while (cinfo.output_scanline < cinfo.output_height) {
678 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000679 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000680 // if row_count == 0, then we didn't get a scanline,
681 // so return early. We will return a partial image.
682 fill_below_level(cinfo.output_scanline, bm);
683 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700684 jpeg_finish_decompress(&cinfo);
685 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000686 }
687 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700688 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000689 }
690 rowptr += bpr;
691 }
692 jpeg_finish_decompress(&cinfo);
scroggo2a120802014-10-22 12:07:00 -0700693 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000694 }
695#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000696
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000697 // check for supported formats
698 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000699 int srcBytesPerPixel;
700
701 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
scroggo2a120802014-10-22 12:07:00 -0700702 return return_failure(cinfo, *bm, "jpeg colorspace");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000703 }
704
scroggo@google.com8d239242013-10-01 17:27:15 +0000705 if (!sampler.begin(bm, sc, *this)) {
scroggo2a120802014-10-22 12:07:00 -0700706 return return_failure(cinfo, *bm, "sampler.begin");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000707 }
708
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000709 SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000710 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000711
712 // Possibly skip initial rows [sampler.srcY0]
713 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
scroggo2a120802014-10-22 12:07:00 -0700714 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000715 }
716
717 // now loop through scanlines until y == bm->height() - 1
718 for (int y = 0;; y++) {
719 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
720 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
721 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000722 // if row_count == 0, then we didn't get a scanline,
723 // so return early. We will return a partial image.
724 fill_below_level(y, bm);
725 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700726 jpeg_finish_decompress(&cinfo);
727 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000728 }
729 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700730 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000731 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000732
733 if (JCS_CMYK == cinfo.out_color_space) {
734 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
735 }
736
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000737 sampler.next(srcRow);
738 if (bm->height() - 1 == y) {
739 // we're done
740 break;
741 }
742
743 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
scroggo2a120802014-10-22 12:07:00 -0700744 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000745 }
746 }
747
748 // we formally skip the rest, so we don't get a complaint from libjpeg
749 if (!skip_src_rows(&cinfo, srcRow,
750 cinfo.output_height - cinfo.output_scanline)) {
scroggo2a120802014-10-22 12:07:00 -0700751 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000752 }
753 jpeg_finish_decompress(&cinfo);
754
scroggo2a120802014-10-22 12:07:00 -0700755 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000756}
757
scroggo2a120802014-10-22 12:07:00 -0700758///////////////////////////////////////////////////////////////////////////////
759
sugoib227e372014-10-16 13:10:57 -0700760enum SizeType {
761 kSizeForMemoryAllocation_SizeType,
762 kActualSize_SizeType
763};
764
765static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component,
766 SizeType sizeType) {
767 if (sizeType == kSizeForMemoryAllocation_SizeType) {
768 return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE,
769 info.cur_comp_info[component]->height_in_blocks * DCTSIZE);
770 }
771 return SkISize::Make(info.cur_comp_info[component]->downsampled_width,
772 info.cur_comp_info[component]->downsampled_height);
773}
774
sugoib227e372014-10-16 13:10:57 -0700775static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
776 SizeType sizeType) {
777 for (int i = 0; i < 3; ++i) {
778 componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
779 }
780}
781
782static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) {
783 // U size and V size have to be the same if we're calling output_raw_data()
784 SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType);
785 SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType));
786
787 JSAMPARRAY bufferraw[3];
788 JSAMPROW bufferraw2[32];
789 bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16)
790 bufferraw[1] = &bufferraw2[16]; // U channel rows (8)
791 bufferraw[2] = &bufferraw2[24]; // V channel rows (8)
792 int yWidth = cinfo.output_width;
793 int yHeight = cinfo.output_height;
794 int yMaxH = yHeight - 1;
795 int v = cinfo.cur_comp_info[0]->v_samp_factor;
796 int uvMaxH = uvSize.height() - 1;
797 JSAMPROW outputY = static_cast<JSAMPROW>(planes[0]);
798 JSAMPROW outputU = static_cast<JSAMPROW>(planes[1]);
799 JSAMPROW outputV = static_cast<JSAMPROW>(planes[2]);
800 size_t rowBytesY = rowBytes[0];
801 size_t rowBytesU = rowBytes[1];
802 size_t rowBytesV = rowBytes[2];
803
804 int yScanlinesToRead = DCTSIZE * v;
805 SkAutoMalloc lastRowStorage(yWidth * 8);
806 JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get();
807 JSAMPROW uLastRow = yLastRow + 2 * yWidth;
808 JSAMPROW vLastRow = uLastRow + 2 * yWidth;
809 JSAMPROW dummyRow = vLastRow + 2 * yWidth;
810
811 while (cinfo.output_scanline < cinfo.output_height) {
812 // Request 8 or 16 scanlines: returns 0 or more scanlines.
813 bool hasYLastRow(false), hasUVLastRow(false);
814 // Assign 8 or 16 rows of memory to read the Y channel.
815 for (int i = 0; i < yScanlinesToRead; ++i) {
816 int scanline = (cinfo.output_scanline + i);
817 if (scanline < yMaxH) {
818 bufferraw2[i] = &outputY[scanline * rowBytesY];
819 } else if (scanline == yMaxH) {
820 bufferraw2[i] = yLastRow;
821 hasYLastRow = true;
822 } else {
823 bufferraw2[i] = dummyRow;
824 }
825 }
826 int scaledScanline = cinfo.output_scanline / v;
827 // Assign 8 rows of memory to read the U and V channels.
828 for (int i = 0; i < 8; ++i) {
829 int scanline = (scaledScanline + i);
830 if (scanline < uvMaxH) {
831 bufferraw2[16 + i] = &outputU[scanline * rowBytesU];
832 bufferraw2[24 + i] = &outputV[scanline * rowBytesV];
833 } else if (scanline == uvMaxH) {
834 bufferraw2[16 + i] = uLastRow;
835 bufferraw2[24 + i] = vLastRow;
836 hasUVLastRow = true;
837 } else {
838 bufferraw2[16 + i] = dummyRow;
839 bufferraw2[24 + i] = dummyRow;
840 }
841 }
842 JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead);
843
scroggo2a120802014-10-22 12:07:00 -0700844 if (scanlinesRead == 0) {
sugoib227e372014-10-16 13:10:57 -0700845 return false;
scroggo2a120802014-10-22 12:07:00 -0700846 }
sugoib227e372014-10-16 13:10:57 -0700847
848 if (hasYLastRow) {
849 memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth);
850 }
851 if (hasUVLastRow) {
852 memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width());
853 memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width());
854 }
855 }
856
857 cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height);
858
859 return true;
860}
861
862bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
863 void* planes[3], size_t rowBytes[3],
864 SkYUVColorSpace* colorSpace) {
865#ifdef TIME_DECODE
866 SkAutoTime atm("JPEG YUV8 Decode");
867#endif
868
869 if (this->getSampleSize() != 1) {
870 return false; // Resizing not supported
871 }
872
873 JPEGAutoClean autoClean;
874
875 jpeg_decompress_struct cinfo;
876 skjpeg_source_mgr srcManager(stream, this);
877
878 skjpeg_error_mgr errorManager;
879 set_error_mgr(&cinfo, &errorManager);
880
881 // All objects need to be instantiated before this setjmp call so that
882 // they will be cleaned up properly if an error occurs.
883 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700884 return return_false(cinfo, "setjmp YUV8");
sugoib227e372014-10-16 13:10:57 -0700885 }
886
887 initialize_info(&cinfo, &srcManager);
888 autoClean.set(&cinfo);
889
890 int status = jpeg_read_header(&cinfo, true);
891 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700892 return return_false(cinfo, "read_header YUV8");
sugoib227e372014-10-16 13:10:57 -0700893 }
894
895 if (cinfo.jpeg_color_space != JCS_YCbCr) {
896 // It's not an error to not be encoded in YUV, so no need to use return_false()
897 return false;
898 }
899
900 cinfo.out_color_space = JCS_YCbCr;
901 cinfo.raw_data_out = TRUE;
902
903 if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only
904 update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType);
905 return true;
906 }
907
908 set_dct_method(*this, &cinfo);
909
910 SkASSERT(1 == cinfo.scale_num);
911 cinfo.scale_denom = 1;
912
913 turn_off_visual_optimizations(&cinfo);
914
915#ifdef ANDROID_RGB
916 cinfo.dither_mode = JDITHER_NONE;
917#endif
918
919 /* image_width and image_height are the original dimensions, available
920 after jpeg_read_header(). To see the scaled dimensions, we have to call
921 jpeg_start_decompress(), and then read output_width and output_height.
922 */
923 if (!jpeg_start_decompress(&cinfo)) {
scroggo2a120802014-10-22 12:07:00 -0700924 return return_false(cinfo, "start_decompress YUV8");
sugoib227e372014-10-16 13:10:57 -0700925 }
926
927 if (!output_raw_data(cinfo, planes, rowBytes)) {
scroggo2a120802014-10-22 12:07:00 -0700928 return return_false(cinfo, "output_raw_data");
sugoib227e372014-10-16 13:10:57 -0700929 }
930
931 update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
932 jpeg_finish_decompress(&cinfo);
933
934 if (NULL != colorSpace) {
935 *colorSpace = kJPEG_SkYUVColorSpace;
936 }
937
938 return true;
939}
940
scroggo2a120802014-10-22 12:07:00 -0700941///////////////////////////////////////////////////////////////////////////////
942
scroggo@google.comd79277f2013-08-07 19:53:53 +0000943#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000944bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000945
scroggo@google.coma1a51542013-08-07 21:02:32 +0000946 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000947 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000948
949 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000950 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000951
952 // All objects need to be instantiated before this setjmp call so that
953 // they will be cleaned up properly if an error occurs.
954 if (setjmp(sk_err.fJmpBuf)) {
955 return false;
956 }
957
958 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000959 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000960 return false;
961 }
962
scroggo@google.coma1a51542013-08-07 21:02:32 +0000963 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000964 return false;
965 }
966
967 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000968 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000969
970 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000971 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000972 return false;
973 }
974
scroggo@google.com590a5af2013-08-07 21:09:13 +0000975 // FIXME: This sets cinfo->out_color_space, which we may change later
976 // based on the config in onDecodeSubset. This should be fine, since
977 // jpeg_init_read_tile_scanline will check out_color_space again after
978 // that change (when it calls jinit_color_deconverter).
reed6c225732014-06-09 19:52:07 -0700979 (void) this->getBitmapColorType(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000980
981 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000982
983 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000984 if (!imageIndex->startTileDecompress()) {
985 return false;
986 }
djsollen@google.com11399402013-03-20 17:45:27 +0000987
scroggo@google.coma1a51542013-08-07 21:02:32 +0000988 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000989 fImageWidth = cinfo->output_width;
990 fImageHeight = cinfo->output_height;
991
992 if (width) {
993 *width = fImageWidth;
994 }
995 if (height) {
996 *height = fImageHeight;
997 }
djsollen@google.com11399402013-03-20 17:45:27 +0000998
999 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +00001000 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +00001001
1002 return true;
1003}
1004
scroggo@google.com7e6fcee2013-05-03 20:14:28 +00001005bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +00001006 if (NULL == fImageIndex) {
1007 return false;
1008 }
1009 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
1010
1011 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
1012 if (!rect.intersect(region)) {
1013 // If the requested region is entirely outside the image return false
1014 return false;
1015 }
1016
1017
1018 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +00001019 set_error_mgr(cinfo, &errorManager);
1020
djsollen@google.com11399402013-03-20 17:45:27 +00001021 if (setjmp(errorManager.fJmpBuf)) {
1022 return false;
1023 }
1024
1025 int requestedSampleSize = this->getSampleSize();
1026 cinfo->scale_denom = requestedSampleSize;
1027
scroggo@google.com590a5af2013-08-07 21:09:13 +00001028 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +00001029
reed6c225732014-06-09 19:52:07 -07001030 const SkColorType colorType = this->getBitmapColorType(cinfo);
1031 adjust_out_color_space_and_dither(cinfo, colorType, *this);
djsollen@google.com11399402013-03-20 17:45:27 +00001032
1033 int startX = rect.fLeft;
1034 int startY = rect.fTop;
1035 int width = rect.width();
1036 int height = rect.height();
1037
1038 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
1039 &startX, &startY, &width, &height);
1040 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
1041 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
1042
1043 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
1044
1045 SkBitmap bitmap;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001046 // Assume an A8 bitmap is not opaque to avoid the check of each
1047 // individual pixel. It is very unlikely to be opaque, since
1048 // an opaque A8 bitmap would not be very interesting.
1049 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -07001050 bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType,
1051 kAlpha_8_SkColorType == colorType ?
1052 kPremul_SkAlphaType : kOpaque_SkAlphaType));
djsollen@google.com11399402013-03-20 17:45:27 +00001053
1054 // Check ahead of time if the swap(dest, src) is possible or not.
1055 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
1056 // swap happening. If no, then we will use alloc to allocate pixels to
1057 // prevent garbage collection.
1058 int w = rect.width() / actualSampleSize;
1059 int h = rect.height() / actualSampleSize;
1060 bool swapOnly = (rect == region) && bm->isNull() &&
1061 (w == bitmap.width()) && (h == bitmap.height()) &&
1062 ((startX - rect.x()) / actualSampleSize == 0) &&
1063 ((startY - rect.y()) / actualSampleSize == 0);
1064 if (swapOnly) {
1065 if (!this->allocPixelRef(&bitmap, NULL)) {
1066 return return_false(*cinfo, bitmap, "allocPixelRef");
1067 }
1068 } else {
reed84825042014-09-02 12:50:45 -07001069 if (!bitmap.tryAllocPixels()) {
djsollen@google.com11399402013-03-20 17:45:27 +00001070 return return_false(*cinfo, bitmap, "allocPixels");
1071 }
1072 }
1073
1074 SkAutoLockPixels alp(bitmap);
1075
1076#ifdef ANDROID_RGB
1077 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
1078 a significant performance boost.
1079 */
1080 if (skiaSampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -07001081 ((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||
1082 (kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565)))
djsollen@google.com11399402013-03-20 17:45:27 +00001083 {
1084 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
1085 INT32 const bpr = bitmap.rowBytes();
1086 int rowTotalCount = 0;
1087
1088 while (rowTotalCount < height) {
1089 int rowCount = jpeg_read_tile_scanline(cinfo,
1090 fImageIndex->huffmanIndex(),
1091 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001092 // if rowCount == 0, then we didn't get a scanline, so abort.
1093 // onDecodeSubset() relies on onBuildTileIndex(), which
1094 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001095 if (0 == rowCount) {
1096 return return_false(*cinfo, bitmap, "read_scanlines");
1097 }
1098 if (this->shouldCancelDecode()) {
1099 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1100 }
1101 rowTotalCount += rowCount;
1102 rowptr += bpr;
1103 }
1104
1105 if (swapOnly) {
1106 bm->swap(bitmap);
1107 } else {
1108 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1109 region.width(), region.height(), startX, startY);
1110 }
1111 return true;
1112 }
1113#endif
1114
1115 // check for supported formats
1116 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001117 int srcBytesPerPixel;
1118
1119 if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001120 return return_false(*cinfo, *bm, "jpeg colorspace");
1121 }
1122
scroggo@google.com8d239242013-10-01 17:27:15 +00001123 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001124 return return_false(*cinfo, bitmap, "sampler.begin");
1125 }
1126
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001127 SkAutoMalloc srcStorage(width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +00001128 uint8_t* srcRow = (uint8_t*)srcStorage.get();
1129
1130 // Possibly skip initial rows [sampler.srcY0]
1131 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
1132 return return_false(*cinfo, bitmap, "skip rows");
1133 }
1134
1135 // now loop through scanlines until y == bitmap->height() - 1
1136 for (int y = 0;; y++) {
1137 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
1138 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001139 // if row_count == 0, then we didn't get a scanline, so abort.
1140 // onDecodeSubset() relies on onBuildTileIndex(), which
1141 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001142 if (0 == row_count) {
1143 return return_false(*cinfo, bitmap, "read_scanlines");
1144 }
1145 if (this->shouldCancelDecode()) {
1146 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1147 }
1148
1149 if (JCS_CMYK == cinfo->out_color_space) {
1150 convert_CMYK_to_RGB(srcRow, width);
1151 }
1152
1153 sampler.next(srcRow);
1154 if (bitmap.height() - 1 == y) {
1155 // we're done
1156 break;
1157 }
1158
1159 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
1160 sampler.srcDY() - 1)) {
1161 return return_false(*cinfo, bitmap, "skip rows");
1162 }
1163 }
1164 if (swapOnly) {
1165 bm->swap(bitmap);
1166 } else {
1167 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1168 region.width(), region.height(), startX, startY);
1169 }
1170 return true;
1171}
1172#endif
1173
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001174///////////////////////////////////////////////////////////////////////////////
1175
1176#include "SkColorPriv.h"
1177
1178// taken from jcolor.c in libjpeg
1179#if 0 // 16bit - precise but slow
1180 #define CYR 19595 // 0.299
1181 #define CYG 38470 // 0.587
1182 #define CYB 7471 // 0.114
1183
1184 #define CUR -11059 // -0.16874
1185 #define CUG -21709 // -0.33126
1186 #define CUB 32768 // 0.5
1187
1188 #define CVR 32768 // 0.5
1189 #define CVG -27439 // -0.41869
1190 #define CVB -5329 // -0.08131
1191
1192 #define CSHIFT 16
1193#else // 8bit - fast, slightly less precise
1194 #define CYR 77 // 0.299
1195 #define CYG 150 // 0.587
1196 #define CYB 29 // 0.114
1197
1198 #define CUR -43 // -0.16874
1199 #define CUG -85 // -0.33126
1200 #define CUB 128 // 0.5
1201
1202 #define CVR 128 // 0.5
1203 #define CVG -107 // -0.41869
1204 #define CVB -21 // -0.08131
1205
1206 #define CSHIFT 8
1207#endif
1208
1209static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
1210 int r = SkGetPackedR32(c);
1211 int g = SkGetPackedG32(c);
1212 int b = SkGetPackedB32(c);
1213
1214 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
1215 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
1216 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
1217
1218 dst[0] = SkToU8(y);
1219 dst[1] = SkToU8(u + 128);
1220 dst[2] = SkToU8(v + 128);
1221}
1222
1223static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
1224 int r = SkGetPackedR4444(c);
1225 int g = SkGetPackedG4444(c);
1226 int b = SkGetPackedB4444(c);
1227
1228 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
1229 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
1230 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
1231
1232 dst[0] = SkToU8(y);
1233 dst[1] = SkToU8(u + 128);
1234 dst[2] = SkToU8(v + 128);
1235}
1236
1237static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
1238 int r = SkGetPackedR16(c);
1239 int g = SkGetPackedG16(c);
1240 int b = SkGetPackedB16(c);
1241
1242 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
1243 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
1244 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
1245
1246 dst[0] = SkToU8(y);
1247 dst[1] = SkToU8(u + 128);
1248 dst[2] = SkToU8(v + 128);
1249}
1250
1251///////////////////////////////////////////////////////////////////////////////
1252
1253typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1254 const void* SK_RESTRICT src, int width,
1255 const SkPMColor* SK_RESTRICT ctable);
1256
1257static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1258 const void* SK_RESTRICT srcRow, int width,
1259 const SkPMColor*) {
1260 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1261 while (--width >= 0) {
1262#ifdef WE_CONVERT_TO_YUV
1263 rgb2yuv_32(dst, *src++);
1264#else
1265 uint32_t c = *src++;
1266 dst[0] = SkGetPackedR32(c);
1267 dst[1] = SkGetPackedG32(c);
1268 dst[2] = SkGetPackedB32(c);
1269#endif
1270 dst += 3;
1271 }
1272}
1273
1274static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1275 const void* SK_RESTRICT srcRow, int width,
1276 const SkPMColor*) {
1277 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1278 while (--width >= 0) {
1279#ifdef WE_CONVERT_TO_YUV
1280 rgb2yuv_4444(dst, *src++);
1281#else
1282 SkPMColor16 c = *src++;
1283 dst[0] = SkPacked4444ToR32(c);
1284 dst[1] = SkPacked4444ToG32(c);
1285 dst[2] = SkPacked4444ToB32(c);
1286#endif
1287 dst += 3;
1288 }
1289}
1290
1291static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1292 const void* SK_RESTRICT srcRow, int width,
1293 const SkPMColor*) {
1294 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1295 while (--width >= 0) {
1296#ifdef WE_CONVERT_TO_YUV
1297 rgb2yuv_16(dst, *src++);
1298#else
1299 uint16_t c = *src++;
1300 dst[0] = SkPacked16ToR32(c);
1301 dst[1] = SkPacked16ToG32(c);
1302 dst[2] = SkPacked16ToB32(c);
1303#endif
1304 dst += 3;
1305 }
1306}
1307
1308static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1309 const void* SK_RESTRICT srcRow, int width,
1310 const SkPMColor* SK_RESTRICT ctable) {
1311 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1312 while (--width >= 0) {
1313#ifdef WE_CONVERT_TO_YUV
1314 rgb2yuv_32(dst, ctable[*src++]);
1315#else
1316 uint32_t c = ctable[*src++];
1317 dst[0] = SkGetPackedR32(c);
1318 dst[1] = SkGetPackedG32(c);
1319 dst[2] = SkGetPackedB32(c);
1320#endif
1321 dst += 3;
1322 }
1323}
1324
1325static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -07001326 switch (bm.colorType()) {
1327 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001328 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -07001329 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001330 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -07001331 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001332 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -07001333 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001334 return Write_Index_YUV;
1335 default:
1336 return NULL;
1337 }
1338}
1339
1340class SkJPEGImageEncoder : public SkImageEncoder {
1341protected:
1342 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1343#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001344 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001345#endif
1346
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001347 SkAutoLockPixels alp(bm);
1348 if (NULL == bm.getPixels()) {
1349 return false;
1350 }
1351
1352 jpeg_compress_struct cinfo;
1353 skjpeg_error_mgr sk_err;
1354 skjpeg_destination_mgr sk_wstream(stream);
1355
1356 // allocate these before set call setjmp
1357 SkAutoMalloc oneRow;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001358
1359 cinfo.err = jpeg_std_error(&sk_err);
1360 sk_err.error_exit = skjpeg_error_exit;
1361 if (setjmp(sk_err.fJmpBuf)) {
1362 return false;
1363 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001364
mtklein@google.com8d725b22013-07-24 16:20:05 +00001365 // Keep after setjmp or mark volatile.
1366 const WriteScanline writer = ChooseWriter(bm);
1367 if (NULL == writer) {
1368 return false;
1369 }
1370
1371 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001372 cinfo.dest = &sk_wstream;
1373 cinfo.image_width = bm.width();
1374 cinfo.image_height = bm.height();
1375 cinfo.input_components = 3;
1376#ifdef WE_CONVERT_TO_YUV
1377 cinfo.in_color_space = JCS_YCbCr;
1378#else
1379 cinfo.in_color_space = JCS_RGB;
1380#endif
1381 cinfo.input_gamma = 1;
1382
1383 jpeg_set_defaults(&cinfo);
1384 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001385#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001386 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001387#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001388
1389 jpeg_start_compress(&cinfo, TRUE);
1390
1391 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001392 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001393
mtklein775b8192014-12-02 09:11:25 -08001394 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : NULL;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001395 const void* srcRow = bm.getPixels();
1396
1397 while (cinfo.next_scanline < cinfo.image_height) {
1398 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1399
1400 writer(oneRowP, srcRow, width, colors);
1401 row_pointer[0] = oneRowP;
1402 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1403 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1404 }
1405
1406 jpeg_finish_compress(&cinfo);
1407 jpeg_destroy_compress(&cinfo);
1408
1409 return true;
1410 }
1411};
1412
1413///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001414DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1415DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1416///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001417
scroggo@google.comb5571b32013-09-25 21:34:24 +00001418static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001419 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001420 static const size_t HEADER_SIZE = sizeof(gHeader);
1421
1422 char buffer[HEADER_SIZE];
1423 size_t len = stream->read(buffer, HEADER_SIZE);
1424
1425 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001426 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001427 }
1428 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001429 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001430 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001431 return true;
1432}
1433
scroggo@google.comb5571b32013-09-25 21:34:24 +00001434
1435static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001436 if (is_jpeg(stream)) {
1437 return SkNEW(SkJPEGImageDecoder);
1438 }
1439 return NULL;
1440}
1441
scroggo@google.comb5571b32013-09-25 21:34:24 +00001442static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001443 if (is_jpeg(stream)) {
1444 return SkImageDecoder::kJPEG_Format;
1445 }
1446 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001447}
1448
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001449static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001450 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1451}
1452
mtklein@google.combd6343b2013-09-04 17:20:18 +00001453static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1454static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1455static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);