blob: 8e92dd874e18a3ccc64c27e54db9301c2653bf75 [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"
scroggo27631bd2015-06-15 09:10:03 -070011#include "SkJpegUtility.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000012#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
halcanary@google.comfed30372013-10-04 12:46:45 +000060static void do_nothing_emit_message(jpeg_common_struct*, int) {
61 /* do nothing */
62}
halcanary@google.com04b57f82013-10-14 20:08:48 +000063static void do_nothing_output_message(j_common_ptr) {
64 /* do nothing */
65}
halcanary@google.comfed30372013-10-04 12:46:45 +000066
scroggo@google.com590a5af2013-08-07 21:09:13 +000067static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* src_mgr) {
68 SkASSERT(cinfo != NULL);
69 SkASSERT(src_mgr != NULL);
70 jpeg_create_decompress(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +000071 cinfo->src = src_mgr;
halcanary@google.comfed30372013-10-04 12:46:45 +000072 /* To suppress warnings with a SK_DEBUG binary, set the
73 * environment variable "skia_images_jpeg_suppressDecoderWarnings"
74 * to "true". Inside a program that links to skia:
75 * SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); */
76 if (c_suppressJPEGImageDecoderWarnings) {
77 cinfo->err->emit_message = &do_nothing_emit_message;
78 }
halcanary@google.com04b57f82013-10-14 20:08:48 +000079 /* To suppress error messages with a SK_DEBUG binary, set the
80 * environment variable "skia_images_jpeg_suppressDecoderErrors"
81 * to "true". Inside a program that links to skia:
82 * SK_CONF_SET("images.jpeg.suppressDecoderErrors", true); */
83 if (c_suppressJPEGImageDecoderErrors) {
84 cinfo->err->output_message = &do_nothing_output_message;
85 }
scroggo@google.com590a5af2013-08-07 21:09:13 +000086}
87
scroggo@google.coma1a51542013-08-07 21:02:32 +000088#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +000089class SkJPEGImageIndex {
90public:
scroggoa1193e42015-01-21 12:09:53 -080091 // Takes ownership of stream.
scroggo@google.comb5571b32013-09-25 21:34:24 +000092 SkJPEGImageIndex(SkStreamRewindable* stream, SkImageDecoder* decoder)
scroggo@google.coma1a51542013-08-07 21:02:32 +000093 : fSrcMgr(stream, decoder)
scroggoa1193e42015-01-21 12:09:53 -080094 , fStream(stream)
scroggo@google.coma1a51542013-08-07 21:02:32 +000095 , fInfoInitialized(false)
96 , fHuffmanCreated(false)
97 , fDecompressStarted(false)
98 {
99 SkDEBUGCODE(fReadHeaderSucceeded = false;)
100 }
djsollen@google.com11399402013-03-20 17:45:27 +0000101
102 ~SkJPEGImageIndex() {
scroggo@google.coma1a51542013-08-07 21:02:32 +0000103 if (fHuffmanCreated) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000104 // Set to false before calling the libjpeg function, in case
105 // the libjpeg function calls longjmp. Our setjmp handler may
106 // attempt to delete this SkJPEGImageIndex, thus entering this
107 // destructor again. Setting fHuffmanCreated to false first
108 // prevents an infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000109 fHuffmanCreated = false;
110 jpeg_destroy_huffman_index(&fHuffmanIndex);
111 }
112 if (fDecompressStarted) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000113 // Like fHuffmanCreated, set to false before calling libjpeg
114 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000115 fDecompressStarted = false;
116 jpeg_finish_decompress(&fCInfo);
117 }
118 if (fInfoInitialized) {
119 this->destroyInfo();
120 }
djsollen@google.com11399402013-03-20 17:45:27 +0000121 }
122
123 /**
scroggo@google.coma1a51542013-08-07 21:02:32 +0000124 * Destroy the cinfo struct.
125 * After this call, if a huffman index was already built, it
126 * can be used after calling initializeInfoAndReadHeader
127 * again. Must not be called after startTileDecompress except
128 * in the destructor.
djsollen@google.com11399402013-03-20 17:45:27 +0000129 */
scroggo@google.coma1a51542013-08-07 21:02:32 +0000130 void destroyInfo() {
131 SkASSERT(fInfoInitialized);
132 SkASSERT(!fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000133 // Like fHuffmanCreated, set to false before calling libjpeg
134 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000135 fInfoInitialized = false;
136 jpeg_destroy_decompress(&fCInfo);
137 SkDEBUGCODE(fReadHeaderSucceeded = false;)
138 }
139
140 /**
141 * Initialize the cinfo struct.
142 * Calls jpeg_create_decompress, makes customizations, and
143 * finally calls jpeg_read_header. Returns true if jpeg_read_header
144 * returns JPEG_HEADER_OK.
145 * If cinfo was already initialized, destroyInfo must be called to
146 * destroy the old one. Must not be called after startTileDecompress.
147 */
148 bool initializeInfoAndReadHeader() {
149 SkASSERT(!fInfoInitialized && !fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000150 initialize_info(&fCInfo, &fSrcMgr);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000151 fInfoInitialized = true;
152 const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true));
153 SkDEBUGCODE(fReadHeaderSucceeded = success;)
154 return success;
djsollen@google.com11399402013-03-20 17:45:27 +0000155 }
156
157 jpeg_decompress_struct* cinfo() { return &fCInfo; }
158
djsollen@google.com11399402013-03-20 17:45:27 +0000159 huffman_index* huffmanIndex() { return &fHuffmanIndex; }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000160
161 /**
162 * Build the index to be used for tile based decoding.
163 * Must only be called after a successful call to
164 * initializeInfoAndReadHeader and must not be called more
165 * than once.
166 */
167 bool buildHuffmanIndex() {
168 SkASSERT(fReadHeaderSucceeded);
169 SkASSERT(!fHuffmanCreated);
170 jpeg_create_huffman_index(&fCInfo, &fHuffmanIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000171 SkASSERT(1 == fCInfo.scale_num && 1 == fCInfo.scale_denom);
scroggo@google.com57a52982013-08-27 20:42:22 +0000172 fHuffmanCreated = jpeg_build_huffman_index(&fCInfo, &fHuffmanIndex);
173 return fHuffmanCreated;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000174 }
175
176 /**
177 * Start tile based decoding. Must only be called after a
178 * successful call to buildHuffmanIndex, and must only be
179 * called once.
180 */
181 bool startTileDecompress() {
182 SkASSERT(fHuffmanCreated);
183 SkASSERT(fReadHeaderSucceeded);
184 SkASSERT(!fDecompressStarted);
185 if (jpeg_start_tile_decompress(&fCInfo)) {
186 fDecompressStarted = true;
187 return true;
188 }
189 return false;
190 }
djsollen@google.com11399402013-03-20 17:45:27 +0000191
192private:
193 skjpeg_source_mgr fSrcMgr;
scroggoa1193e42015-01-21 12:09:53 -0800194 SkAutoTDelete<SkStream> fStream;
djsollen@google.com11399402013-03-20 17:45:27 +0000195 jpeg_decompress_struct fCInfo;
djsollen@google.com11399402013-03-20 17:45:27 +0000196 huffman_index fHuffmanIndex;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000197 bool fInfoInitialized;
198 bool fHuffmanCreated;
199 bool fDecompressStarted;
200 SkDEBUGCODE(bool fReadHeaderSucceeded;)
djsollen@google.com11399402013-03-20 17:45:27 +0000201};
scroggo@google.coma1a51542013-08-07 21:02:32 +0000202#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000203
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000204class SkJPEGImageDecoder : public SkImageDecoder {
205public:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000206#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000207 SkJPEGImageDecoder() {
208 fImageIndex = NULL;
209 fImageWidth = 0;
210 fImageHeight = 0;
211 }
212
213 virtual ~SkJPEGImageDecoder() {
214 SkDELETE(fImageIndex);
215 }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000216#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000217
mtklein36352bf2015-03-25 18:17:31 -0700218 Format getFormat() const override {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000219 return kJPEG_Format;
220 }
221
222protected:
scroggo@google.comd79277f2013-08-07 19:53:53 +0000223#ifdef SK_BUILD_FOR_ANDROID
mtklein36352bf2015-03-25 18:17:31 -0700224 bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) override;
225 bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) override;
djsollen@google.com11399402013-03-20 17:45:27 +0000226#endif
mtklein36352bf2015-03-25 18:17:31 -0700227 Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
tfarina7831a4b2015-04-27 07:53:07 -0700228 bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
229 void* planes[3], size_t rowBytes[3],
230 SkYUVColorSpace* colorSpace) override;
djsollen@google.com11399402013-03-20 17:45:27 +0000231
232private:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000233#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000234 SkJPEGImageIndex* fImageIndex;
235 int fImageWidth;
236 int fImageHeight;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000237#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000238
scroggo@google.com590a5af2013-08-07 21:09:13 +0000239 /**
reed6c225732014-06-09 19:52:07 -0700240 * Determine the appropriate bitmap colortype and out_color_space based on
scroggo@google.com590a5af2013-08-07 21:09:13 +0000241 * both the preference of the caller and the jpeg_color_space on the
242 * jpeg_decompress_struct passed in.
243 * Must be called after jpeg_read_header.
244 */
reed6c225732014-06-09 19:52:07 -0700245 SkColorType getBitmapColorType(jpeg_decompress_struct*);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000246
djsollen@google.com11399402013-03-20 17:45:27 +0000247 typedef SkImageDecoder INHERITED;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000248};
249
250//////////////////////////////////////////////////////////////////////////
251
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000252/* Automatically clean up after throwing an exception */
253class JPEGAutoClean {
254public:
255 JPEGAutoClean(): cinfo_ptr(NULL) {}
256 ~JPEGAutoClean() {
257 if (cinfo_ptr) {
258 jpeg_destroy_decompress(cinfo_ptr);
259 }
260 }
261 void set(jpeg_decompress_struct* info) {
262 cinfo_ptr = info;
263 }
264private:
265 jpeg_decompress_struct* cinfo_ptr;
266};
267
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000268///////////////////////////////////////////////////////////////////////////////
269
270/* If we need to better match the request, we might examine the image and
271 output dimensions, and determine if the downsampling jpeg provided is
272 not sufficient. If so, we can recompute a modified sampleSize value to
273 make up the difference.
274
275 To skip this additional scaling, just set sampleSize = 1; below.
276 */
277static int recompute_sampleSize(int sampleSize,
278 const jpeg_decompress_struct& cinfo) {
279 return sampleSize * cinfo.output_width / cinfo.image_width;
280}
281
282static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
283 /* These are initialized to 0, so if they have non-zero values, we assume
284 they are "valid" (i.e. have been computed by libjpeg)
285 */
djsollen@google.com11399402013-03-20 17:45:27 +0000286 return 0 != cinfo.output_width && 0 != cinfo.output_height;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000287}
288
djsollen@google.com11399402013-03-20 17:45:27 +0000289static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000290 for (int i = 0; i < count; i++) {
291 JSAMPLE* rowptr = (JSAMPLE*)buffer;
292 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
djsollen@google.com11399402013-03-20 17:45:27 +0000293 if (1 != row_count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000294 return false;
295 }
296 }
297 return true;
298}
299
scroggo@google.comd79277f2013-08-07 19:53:53 +0000300#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000301static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
302 huffman_index *index, void* buffer, int count) {
303 for (int i = 0; i < count; i++) {
304 JSAMPLE* rowptr = (JSAMPLE*)buffer;
305 int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
306 if (1 != row_count) {
307 return false;
308 }
309 }
310 return true;
311}
312#endif
313
scroggo2a120802014-10-22 12:07:00 -0700314///////////////////////////////////////////////////////////////////////////////
315
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000316// This guy exists just to aid in debugging, as it allows debuggers to just
317// set a break-point in one place to see all error exists.
scroggo2a120802014-10-22 12:07:00 -0700318static void print_jpeg_decoder_errors(const jpeg_decompress_struct& cinfo,
sugoib227e372014-10-16 13:10:57 -0700319 int width, int height, const char caller[]) {
halcanary@google.com04b57f82013-10-14 20:08:48 +0000320 if (!(c_suppressJPEGImageDecoderErrors)) {
321 char buffer[JMSG_LENGTH_MAX];
322 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
323 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
sugoib227e372014-10-16 13:10:57 -0700324 cinfo.err->msg_code, buffer, caller, width, height);
halcanary@google.com04b57f82013-10-14 20:08:48 +0000325 }
scroggo2a120802014-10-22 12:07:00 -0700326}
327
328static bool return_false(const jpeg_decompress_struct& cinfo,
329 const char caller[]) {
330 print_jpeg_decoder_errors(cinfo, 0, 0, caller);
331 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000332}
333
mtkleindf78d0a2014-10-23 10:13:21 -0700334#ifdef SK_BUILD_FOR_ANDROID
sugoib227e372014-10-16 13:10:57 -0700335static bool return_false(const jpeg_decompress_struct& cinfo,
336 const SkBitmap& bm, const char caller[]) {
scroggo2a120802014-10-22 12:07:00 -0700337 print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
338 return false;
sugoib227e372014-10-16 13:10:57 -0700339}
mtkleindf78d0a2014-10-23 10:13:21 -0700340#endif
sugoib227e372014-10-16 13:10:57 -0700341
scroggo2a120802014-10-22 12:07:00 -0700342static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo,
343 const SkBitmap& bm, const char caller[]) {
344 print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
345 return SkImageDecoder::kFailure;
346}
347
348///////////////////////////////////////////////////////////////////////////////
349
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000350// Convert a scanline of CMYK samples to RGBX in place. Note that this
351// method moves the "scanline" pointer in its processing
352static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
353 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000354 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000355 // from easyrgb.com):
356 // CMYK -> CMY
357 // C = ( C * (1 - K) + K ) // for each CMY component
358 // CMY -> RGB
359 // R = ( 1 - C ) * 255 // for each RGB component
360 // Unfortunately we are seeing inverted CMYK so all the original terms
361 // are 1-. This yields:
362 // CMYK -> CMY
363 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
364 // The conversion from CMY->RGB remains the same
365 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
366 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
367 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
368 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
369 scanline[3] = 255;
370 }
371}
372
scroggo@google.com590a5af2013-08-07 21:09:13 +0000373/**
374 * Common code for setting the error manager.
375 */
376static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
377 SkASSERT(cinfo != NULL);
378 SkASSERT(errorManager != NULL);
379 cinfo->err = jpeg_std_error(errorManager);
380 errorManager->error_exit = skjpeg_error_exit;
381}
382
383/**
384 * Common code for turning off upsampling and smoothing. Turning these
385 * off helps performance without showing noticable differences in the
386 * resulting bitmap.
387 */
388static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
389 SkASSERT(cinfo != NULL);
390 /* this gives about 30% performance improvement. In theory it may
391 reduce the visual quality, in practice I'm not seeing a difference
392 */
393 cinfo->do_fancy_upsampling = 0;
394
395 /* this gives another few percents */
396 cinfo->do_block_smoothing = 0;
397}
398
399/**
400 * Common code for setting the dct method.
401 */
402static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
403 SkASSERT(cinfo != NULL);
404#ifdef DCT_IFAST_SUPPORTED
405 if (decoder.getPreferQualityOverSpeed()) {
406 cinfo->dct_method = JDCT_ISLOW;
407 } else {
408 cinfo->dct_method = JDCT_IFAST;
409 }
410#else
411 cinfo->dct_method = JDCT_ISLOW;
412#endif
413}
414
reed6c225732014-06-09 19:52:07 -0700415SkColorType SkJPEGImageDecoder::getBitmapColorType(jpeg_decompress_struct* cinfo) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000416 SkASSERT(cinfo != NULL);
417
418 SrcDepth srcDepth = k32Bit_SrcDepth;
419 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
420 srcDepth = k8BitGray_SrcDepth;
421 }
422
reed6c225732014-06-09 19:52:07 -0700423 SkColorType colorType = this->getPrefColorType(srcDepth, /*hasAlpha*/ false);
424 switch (colorType) {
425 case kAlpha_8_SkColorType:
426 // Only respect A8 colortype if the original is grayscale,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000427 // in which case we will treat the grayscale as alpha
428 // values.
429 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
reed6c225732014-06-09 19:52:07 -0700430 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000431 }
432 break;
reed6c225732014-06-09 19:52:07 -0700433 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000434 // Fall through.
reed6c225732014-06-09 19:52:07 -0700435 case kARGB_4444_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000436 // Fall through.
reed6c225732014-06-09 19:52:07 -0700437 case kRGB_565_SkColorType:
438 // These are acceptable destination colortypes.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000439 break;
440 default:
reed6c225732014-06-09 19:52:07 -0700441 // Force all other colortypes to 8888.
442 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000443 break;
444 }
445
446 switch (cinfo->jpeg_color_space) {
447 case JCS_CMYK:
448 // Fall through.
449 case JCS_YCCK:
450 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
451 // so libjpeg will give us CMYK samples back and we will later
452 // manually convert them to RGB
453 cinfo->out_color_space = JCS_CMYK;
454 break;
455 case JCS_GRAYSCALE:
reed6c225732014-06-09 19:52:07 -0700456 if (kAlpha_8_SkColorType == colorType) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000457 cinfo->out_color_space = JCS_GRAYSCALE;
458 break;
459 }
460 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
reed6c225732014-06-09 19:52:07 -0700461 // colortype. Fall through to set to the default.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000462 default:
463 cinfo->out_color_space = JCS_RGB;
464 break;
465 }
reed6c225732014-06-09 19:52:07 -0700466 return colorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000467}
468
scroggo@google.com590a5af2013-08-07 21:09:13 +0000469/**
reed6c225732014-06-09 19:52:07 -0700470 * Based on the colortype and dither mode, adjust out_color_space and
471 * dither_mode of cinfo. Only does work in ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000472 */
473static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
reed6c225732014-06-09 19:52:07 -0700474 SkColorType colorType,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000475 const SkImageDecoder& decoder) {
476 SkASSERT(cinfo != NULL);
reed6c225732014-06-09 19:52:07 -0700477#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000478 cinfo->dither_mode = JDITHER_NONE;
479 if (JCS_CMYK == cinfo->out_color_space) {
480 return;
481 }
reed6c225732014-06-09 19:52:07 -0700482 switch (colorType) {
483 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000484 cinfo->out_color_space = JCS_RGBA_8888;
485 break;
reed6c225732014-06-09 19:52:07 -0700486 case kRGB_565_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000487 cinfo->out_color_space = JCS_RGB_565;
488 if (decoder.getDitherImage()) {
489 cinfo->dither_mode = JDITHER_ORDERED;
490 }
491 break;
492 default:
493 break;
494 }
scroggo@google.com590a5af2013-08-07 21:09:13 +0000495#endif
reed6c225732014-06-09 19:52:07 -0700496}
scroggo@google.com590a5af2013-08-07 21:09:13 +0000497
halcanary@google.comfed30372013-10-04 12:46:45 +0000498/**
499 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
500 Used when decoding fails partway through reading scanlines to fill
501 remaining lines. */
502static void fill_below_level(int y, SkBitmap* bitmap) {
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000503 SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
halcanary@google.comfed30372013-10-04 12:46:45 +0000504 SkCanvas canvas(*bitmap);
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000505 canvas.clipRect(SkRect::Make(rect));
halcanary@google.comfed30372013-10-04 12:46:45 +0000506 canvas.drawColor(SK_ColorWHITE);
507}
508
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000509/**
510 * Get the config and bytes per pixel of the source data. Return
511 * whether the data is supported.
512 */
513static bool get_src_config(const jpeg_decompress_struct& cinfo,
514 SkScaledBitmapSampler::SrcConfig* sc,
515 int* srcBytesPerPixel) {
516 SkASSERT(sc != NULL && srcBytesPerPixel != NULL);
517 if (JCS_CMYK == cinfo.out_color_space) {
518 // In this case we will manually convert the CMYK values to RGB
519 *sc = SkScaledBitmapSampler::kRGBX;
520 // The CMYK work-around relies on 4 components per pixel here
521 *srcBytesPerPixel = 4;
522 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
523 *sc = SkScaledBitmapSampler::kRGB;
524 *srcBytesPerPixel = 3;
525#ifdef ANDROID_RGB
526 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
527 *sc = SkScaledBitmapSampler::kRGBX;
528 *srcBytesPerPixel = 4;
529 } else if (JCS_RGB_565 == cinfo.out_color_space) {
530 *sc = SkScaledBitmapSampler::kRGB_565;
531 *srcBytesPerPixel = 2;
532#endif
533 } else if (1 == cinfo.out_color_components &&
534 JCS_GRAYSCALE == cinfo.out_color_space) {
535 *sc = SkScaledBitmapSampler::kGray;
536 *srcBytesPerPixel = 1;
537 } else {
538 return false;
539 }
540 return true;
541}
halcanary@google.comfed30372013-10-04 12:46:45 +0000542
scroggo2a120802014-10-22 12:07:00 -0700543SkImageDecoder::Result SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000544#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000545 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000546#endif
547
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000548 JPEGAutoClean autoClean;
549
550 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000551 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000552
scroggo@google.com590a5af2013-08-07 21:09:13 +0000553 skjpeg_error_mgr errorManager;
554 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000555
556 // All objects need to be instantiated before this setjmp call so that
557 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000558 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700559 return return_failure(cinfo, *bm, "setjmp");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000560 }
561
scroggo@google.com590a5af2013-08-07 21:09:13 +0000562 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000563 autoClean.set(&cinfo);
564
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000565 int status = jpeg_read_header(&cinfo, true);
566 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700567 return return_failure(cinfo, *bm, "read_header");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000568 }
569
570 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
571 can) much faster that we, just use their num/denom api to approximate
572 the size.
573 */
574 int sampleSize = this->getSampleSize();
575
scroggo@google.com590a5af2013-08-07 21:09:13 +0000576 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000577
scroggo@google.com590a5af2013-08-07 21:09:13 +0000578 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000579 cinfo.scale_denom = sampleSize;
580
scroggo@google.com590a5af2013-08-07 21:09:13 +0000581 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000582
reed6c225732014-06-09 19:52:07 -0700583 const SkColorType colorType = this->getBitmapColorType(&cinfo);
584 const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ?
585 kPremul_SkAlphaType : kOpaque_SkAlphaType;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000586
reed6c225732014-06-09 19:52:07 -0700587 adjust_out_color_space_and_dither(&cinfo, colorType, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000588
djsollen@google.com11399402013-03-20 17:45:27 +0000589 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000590 // Assume an A8 bitmap is not opaque to avoid the check of each
591 // individual pixel. It is very unlikely to be opaque, since
592 // an opaque A8 bitmap would not be very interesting.
593 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700594 bool success = bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
595 colorType, alphaType));
596 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000597 }
598
599 /* image_width and image_height are the original dimensions, available
600 after jpeg_read_header(). To see the scaled dimensions, we have to call
601 jpeg_start_decompress(), and then read output_width and output_height.
602 */
603 if (!jpeg_start_decompress(&cinfo)) {
604 /* If we failed here, we may still have enough information to return
605 to the caller if they just wanted (subsampled bounds). If sampleSize
606 was 1, then we would have already returned. Thus we just check if
607 we're in kDecodeBounds_Mode, and that we have valid output sizes.
608
609 One reason to fail here is that we have insufficient stream data
610 to complete the setup. However, output dimensions seem to get
611 computed very early, which is why this special check can pay off.
612 */
djsollen@google.com11399402013-03-20 17:45:27 +0000613 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000614 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
615 recompute_sampleSize(sampleSize, cinfo));
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000616 // Assume an A8 bitmap is not opaque to avoid the check of each
617 // individual pixel. It is very unlikely to be opaque, since
618 // an opaque A8 bitmap would not be very interesting.
619 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700620 bool success = bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
621 colorType, alphaType));
622 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000623 } else {
scroggo2a120802014-10-22 12:07:00 -0700624 return return_failure(cinfo, *bm, "start_decompress");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000625 }
626 }
627 sampleSize = recompute_sampleSize(sampleSize, cinfo);
628
djsollen@google.com11399402013-03-20 17:45:27 +0000629 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
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.
reed6c225732014-06-09 19:52:07 -0700634 bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
635 colorType, alphaType));
scroggo@google.combc69ce92013-07-09 15:45:14 +0000636 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo2a120802014-10-22 12:07:00 -0700637 return kSuccess;
scroggo@google.combc69ce92013-07-09 15:45:14 +0000638 }
639 if (!this->allocPixelRef(bm, NULL)) {
scroggo2a120802014-10-22 12:07:00 -0700640 return return_failure(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000641 }
642
643 SkAutoLockPixels alp(*bm);
644
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000645#ifdef ANDROID_RGB
646 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
647 a significant performance boost.
648 */
649 if (sampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700650 ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||
651 (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000652 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000653 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000654 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000655
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000656 while (cinfo.output_scanline < cinfo.output_height) {
657 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000658 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000659 // if row_count == 0, then we didn't get a scanline,
660 // so return early. We will return a partial image.
661 fill_below_level(cinfo.output_scanline, bm);
662 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700663 jpeg_finish_decompress(&cinfo);
664 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000665 }
666 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700667 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000668 }
669 rowptr += bpr;
670 }
671 jpeg_finish_decompress(&cinfo);
scroggo2a120802014-10-22 12:07:00 -0700672 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000673 }
674#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000675
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000676 // check for supported formats
677 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000678 int srcBytesPerPixel;
679
680 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
scroggo2a120802014-10-22 12:07:00 -0700681 return return_failure(cinfo, *bm, "jpeg colorspace");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000682 }
683
scroggo@google.com8d239242013-10-01 17:27:15 +0000684 if (!sampler.begin(bm, sc, *this)) {
scroggo2a120802014-10-22 12:07:00 -0700685 return return_failure(cinfo, *bm, "sampler.begin");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000686 }
687
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000688 SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000689 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000690
691 // Possibly skip initial rows [sampler.srcY0]
692 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
scroggo2a120802014-10-22 12:07:00 -0700693 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000694 }
695
696 // now loop through scanlines until y == bm->height() - 1
697 for (int y = 0;; y++) {
698 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
699 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
700 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000701 // if row_count == 0, then we didn't get a scanline,
702 // so return early. We will return a partial image.
703 fill_below_level(y, bm);
704 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700705 jpeg_finish_decompress(&cinfo);
scroggoc6b8ffa2014-12-17 06:55:02 -0800706 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000707 }
708 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700709 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000710 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000711
712 if (JCS_CMYK == cinfo.out_color_space) {
713 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
714 }
715
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000716 sampler.next(srcRow);
717 if (bm->height() - 1 == y) {
718 // we're done
719 break;
720 }
721
722 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
scroggo2a120802014-10-22 12:07:00 -0700723 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000724 }
725 }
726
727 // we formally skip the rest, so we don't get a complaint from libjpeg
728 if (!skip_src_rows(&cinfo, srcRow,
729 cinfo.output_height - cinfo.output_scanline)) {
scroggo2a120802014-10-22 12:07:00 -0700730 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000731 }
732 jpeg_finish_decompress(&cinfo);
733
scroggo2a120802014-10-22 12:07:00 -0700734 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000735}
736
scroggo2a120802014-10-22 12:07:00 -0700737///////////////////////////////////////////////////////////////////////////////
738
sugoib227e372014-10-16 13:10:57 -0700739enum SizeType {
740 kSizeForMemoryAllocation_SizeType,
741 kActualSize_SizeType
742};
743
744static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component,
745 SizeType sizeType) {
746 if (sizeType == kSizeForMemoryAllocation_SizeType) {
747 return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE,
748 info.cur_comp_info[component]->height_in_blocks * DCTSIZE);
749 }
750 return SkISize::Make(info.cur_comp_info[component]->downsampled_width,
751 info.cur_comp_info[component]->downsampled_height);
752}
753
mtkleinb3e5e4d2015-03-25 13:13:43 -0700754static bool appears_to_be_yuv(const jpeg_decompress_struct& info) {
755 return (info.jpeg_color_space == JCS_YCbCr)
756 && (DCTSIZE == 8)
757 && (info.num_components == 3)
758 && (info.comps_in_scan >= info.num_components)
759 && (info.scale_denom <= 8)
760 && (info.cur_comp_info[0])
761 && (info.cur_comp_info[1])
762 && (info.cur_comp_info[2])
763 && (info.cur_comp_info[1]->h_samp_factor == 1)
764 && (info.cur_comp_info[1]->v_samp_factor == 1)
765 && (info.cur_comp_info[2]->h_samp_factor == 1)
766 && (info.cur_comp_info[2]->v_samp_factor == 1);
767}
768
sugoib227e372014-10-16 13:10:57 -0700769static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
770 SizeType sizeType) {
mtkleinb3e5e4d2015-03-25 13:13:43 -0700771 SkASSERT(appears_to_be_yuv(cinfo));
sugoib227e372014-10-16 13:10:57 -0700772 for (int i = 0; i < 3; ++i) {
773 componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
774 }
775}
776
777static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) {
mtkleinb3e5e4d2015-03-25 13:13:43 -0700778 SkASSERT(appears_to_be_yuv(cinfo));
sugoib227e372014-10-16 13:10:57 -0700779 // U size and V size have to be the same if we're calling output_raw_data()
780 SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType);
781 SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType));
782
783 JSAMPARRAY bufferraw[3];
784 JSAMPROW bufferraw2[32];
785 bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16)
786 bufferraw[1] = &bufferraw2[16]; // U channel rows (8)
787 bufferraw[2] = &bufferraw2[24]; // V channel rows (8)
788 int yWidth = cinfo.output_width;
789 int yHeight = cinfo.output_height;
790 int yMaxH = yHeight - 1;
791 int v = cinfo.cur_comp_info[0]->v_samp_factor;
792 int uvMaxH = uvSize.height() - 1;
793 JSAMPROW outputY = static_cast<JSAMPROW>(planes[0]);
794 JSAMPROW outputU = static_cast<JSAMPROW>(planes[1]);
795 JSAMPROW outputV = static_cast<JSAMPROW>(planes[2]);
796 size_t rowBytesY = rowBytes[0];
797 size_t rowBytesU = rowBytes[1];
798 size_t rowBytesV = rowBytes[2];
799
800 int yScanlinesToRead = DCTSIZE * v;
sugoif421ec62015-02-19 05:32:08 -0800801 SkAutoMalloc lastRowStorage(rowBytesY * 4);
sugoib227e372014-10-16 13:10:57 -0700802 JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get();
sugoif421ec62015-02-19 05:32:08 -0800803 JSAMPROW uLastRow = yLastRow + rowBytesY;
804 JSAMPROW vLastRow = uLastRow + rowBytesY;
805 JSAMPROW dummyRow = vLastRow + rowBytesY;
sugoib227e372014-10-16 13:10:57 -0700806
807 while (cinfo.output_scanline < cinfo.output_height) {
808 // Request 8 or 16 scanlines: returns 0 or more scanlines.
809 bool hasYLastRow(false), hasUVLastRow(false);
810 // Assign 8 or 16 rows of memory to read the Y channel.
811 for (int i = 0; i < yScanlinesToRead; ++i) {
812 int scanline = (cinfo.output_scanline + i);
813 if (scanline < yMaxH) {
814 bufferraw2[i] = &outputY[scanline * rowBytesY];
815 } else if (scanline == yMaxH) {
816 bufferraw2[i] = yLastRow;
817 hasYLastRow = true;
818 } else {
819 bufferraw2[i] = dummyRow;
820 }
821 }
822 int scaledScanline = cinfo.output_scanline / v;
823 // Assign 8 rows of memory to read the U and V channels.
824 for (int i = 0; i < 8; ++i) {
825 int scanline = (scaledScanline + i);
826 if (scanline < uvMaxH) {
827 bufferraw2[16 + i] = &outputU[scanline * rowBytesU];
828 bufferraw2[24 + i] = &outputV[scanline * rowBytesV];
829 } else if (scanline == uvMaxH) {
830 bufferraw2[16 + i] = uLastRow;
831 bufferraw2[24 + i] = vLastRow;
832 hasUVLastRow = true;
833 } else {
834 bufferraw2[16 + i] = dummyRow;
835 bufferraw2[24 + i] = dummyRow;
836 }
837 }
838 JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead);
839
scroggo2a120802014-10-22 12:07:00 -0700840 if (scanlinesRead == 0) {
sugoib227e372014-10-16 13:10:57 -0700841 return false;
scroggo2a120802014-10-22 12:07:00 -0700842 }
sugoib227e372014-10-16 13:10:57 -0700843
844 if (hasYLastRow) {
845 memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth);
846 }
847 if (hasUVLastRow) {
848 memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width());
849 memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width());
850 }
851 }
852
853 cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height);
854
855 return true;
856}
857
858bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
859 void* planes[3], size_t rowBytes[3],
860 SkYUVColorSpace* colorSpace) {
861#ifdef TIME_DECODE
862 SkAutoTime atm("JPEG YUV8 Decode");
863#endif
sugoib227e372014-10-16 13:10:57 -0700864 if (this->getSampleSize() != 1) {
865 return false; // Resizing not supported
866 }
867
868 JPEGAutoClean autoClean;
869
870 jpeg_decompress_struct cinfo;
871 skjpeg_source_mgr srcManager(stream, this);
872
873 skjpeg_error_mgr errorManager;
874 set_error_mgr(&cinfo, &errorManager);
875
876 // All objects need to be instantiated before this setjmp call so that
877 // they will be cleaned up properly if an error occurs.
878 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700879 return return_false(cinfo, "setjmp YUV8");
sugoib227e372014-10-16 13:10:57 -0700880 }
881
882 initialize_info(&cinfo, &srcManager);
883 autoClean.set(&cinfo);
884
885 int status = jpeg_read_header(&cinfo, true);
886 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700887 return return_false(cinfo, "read_header YUV8");
sugoib227e372014-10-16 13:10:57 -0700888 }
889
mtkleinb3e5e4d2015-03-25 13:13:43 -0700890 if (!appears_to_be_yuv(cinfo)) {
sugoib227e372014-10-16 13:10:57 -0700891 // It's not an error to not be encoded in YUV, so no need to use return_false()
892 return false;
893 }
894
895 cinfo.out_color_space = JCS_YCbCr;
896 cinfo.raw_data_out = TRUE;
897
898 if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only
899 update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType);
900 return true;
901 }
902
903 set_dct_method(*this, &cinfo);
904
905 SkASSERT(1 == cinfo.scale_num);
906 cinfo.scale_denom = 1;
907
908 turn_off_visual_optimizations(&cinfo);
909
910#ifdef ANDROID_RGB
911 cinfo.dither_mode = JDITHER_NONE;
912#endif
913
914 /* image_width and image_height are the original dimensions, available
915 after jpeg_read_header(). To see the scaled dimensions, we have to call
916 jpeg_start_decompress(), and then read output_width and output_height.
917 */
918 if (!jpeg_start_decompress(&cinfo)) {
scroggo2a120802014-10-22 12:07:00 -0700919 return return_false(cinfo, "start_decompress YUV8");
sugoib227e372014-10-16 13:10:57 -0700920 }
921
mtkleinb3e5e4d2015-03-25 13:13:43 -0700922 // Seems like jpeg_start_decompress is updating our opinion of whether cinfo represents YUV.
923 // Again, not really an error.
924 if (!appears_to_be_yuv(cinfo)) {
925 return false;
926 }
927
sugoib227e372014-10-16 13:10:57 -0700928 if (!output_raw_data(cinfo, planes, rowBytes)) {
scroggo2a120802014-10-22 12:07:00 -0700929 return return_false(cinfo, "output_raw_data");
sugoib227e372014-10-16 13:10:57 -0700930 }
931
932 update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
933 jpeg_finish_decompress(&cinfo);
934
935 if (NULL != colorSpace) {
936 *colorSpace = kJPEG_SkYUVColorSpace;
937 }
938
939 return true;
940}
941
scroggo2a120802014-10-22 12:07:00 -0700942///////////////////////////////////////////////////////////////////////////////
943
scroggo@google.comd79277f2013-08-07 19:53:53 +0000944#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000945bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000946
scroggo@google.coma1a51542013-08-07 21:02:32 +0000947 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000948
949 skjpeg_error_mgr sk_err;
scroggo6d7e47a2015-01-22 10:27:25 -0800950 set_error_mgr(imageIndex->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
scroggo6d7e47a2015-01-22 10:27:25 -0800975 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
976 // We have a new cinfo, so set the error mgr again.
977 set_error_mgr(cinfo, &sk_err);
978
scroggo@google.com590a5af2013-08-07 21:09:13 +0000979 // FIXME: This sets cinfo->out_color_space, which we may change later
980 // based on the config in onDecodeSubset. This should be fine, since
981 // jpeg_init_read_tile_scanline will check out_color_space again after
982 // that change (when it calls jinit_color_deconverter).
reed6c225732014-06-09 19:52:07 -0700983 (void) this->getBitmapColorType(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000984
985 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000986
987 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000988 if (!imageIndex->startTileDecompress()) {
989 return false;
990 }
djsollen@google.com11399402013-03-20 17:45:27 +0000991
scroggo@google.coma1a51542013-08-07 21:02:32 +0000992 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000993 fImageWidth = cinfo->output_width;
994 fImageHeight = cinfo->output_height;
995
996 if (width) {
997 *width = fImageWidth;
998 }
999 if (height) {
1000 *height = fImageHeight;
1001 }
djsollen@google.com11399402013-03-20 17:45:27 +00001002
1003 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +00001004 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +00001005
1006 return true;
1007}
1008
scroggo@google.com7e6fcee2013-05-03 20:14:28 +00001009bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +00001010 if (NULL == fImageIndex) {
1011 return false;
1012 }
1013 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
1014
1015 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
1016 if (!rect.intersect(region)) {
1017 // If the requested region is entirely outside the image return false
1018 return false;
1019 }
1020
1021
1022 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +00001023 set_error_mgr(cinfo, &errorManager);
1024
djsollen@google.com11399402013-03-20 17:45:27 +00001025 if (setjmp(errorManager.fJmpBuf)) {
1026 return false;
1027 }
1028
1029 int requestedSampleSize = this->getSampleSize();
1030 cinfo->scale_denom = requestedSampleSize;
1031
scroggo@google.com590a5af2013-08-07 21:09:13 +00001032 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +00001033
reed6c225732014-06-09 19:52:07 -07001034 const SkColorType colorType = this->getBitmapColorType(cinfo);
1035 adjust_out_color_space_and_dither(cinfo, colorType, *this);
djsollen@google.com11399402013-03-20 17:45:27 +00001036
1037 int startX = rect.fLeft;
1038 int startY = rect.fTop;
1039 int width = rect.width();
1040 int height = rect.height();
1041
1042 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
1043 &startX, &startY, &width, &height);
1044 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
1045 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
1046
1047 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
1048
1049 SkBitmap bitmap;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001050 // Assume an A8 bitmap is not opaque to avoid the check of each
1051 // individual pixel. It is very unlikely to be opaque, since
1052 // an opaque A8 bitmap would not be very interesting.
1053 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -07001054 bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType,
1055 kAlpha_8_SkColorType == colorType ?
1056 kPremul_SkAlphaType : kOpaque_SkAlphaType));
djsollen@google.com11399402013-03-20 17:45:27 +00001057
1058 // Check ahead of time if the swap(dest, src) is possible or not.
1059 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
1060 // swap happening. If no, then we will use alloc to allocate pixels to
1061 // prevent garbage collection.
1062 int w = rect.width() / actualSampleSize;
1063 int h = rect.height() / actualSampleSize;
1064 bool swapOnly = (rect == region) && bm->isNull() &&
1065 (w == bitmap.width()) && (h == bitmap.height()) &&
1066 ((startX - rect.x()) / actualSampleSize == 0) &&
1067 ((startY - rect.y()) / actualSampleSize == 0);
1068 if (swapOnly) {
1069 if (!this->allocPixelRef(&bitmap, NULL)) {
1070 return return_false(*cinfo, bitmap, "allocPixelRef");
1071 }
1072 } else {
reed84825042014-09-02 12:50:45 -07001073 if (!bitmap.tryAllocPixels()) {
djsollen@google.com11399402013-03-20 17:45:27 +00001074 return return_false(*cinfo, bitmap, "allocPixels");
1075 }
1076 }
1077
1078 SkAutoLockPixels alp(bitmap);
1079
1080#ifdef ANDROID_RGB
1081 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
1082 a significant performance boost.
1083 */
1084 if (skiaSampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -07001085 ((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||
1086 (kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565)))
djsollen@google.com11399402013-03-20 17:45:27 +00001087 {
1088 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
1089 INT32 const bpr = bitmap.rowBytes();
1090 int rowTotalCount = 0;
1091
1092 while (rowTotalCount < height) {
1093 int rowCount = jpeg_read_tile_scanline(cinfo,
1094 fImageIndex->huffmanIndex(),
1095 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001096 // if rowCount == 0, then we didn't get a scanline, so abort.
1097 // onDecodeSubset() relies on onBuildTileIndex(), which
1098 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001099 if (0 == rowCount) {
1100 return return_false(*cinfo, bitmap, "read_scanlines");
1101 }
1102 if (this->shouldCancelDecode()) {
1103 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1104 }
1105 rowTotalCount += rowCount;
1106 rowptr += bpr;
1107 }
1108
1109 if (swapOnly) {
1110 bm->swap(bitmap);
zoran.jovanovic7f00acb2015-04-15 05:48:54 -07001111 return true;
djsollen@google.com11399402013-03-20 17:45:27 +00001112 }
zoran.jovanovic7f00acb2015-04-15 05:48:54 -07001113
1114 return cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1115 region.width(), region.height(), startX, startY);
djsollen@google.com11399402013-03-20 17:45:27 +00001116 }
1117#endif
1118
1119 // check for supported formats
1120 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001121 int srcBytesPerPixel;
1122
1123 if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001124 return return_false(*cinfo, *bm, "jpeg colorspace");
1125 }
1126
scroggo@google.com8d239242013-10-01 17:27:15 +00001127 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001128 return return_false(*cinfo, bitmap, "sampler.begin");
1129 }
1130
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001131 SkAutoMalloc srcStorage(width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +00001132 uint8_t* srcRow = (uint8_t*)srcStorage.get();
1133
1134 // Possibly skip initial rows [sampler.srcY0]
1135 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
1136 return return_false(*cinfo, bitmap, "skip rows");
1137 }
1138
1139 // now loop through scanlines until y == bitmap->height() - 1
1140 for (int y = 0;; y++) {
1141 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
1142 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001143 // if row_count == 0, then we didn't get a scanline, so abort.
1144 // onDecodeSubset() relies on onBuildTileIndex(), which
1145 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001146 if (0 == row_count) {
1147 return return_false(*cinfo, bitmap, "read_scanlines");
1148 }
1149 if (this->shouldCancelDecode()) {
1150 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1151 }
1152
1153 if (JCS_CMYK == cinfo->out_color_space) {
1154 convert_CMYK_to_RGB(srcRow, width);
1155 }
1156
1157 sampler.next(srcRow);
1158 if (bitmap.height() - 1 == y) {
1159 // we're done
1160 break;
1161 }
1162
1163 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
1164 sampler.srcDY() - 1)) {
1165 return return_false(*cinfo, bitmap, "skip rows");
1166 }
1167 }
1168 if (swapOnly) {
1169 bm->swap(bitmap);
zoran.jovanovic7f00acb2015-04-15 05:48:54 -07001170 return true;
djsollen@google.com11399402013-03-20 17:45:27 +00001171 }
zoran.jovanovic7f00acb2015-04-15 05:48:54 -07001172 return cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1173 region.width(), region.height(), startX, startY);
djsollen@google.com11399402013-03-20 17:45:27 +00001174}
1175#endif
1176
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001177///////////////////////////////////////////////////////////////////////////////
1178
1179#include "SkColorPriv.h"
1180
1181// taken from jcolor.c in libjpeg
1182#if 0 // 16bit - precise but slow
1183 #define CYR 19595 // 0.299
1184 #define CYG 38470 // 0.587
1185 #define CYB 7471 // 0.114
1186
1187 #define CUR -11059 // -0.16874
1188 #define CUG -21709 // -0.33126
1189 #define CUB 32768 // 0.5
1190
1191 #define CVR 32768 // 0.5
1192 #define CVG -27439 // -0.41869
1193 #define CVB -5329 // -0.08131
1194
1195 #define CSHIFT 16
1196#else // 8bit - fast, slightly less precise
1197 #define CYR 77 // 0.299
1198 #define CYG 150 // 0.587
1199 #define CYB 29 // 0.114
1200
1201 #define CUR -43 // -0.16874
1202 #define CUG -85 // -0.33126
1203 #define CUB 128 // 0.5
1204
1205 #define CVR 128 // 0.5
1206 #define CVG -107 // -0.41869
1207 #define CVB -21 // -0.08131
1208
1209 #define CSHIFT 8
1210#endif
1211
1212static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
1213 int r = SkGetPackedR32(c);
1214 int g = SkGetPackedG32(c);
1215 int b = SkGetPackedB32(c);
1216
1217 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
1218 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
1219 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
1220
1221 dst[0] = SkToU8(y);
1222 dst[1] = SkToU8(u + 128);
1223 dst[2] = SkToU8(v + 128);
1224}
1225
1226static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
1227 int r = SkGetPackedR4444(c);
1228 int g = SkGetPackedG4444(c);
1229 int b = SkGetPackedB4444(c);
1230
1231 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
1232 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
1233 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
1234
1235 dst[0] = SkToU8(y);
1236 dst[1] = SkToU8(u + 128);
1237 dst[2] = SkToU8(v + 128);
1238}
1239
1240static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
1241 int r = SkGetPackedR16(c);
1242 int g = SkGetPackedG16(c);
1243 int b = SkGetPackedB16(c);
1244
1245 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
1246 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
1247 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
1248
1249 dst[0] = SkToU8(y);
1250 dst[1] = SkToU8(u + 128);
1251 dst[2] = SkToU8(v + 128);
1252}
1253
1254///////////////////////////////////////////////////////////////////////////////
1255
1256typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1257 const void* SK_RESTRICT src, int width,
1258 const SkPMColor* SK_RESTRICT ctable);
1259
1260static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1261 const void* SK_RESTRICT srcRow, int width,
1262 const SkPMColor*) {
1263 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1264 while (--width >= 0) {
1265#ifdef WE_CONVERT_TO_YUV
1266 rgb2yuv_32(dst, *src++);
1267#else
1268 uint32_t c = *src++;
1269 dst[0] = SkGetPackedR32(c);
1270 dst[1] = SkGetPackedG32(c);
1271 dst[2] = SkGetPackedB32(c);
1272#endif
1273 dst += 3;
1274 }
1275}
1276
1277static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1278 const void* SK_RESTRICT srcRow, int width,
1279 const SkPMColor*) {
1280 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1281 while (--width >= 0) {
1282#ifdef WE_CONVERT_TO_YUV
1283 rgb2yuv_4444(dst, *src++);
1284#else
1285 SkPMColor16 c = *src++;
1286 dst[0] = SkPacked4444ToR32(c);
1287 dst[1] = SkPacked4444ToG32(c);
1288 dst[2] = SkPacked4444ToB32(c);
1289#endif
1290 dst += 3;
1291 }
1292}
1293
1294static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1295 const void* SK_RESTRICT srcRow, int width,
1296 const SkPMColor*) {
1297 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1298 while (--width >= 0) {
1299#ifdef WE_CONVERT_TO_YUV
1300 rgb2yuv_16(dst, *src++);
1301#else
1302 uint16_t c = *src++;
1303 dst[0] = SkPacked16ToR32(c);
1304 dst[1] = SkPacked16ToG32(c);
1305 dst[2] = SkPacked16ToB32(c);
1306#endif
1307 dst += 3;
1308 }
1309}
1310
1311static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1312 const void* SK_RESTRICT srcRow, int width,
1313 const SkPMColor* SK_RESTRICT ctable) {
1314 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1315 while (--width >= 0) {
1316#ifdef WE_CONVERT_TO_YUV
1317 rgb2yuv_32(dst, ctable[*src++]);
1318#else
1319 uint32_t c = ctable[*src++];
1320 dst[0] = SkGetPackedR32(c);
1321 dst[1] = SkGetPackedG32(c);
1322 dst[2] = SkGetPackedB32(c);
1323#endif
1324 dst += 3;
1325 }
1326}
1327
1328static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -07001329 switch (bm.colorType()) {
1330 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001331 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -07001332 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001333 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -07001334 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001335 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -07001336 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001337 return Write_Index_YUV;
1338 default:
1339 return NULL;
1340 }
1341}
1342
1343class SkJPEGImageEncoder : public SkImageEncoder {
1344protected:
1345 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1346#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001347 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001348#endif
1349
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001350 SkAutoLockPixels alp(bm);
1351 if (NULL == bm.getPixels()) {
1352 return false;
1353 }
1354
1355 jpeg_compress_struct cinfo;
1356 skjpeg_error_mgr sk_err;
1357 skjpeg_destination_mgr sk_wstream(stream);
1358
1359 // allocate these before set call setjmp
1360 SkAutoMalloc oneRow;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001361
1362 cinfo.err = jpeg_std_error(&sk_err);
1363 sk_err.error_exit = skjpeg_error_exit;
1364 if (setjmp(sk_err.fJmpBuf)) {
1365 return false;
1366 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001367
mtklein@google.com8d725b22013-07-24 16:20:05 +00001368 // Keep after setjmp or mark volatile.
1369 const WriteScanline writer = ChooseWriter(bm);
1370 if (NULL == writer) {
1371 return false;
1372 }
1373
1374 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001375 cinfo.dest = &sk_wstream;
1376 cinfo.image_width = bm.width();
1377 cinfo.image_height = bm.height();
1378 cinfo.input_components = 3;
1379#ifdef WE_CONVERT_TO_YUV
1380 cinfo.in_color_space = JCS_YCbCr;
1381#else
1382 cinfo.in_color_space = JCS_RGB;
1383#endif
1384 cinfo.input_gamma = 1;
1385
1386 jpeg_set_defaults(&cinfo);
1387 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001388#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001389 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001390#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001391
1392 jpeg_start_compress(&cinfo, TRUE);
1393
1394 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001395 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001396
mtklein775b8192014-12-02 09:11:25 -08001397 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : NULL;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001398 const void* srcRow = bm.getPixels();
1399
1400 while (cinfo.next_scanline < cinfo.image_height) {
1401 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1402
1403 writer(oneRowP, srcRow, width, colors);
1404 row_pointer[0] = oneRowP;
1405 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1406 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1407 }
1408
1409 jpeg_finish_compress(&cinfo);
1410 jpeg_destroy_compress(&cinfo);
1411
1412 return true;
1413 }
1414};
1415
1416///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001417DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1418DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1419///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001420
scroggo@google.comb5571b32013-09-25 21:34:24 +00001421static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001422 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001423 static const size_t HEADER_SIZE = sizeof(gHeader);
1424
1425 char buffer[HEADER_SIZE];
1426 size_t len = stream->read(buffer, HEADER_SIZE);
1427
1428 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001429 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001430 }
1431 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001432 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001433 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001434 return true;
1435}
1436
scroggo@google.comb5571b32013-09-25 21:34:24 +00001437
1438static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001439 if (is_jpeg(stream)) {
1440 return SkNEW(SkJPEGImageDecoder);
1441 }
1442 return NULL;
1443}
1444
scroggo@google.comb5571b32013-09-25 21:34:24 +00001445static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001446 if (is_jpeg(stream)) {
1447 return SkImageDecoder::kJPEG_Format;
1448 }
1449 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001450}
1451
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001452static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001453 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1454}
1455
mtklein@google.combd6343b2013-09-04 17:20:18 +00001456static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1457static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1458static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);