blob: d32e2a21c91bea0fe06e472a5e6cce22518f41c5 [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:
scroggoa1193e42015-01-21 12:09:53 -0800108 // Takes ownership of stream.
scroggo@google.comb5571b32013-09-25 21:34:24 +0000109 SkJPEGImageIndex(SkStreamRewindable* stream, SkImageDecoder* decoder)
scroggo@google.coma1a51542013-08-07 21:02:32 +0000110 : fSrcMgr(stream, decoder)
scroggoa1193e42015-01-21 12:09:53 -0800111 , fStream(stream)
scroggo@google.coma1a51542013-08-07 21:02:32 +0000112 , fInfoInitialized(false)
113 , fHuffmanCreated(false)
114 , fDecompressStarted(false)
115 {
116 SkDEBUGCODE(fReadHeaderSucceeded = false;)
117 }
djsollen@google.com11399402013-03-20 17:45:27 +0000118
119 ~SkJPEGImageIndex() {
scroggo@google.coma1a51542013-08-07 21:02:32 +0000120 if (fHuffmanCreated) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000121 // Set to false before calling the libjpeg function, in case
122 // the libjpeg function calls longjmp. Our setjmp handler may
123 // attempt to delete this SkJPEGImageIndex, thus entering this
124 // destructor again. Setting fHuffmanCreated to false first
125 // prevents an infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000126 fHuffmanCreated = false;
127 jpeg_destroy_huffman_index(&fHuffmanIndex);
128 }
129 if (fDecompressStarted) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000130 // Like fHuffmanCreated, set to false before calling libjpeg
131 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000132 fDecompressStarted = false;
133 jpeg_finish_decompress(&fCInfo);
134 }
135 if (fInfoInitialized) {
136 this->destroyInfo();
137 }
djsollen@google.com11399402013-03-20 17:45:27 +0000138 }
139
140 /**
scroggo@google.coma1a51542013-08-07 21:02:32 +0000141 * Destroy the cinfo struct.
142 * After this call, if a huffman index was already built, it
143 * can be used after calling initializeInfoAndReadHeader
144 * again. Must not be called after startTileDecompress except
145 * in the destructor.
djsollen@google.com11399402013-03-20 17:45:27 +0000146 */
scroggo@google.coma1a51542013-08-07 21:02:32 +0000147 void destroyInfo() {
148 SkASSERT(fInfoInitialized);
149 SkASSERT(!fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000150 // Like fHuffmanCreated, set to false before calling libjpeg
151 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000152 fInfoInitialized = false;
153 jpeg_destroy_decompress(&fCInfo);
154 SkDEBUGCODE(fReadHeaderSucceeded = false;)
155 }
156
157 /**
158 * Initialize the cinfo struct.
159 * Calls jpeg_create_decompress, makes customizations, and
160 * finally calls jpeg_read_header. Returns true if jpeg_read_header
161 * returns JPEG_HEADER_OK.
162 * If cinfo was already initialized, destroyInfo must be called to
163 * destroy the old one. Must not be called after startTileDecompress.
164 */
165 bool initializeInfoAndReadHeader() {
166 SkASSERT(!fInfoInitialized && !fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000167 initialize_info(&fCInfo, &fSrcMgr);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000168 fInfoInitialized = true;
169 const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true));
170 SkDEBUGCODE(fReadHeaderSucceeded = success;)
171 return success;
djsollen@google.com11399402013-03-20 17:45:27 +0000172 }
173
174 jpeg_decompress_struct* cinfo() { return &fCInfo; }
175
djsollen@google.com11399402013-03-20 17:45:27 +0000176 huffman_index* huffmanIndex() { return &fHuffmanIndex; }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000177
178 /**
179 * Build the index to be used for tile based decoding.
180 * Must only be called after a successful call to
181 * initializeInfoAndReadHeader and must not be called more
182 * than once.
183 */
184 bool buildHuffmanIndex() {
185 SkASSERT(fReadHeaderSucceeded);
186 SkASSERT(!fHuffmanCreated);
187 jpeg_create_huffman_index(&fCInfo, &fHuffmanIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000188 SkASSERT(1 == fCInfo.scale_num && 1 == fCInfo.scale_denom);
scroggo@google.com57a52982013-08-27 20:42:22 +0000189 fHuffmanCreated = jpeg_build_huffman_index(&fCInfo, &fHuffmanIndex);
190 return fHuffmanCreated;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000191 }
192
193 /**
194 * Start tile based decoding. Must only be called after a
195 * successful call to buildHuffmanIndex, and must only be
196 * called once.
197 */
198 bool startTileDecompress() {
199 SkASSERT(fHuffmanCreated);
200 SkASSERT(fReadHeaderSucceeded);
201 SkASSERT(!fDecompressStarted);
202 if (jpeg_start_tile_decompress(&fCInfo)) {
203 fDecompressStarted = true;
204 return true;
205 }
206 return false;
207 }
djsollen@google.com11399402013-03-20 17:45:27 +0000208
209private:
210 skjpeg_source_mgr fSrcMgr;
scroggoa1193e42015-01-21 12:09:53 -0800211 SkAutoTDelete<SkStream> fStream;
djsollen@google.com11399402013-03-20 17:45:27 +0000212 jpeg_decompress_struct fCInfo;
djsollen@google.com11399402013-03-20 17:45:27 +0000213 huffman_index fHuffmanIndex;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000214 bool fInfoInitialized;
215 bool fHuffmanCreated;
216 bool fDecompressStarted;
217 SkDEBUGCODE(bool fReadHeaderSucceeded;)
djsollen@google.com11399402013-03-20 17:45:27 +0000218};
scroggo@google.coma1a51542013-08-07 21:02:32 +0000219#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000220
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000221class SkJPEGImageDecoder : public SkImageDecoder {
222public:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000223#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000224 SkJPEGImageDecoder() {
225 fImageIndex = NULL;
226 fImageWidth = 0;
227 fImageHeight = 0;
228 }
229
230 virtual ~SkJPEGImageDecoder() {
231 SkDELETE(fImageIndex);
232 }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000233#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000234
mtklein72c9faa2015-01-09 10:06:39 -0800235 Format getFormat() const SK_OVERRIDE {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000236 return kJPEG_Format;
237 }
238
239protected:
scroggo@google.comd79277f2013-08-07 19:53:53 +0000240#ifdef SK_BUILD_FOR_ANDROID
mtklein72c9faa2015-01-09 10:06:39 -0800241 bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
242 bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
djsollen@google.com11399402013-03-20 17:45:27 +0000243#endif
mtklein72c9faa2015-01-09 10:06:39 -0800244 Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
sugoib227e372014-10-16 13:10:57 -0700245 virtual bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
246 void* planes[3], size_t rowBytes[3],
247 SkYUVColorSpace* colorSpace) SK_OVERRIDE;
djsollen@google.com11399402013-03-20 17:45:27 +0000248
249private:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000250#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000251 SkJPEGImageIndex* fImageIndex;
252 int fImageWidth;
253 int fImageHeight;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000254#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000255
scroggo@google.com590a5af2013-08-07 21:09:13 +0000256 /**
reed6c225732014-06-09 19:52:07 -0700257 * Determine the appropriate bitmap colortype and out_color_space based on
scroggo@google.com590a5af2013-08-07 21:09:13 +0000258 * both the preference of the caller and the jpeg_color_space on the
259 * jpeg_decompress_struct passed in.
260 * Must be called after jpeg_read_header.
261 */
reed6c225732014-06-09 19:52:07 -0700262 SkColorType getBitmapColorType(jpeg_decompress_struct*);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000263
djsollen@google.com11399402013-03-20 17:45:27 +0000264 typedef SkImageDecoder INHERITED;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000265};
266
267//////////////////////////////////////////////////////////////////////////
268
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000269/* Automatically clean up after throwing an exception */
270class JPEGAutoClean {
271public:
272 JPEGAutoClean(): cinfo_ptr(NULL) {}
273 ~JPEGAutoClean() {
274 if (cinfo_ptr) {
275 jpeg_destroy_decompress(cinfo_ptr);
276 }
277 }
278 void set(jpeg_decompress_struct* info) {
279 cinfo_ptr = info;
280 }
281private:
282 jpeg_decompress_struct* cinfo_ptr;
283};
284
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000285///////////////////////////////////////////////////////////////////////////////
286
287/* If we need to better match the request, we might examine the image and
288 output dimensions, and determine if the downsampling jpeg provided is
289 not sufficient. If so, we can recompute a modified sampleSize value to
290 make up the difference.
291
292 To skip this additional scaling, just set sampleSize = 1; below.
293 */
294static int recompute_sampleSize(int sampleSize,
295 const jpeg_decompress_struct& cinfo) {
296 return sampleSize * cinfo.output_width / cinfo.image_width;
297}
298
299static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
300 /* These are initialized to 0, so if they have non-zero values, we assume
301 they are "valid" (i.e. have been computed by libjpeg)
302 */
djsollen@google.com11399402013-03-20 17:45:27 +0000303 return 0 != cinfo.output_width && 0 != cinfo.output_height;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000304}
305
djsollen@google.com11399402013-03-20 17:45:27 +0000306static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000307 for (int i = 0; i < count; i++) {
308 JSAMPLE* rowptr = (JSAMPLE*)buffer;
309 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
djsollen@google.com11399402013-03-20 17:45:27 +0000310 if (1 != row_count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000311 return false;
312 }
313 }
314 return true;
315}
316
scroggo@google.comd79277f2013-08-07 19:53:53 +0000317#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000318static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
319 huffman_index *index, void* buffer, int count) {
320 for (int i = 0; i < count; i++) {
321 JSAMPLE* rowptr = (JSAMPLE*)buffer;
322 int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
323 if (1 != row_count) {
324 return false;
325 }
326 }
327 return true;
328}
329#endif
330
scroggo2a120802014-10-22 12:07:00 -0700331///////////////////////////////////////////////////////////////////////////////
332
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000333// This guy exists just to aid in debugging, as it allows debuggers to just
334// set a break-point in one place to see all error exists.
scroggo2a120802014-10-22 12:07:00 -0700335static void print_jpeg_decoder_errors(const jpeg_decompress_struct& cinfo,
sugoib227e372014-10-16 13:10:57 -0700336 int width, int height, const char caller[]) {
halcanary@google.com04b57f82013-10-14 20:08:48 +0000337 if (!(c_suppressJPEGImageDecoderErrors)) {
338 char buffer[JMSG_LENGTH_MAX];
339 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
340 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
sugoib227e372014-10-16 13:10:57 -0700341 cinfo.err->msg_code, buffer, caller, width, height);
halcanary@google.com04b57f82013-10-14 20:08:48 +0000342 }
scroggo2a120802014-10-22 12:07:00 -0700343}
344
345static bool return_false(const jpeg_decompress_struct& cinfo,
346 const char caller[]) {
347 print_jpeg_decoder_errors(cinfo, 0, 0, caller);
348 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000349}
350
mtkleindf78d0a2014-10-23 10:13:21 -0700351#ifdef SK_BUILD_FOR_ANDROID
sugoib227e372014-10-16 13:10:57 -0700352static bool return_false(const jpeg_decompress_struct& cinfo,
353 const SkBitmap& bm, const char caller[]) {
scroggo2a120802014-10-22 12:07:00 -0700354 print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
355 return false;
sugoib227e372014-10-16 13:10:57 -0700356}
mtkleindf78d0a2014-10-23 10:13:21 -0700357#endif
sugoib227e372014-10-16 13:10:57 -0700358
scroggo2a120802014-10-22 12:07:00 -0700359static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo,
360 const SkBitmap& bm, const char caller[]) {
361 print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
362 return SkImageDecoder::kFailure;
363}
364
365///////////////////////////////////////////////////////////////////////////////
366
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000367// Convert a scanline of CMYK samples to RGBX in place. Note that this
368// method moves the "scanline" pointer in its processing
369static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
370 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000371 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000372 // from easyrgb.com):
373 // CMYK -> CMY
374 // C = ( C * (1 - K) + K ) // for each CMY component
375 // CMY -> RGB
376 // R = ( 1 - C ) * 255 // for each RGB component
377 // Unfortunately we are seeing inverted CMYK so all the original terms
378 // are 1-. This yields:
379 // CMYK -> CMY
380 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
381 // The conversion from CMY->RGB remains the same
382 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
383 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
384 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
385 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
386 scanline[3] = 255;
387 }
388}
389
scroggo@google.com590a5af2013-08-07 21:09:13 +0000390/**
391 * Common code for setting the error manager.
392 */
393static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
394 SkASSERT(cinfo != NULL);
395 SkASSERT(errorManager != NULL);
396 cinfo->err = jpeg_std_error(errorManager);
397 errorManager->error_exit = skjpeg_error_exit;
398}
399
400/**
401 * Common code for turning off upsampling and smoothing. Turning these
402 * off helps performance without showing noticable differences in the
403 * resulting bitmap.
404 */
405static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
406 SkASSERT(cinfo != NULL);
407 /* this gives about 30% performance improvement. In theory it may
408 reduce the visual quality, in practice I'm not seeing a difference
409 */
410 cinfo->do_fancy_upsampling = 0;
411
412 /* this gives another few percents */
413 cinfo->do_block_smoothing = 0;
414}
415
416/**
417 * Common code for setting the dct method.
418 */
419static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
420 SkASSERT(cinfo != NULL);
421#ifdef DCT_IFAST_SUPPORTED
422 if (decoder.getPreferQualityOverSpeed()) {
423 cinfo->dct_method = JDCT_ISLOW;
424 } else {
425 cinfo->dct_method = JDCT_IFAST;
426 }
427#else
428 cinfo->dct_method = JDCT_ISLOW;
429#endif
430}
431
reed6c225732014-06-09 19:52:07 -0700432SkColorType SkJPEGImageDecoder::getBitmapColorType(jpeg_decompress_struct* cinfo) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000433 SkASSERT(cinfo != NULL);
434
435 SrcDepth srcDepth = k32Bit_SrcDepth;
436 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
437 srcDepth = k8BitGray_SrcDepth;
438 }
439
reed6c225732014-06-09 19:52:07 -0700440 SkColorType colorType = this->getPrefColorType(srcDepth, /*hasAlpha*/ false);
441 switch (colorType) {
442 case kAlpha_8_SkColorType:
443 // Only respect A8 colortype if the original is grayscale,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000444 // in which case we will treat the grayscale as alpha
445 // values.
446 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
reed6c225732014-06-09 19:52:07 -0700447 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000448 }
449 break;
reed6c225732014-06-09 19:52:07 -0700450 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000451 // Fall through.
reed6c225732014-06-09 19:52:07 -0700452 case kARGB_4444_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000453 // Fall through.
reed6c225732014-06-09 19:52:07 -0700454 case kRGB_565_SkColorType:
455 // These are acceptable destination colortypes.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000456 break;
457 default:
reed6c225732014-06-09 19:52:07 -0700458 // Force all other colortypes to 8888.
459 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000460 break;
461 }
462
463 switch (cinfo->jpeg_color_space) {
464 case JCS_CMYK:
465 // Fall through.
466 case JCS_YCCK:
467 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
468 // so libjpeg will give us CMYK samples back and we will later
469 // manually convert them to RGB
470 cinfo->out_color_space = JCS_CMYK;
471 break;
472 case JCS_GRAYSCALE:
reed6c225732014-06-09 19:52:07 -0700473 if (kAlpha_8_SkColorType == colorType) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000474 cinfo->out_color_space = JCS_GRAYSCALE;
475 break;
476 }
477 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
reed6c225732014-06-09 19:52:07 -0700478 // colortype. Fall through to set to the default.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000479 default:
480 cinfo->out_color_space = JCS_RGB;
481 break;
482 }
reed6c225732014-06-09 19:52:07 -0700483 return colorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000484}
485
scroggo@google.com590a5af2013-08-07 21:09:13 +0000486/**
reed6c225732014-06-09 19:52:07 -0700487 * Based on the colortype and dither mode, adjust out_color_space and
488 * dither_mode of cinfo. Only does work in ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000489 */
490static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
reed6c225732014-06-09 19:52:07 -0700491 SkColorType colorType,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000492 const SkImageDecoder& decoder) {
493 SkASSERT(cinfo != NULL);
reed6c225732014-06-09 19:52:07 -0700494#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000495 cinfo->dither_mode = JDITHER_NONE;
496 if (JCS_CMYK == cinfo->out_color_space) {
497 return;
498 }
reed6c225732014-06-09 19:52:07 -0700499 switch (colorType) {
500 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000501 cinfo->out_color_space = JCS_RGBA_8888;
502 break;
reed6c225732014-06-09 19:52:07 -0700503 case kRGB_565_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000504 cinfo->out_color_space = JCS_RGB_565;
505 if (decoder.getDitherImage()) {
506 cinfo->dither_mode = JDITHER_ORDERED;
507 }
508 break;
509 default:
510 break;
511 }
scroggo@google.com590a5af2013-08-07 21:09:13 +0000512#endif
reed6c225732014-06-09 19:52:07 -0700513}
scroggo@google.com590a5af2013-08-07 21:09:13 +0000514
halcanary@google.comfed30372013-10-04 12:46:45 +0000515/**
516 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
517 Used when decoding fails partway through reading scanlines to fill
518 remaining lines. */
519static void fill_below_level(int y, SkBitmap* bitmap) {
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000520 SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
halcanary@google.comfed30372013-10-04 12:46:45 +0000521 SkCanvas canvas(*bitmap);
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000522 canvas.clipRect(SkRect::Make(rect));
halcanary@google.comfed30372013-10-04 12:46:45 +0000523 canvas.drawColor(SK_ColorWHITE);
524}
525
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000526/**
527 * Get the config and bytes per pixel of the source data. Return
528 * whether the data is supported.
529 */
530static bool get_src_config(const jpeg_decompress_struct& cinfo,
531 SkScaledBitmapSampler::SrcConfig* sc,
532 int* srcBytesPerPixel) {
533 SkASSERT(sc != NULL && srcBytesPerPixel != NULL);
534 if (JCS_CMYK == cinfo.out_color_space) {
535 // In this case we will manually convert the CMYK values to RGB
536 *sc = SkScaledBitmapSampler::kRGBX;
537 // The CMYK work-around relies on 4 components per pixel here
538 *srcBytesPerPixel = 4;
539 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
540 *sc = SkScaledBitmapSampler::kRGB;
541 *srcBytesPerPixel = 3;
542#ifdef ANDROID_RGB
543 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
544 *sc = SkScaledBitmapSampler::kRGBX;
545 *srcBytesPerPixel = 4;
546 } else if (JCS_RGB_565 == cinfo.out_color_space) {
547 *sc = SkScaledBitmapSampler::kRGB_565;
548 *srcBytesPerPixel = 2;
549#endif
550 } else if (1 == cinfo.out_color_components &&
551 JCS_GRAYSCALE == cinfo.out_color_space) {
552 *sc = SkScaledBitmapSampler::kGray;
553 *srcBytesPerPixel = 1;
554 } else {
555 return false;
556 }
557 return true;
558}
halcanary@google.comfed30372013-10-04 12:46:45 +0000559
scroggo2a120802014-10-22 12:07:00 -0700560SkImageDecoder::Result SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000561#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000562 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000563#endif
564
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000565 JPEGAutoClean autoClean;
566
567 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000568 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000569
scroggo@google.com590a5af2013-08-07 21:09:13 +0000570 skjpeg_error_mgr errorManager;
571 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000572
573 // All objects need to be instantiated before this setjmp call so that
574 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000575 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700576 return return_failure(cinfo, *bm, "setjmp");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000577 }
578
scroggo@google.com590a5af2013-08-07 21:09:13 +0000579 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000580 autoClean.set(&cinfo);
581
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000582 int status = jpeg_read_header(&cinfo, true);
583 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700584 return return_failure(cinfo, *bm, "read_header");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000585 }
586
587 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
588 can) much faster that we, just use their num/denom api to approximate
589 the size.
590 */
591 int sampleSize = this->getSampleSize();
592
scroggo@google.com590a5af2013-08-07 21:09:13 +0000593 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000594
scroggo@google.com590a5af2013-08-07 21:09:13 +0000595 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000596 cinfo.scale_denom = sampleSize;
597
scroggo@google.com590a5af2013-08-07 21:09:13 +0000598 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000599
reed6c225732014-06-09 19:52:07 -0700600 const SkColorType colorType = this->getBitmapColorType(&cinfo);
601 const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ?
602 kPremul_SkAlphaType : kOpaque_SkAlphaType;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000603
reed6c225732014-06-09 19:52:07 -0700604 adjust_out_color_space_and_dither(&cinfo, colorType, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000605
djsollen@google.com11399402013-03-20 17:45:27 +0000606 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000607 // Assume an A8 bitmap is not opaque to avoid the check of each
608 // individual pixel. It is very unlikely to be opaque, since
609 // an opaque A8 bitmap would not be very interesting.
610 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700611 bool success = bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
612 colorType, alphaType));
613 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000614 }
615
616 /* image_width and image_height are the original dimensions, available
617 after jpeg_read_header(). To see the scaled dimensions, we have to call
618 jpeg_start_decompress(), and then read output_width and output_height.
619 */
620 if (!jpeg_start_decompress(&cinfo)) {
621 /* If we failed here, we may still have enough information to return
622 to the caller if they just wanted (subsampled bounds). If sampleSize
623 was 1, then we would have already returned. Thus we just check if
624 we're in kDecodeBounds_Mode, and that we have valid output sizes.
625
626 One reason to fail here is that we have insufficient stream data
627 to complete the setup. However, output dimensions seem to get
628 computed very early, which is why this special check can pay off.
629 */
djsollen@google.com11399402013-03-20 17:45:27 +0000630 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000631 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
632 recompute_sampleSize(sampleSize, cinfo));
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000633 // Assume an A8 bitmap is not opaque to avoid the check of each
634 // individual pixel. It is very unlikely to be opaque, since
635 // an opaque A8 bitmap would not be very interesting.
636 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700637 bool success = bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
638 colorType, alphaType));
639 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000640 } else {
scroggo2a120802014-10-22 12:07:00 -0700641 return return_failure(cinfo, *bm, "start_decompress");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000642 }
643 }
644 sampleSize = recompute_sampleSize(sampleSize, cinfo);
645
djsollen@google.com11399402013-03-20 17:45:27 +0000646 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000647 // Assume an A8 bitmap is not opaque to avoid the check of each
648 // individual pixel. It is very unlikely to be opaque, since
649 // an opaque A8 bitmap would not be very interesting.
650 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700651 bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
652 colorType, alphaType));
scroggo@google.combc69ce92013-07-09 15:45:14 +0000653 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo2a120802014-10-22 12:07:00 -0700654 return kSuccess;
scroggo@google.combc69ce92013-07-09 15:45:14 +0000655 }
656 if (!this->allocPixelRef(bm, NULL)) {
scroggo2a120802014-10-22 12:07:00 -0700657 return return_failure(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000658 }
659
660 SkAutoLockPixels alp(*bm);
661
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000662#ifdef ANDROID_RGB
663 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
664 a significant performance boost.
665 */
666 if (sampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700667 ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||
668 (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000669 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000670 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000671 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000672
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000673 while (cinfo.output_scanline < cinfo.output_height) {
674 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000675 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000676 // if row_count == 0, then we didn't get a scanline,
677 // so return early. We will return a partial image.
678 fill_below_level(cinfo.output_scanline, bm);
679 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700680 jpeg_finish_decompress(&cinfo);
681 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000682 }
683 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700684 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000685 }
686 rowptr += bpr;
687 }
688 jpeg_finish_decompress(&cinfo);
scroggo2a120802014-10-22 12:07:00 -0700689 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000690 }
691#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000692
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000693 // check for supported formats
694 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000695 int srcBytesPerPixel;
696
697 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
scroggo2a120802014-10-22 12:07:00 -0700698 return return_failure(cinfo, *bm, "jpeg colorspace");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000699 }
700
scroggo@google.com8d239242013-10-01 17:27:15 +0000701 if (!sampler.begin(bm, sc, *this)) {
scroggo2a120802014-10-22 12:07:00 -0700702 return return_failure(cinfo, *bm, "sampler.begin");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000703 }
704
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000705 SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000706 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000707
708 // Possibly skip initial rows [sampler.srcY0]
709 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
scroggo2a120802014-10-22 12:07:00 -0700710 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000711 }
712
713 // now loop through scanlines until y == bm->height() - 1
714 for (int y = 0;; y++) {
715 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
716 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
717 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000718 // if row_count == 0, then we didn't get a scanline,
719 // so return early. We will return a partial image.
720 fill_below_level(y, bm);
721 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700722 jpeg_finish_decompress(&cinfo);
scroggoc6b8ffa2014-12-17 06:55:02 -0800723 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000724 }
725 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700726 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000727 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000728
729 if (JCS_CMYK == cinfo.out_color_space) {
730 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
731 }
732
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000733 sampler.next(srcRow);
734 if (bm->height() - 1 == y) {
735 // we're done
736 break;
737 }
738
739 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
scroggo2a120802014-10-22 12:07:00 -0700740 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000741 }
742 }
743
744 // we formally skip the rest, so we don't get a complaint from libjpeg
745 if (!skip_src_rows(&cinfo, srcRow,
746 cinfo.output_height - cinfo.output_scanline)) {
scroggo2a120802014-10-22 12:07:00 -0700747 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000748 }
749 jpeg_finish_decompress(&cinfo);
750
scroggo2a120802014-10-22 12:07:00 -0700751 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000752}
753
scroggo2a120802014-10-22 12:07:00 -0700754///////////////////////////////////////////////////////////////////////////////
755
sugoib227e372014-10-16 13:10:57 -0700756enum SizeType {
757 kSizeForMemoryAllocation_SizeType,
758 kActualSize_SizeType
759};
760
761static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component,
762 SizeType sizeType) {
763 if (sizeType == kSizeForMemoryAllocation_SizeType) {
764 return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE,
765 info.cur_comp_info[component]->height_in_blocks * DCTSIZE);
766 }
767 return SkISize::Make(info.cur_comp_info[component]->downsampled_width,
768 info.cur_comp_info[component]->downsampled_height);
769}
770
sugoib227e372014-10-16 13:10:57 -0700771static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
772 SizeType sizeType) {
773 for (int i = 0; i < 3; ++i) {
774 componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
775 }
776}
777
778static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) {
779 // 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
864
865 if (this->getSampleSize() != 1) {
866 return false; // Resizing not supported
867 }
868
869 JPEGAutoClean autoClean;
870
871 jpeg_decompress_struct cinfo;
872 skjpeg_source_mgr srcManager(stream, this);
873
874 skjpeg_error_mgr errorManager;
875 set_error_mgr(&cinfo, &errorManager);
876
877 // All objects need to be instantiated before this setjmp call so that
878 // they will be cleaned up properly if an error occurs.
879 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700880 return return_false(cinfo, "setjmp YUV8");
sugoib227e372014-10-16 13:10:57 -0700881 }
882
883 initialize_info(&cinfo, &srcManager);
884 autoClean.set(&cinfo);
885
886 int status = jpeg_read_header(&cinfo, true);
887 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700888 return return_false(cinfo, "read_header YUV8");
sugoib227e372014-10-16 13:10:57 -0700889 }
890
891 if (cinfo.jpeg_color_space != JCS_YCbCr) {
892 // It's not an error to not be encoded in YUV, so no need to use return_false()
893 return false;
894 }
895
896 cinfo.out_color_space = JCS_YCbCr;
897 cinfo.raw_data_out = TRUE;
898
899 if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only
900 update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType);
901 return true;
902 }
903
904 set_dct_method(*this, &cinfo);
905
906 SkASSERT(1 == cinfo.scale_num);
907 cinfo.scale_denom = 1;
908
909 turn_off_visual_optimizations(&cinfo);
910
911#ifdef ANDROID_RGB
912 cinfo.dither_mode = JDITHER_NONE;
913#endif
914
915 /* image_width and image_height are the original dimensions, available
916 after jpeg_read_header(). To see the scaled dimensions, we have to call
917 jpeg_start_decompress(), and then read output_width and output_height.
918 */
919 if (!jpeg_start_decompress(&cinfo)) {
scroggo2a120802014-10-22 12:07:00 -0700920 return return_false(cinfo, "start_decompress YUV8");
sugoib227e372014-10-16 13:10:57 -0700921 }
922
923 if (!output_raw_data(cinfo, planes, rowBytes)) {
scroggo2a120802014-10-22 12:07:00 -0700924 return return_false(cinfo, "output_raw_data");
sugoib227e372014-10-16 13:10:57 -0700925 }
926
927 update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
928 jpeg_finish_decompress(&cinfo);
929
930 if (NULL != colorSpace) {
931 *colorSpace = kJPEG_SkYUVColorSpace;
932 }
933
934 return true;
935}
936
scroggo2a120802014-10-22 12:07:00 -0700937///////////////////////////////////////////////////////////////////////////////
938
scroggo@google.comd79277f2013-08-07 19:53:53 +0000939#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000940bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000941
scroggo@google.coma1a51542013-08-07 21:02:32 +0000942 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000943
944 skjpeg_error_mgr sk_err;
scroggo6d7e47a2015-01-22 10:27:25 -0800945 set_error_mgr(imageIndex->cinfo(), &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000946
947 // All objects need to be instantiated before this setjmp call so that
948 // they will be cleaned up properly if an error occurs.
949 if (setjmp(sk_err.fJmpBuf)) {
950 return false;
951 }
952
953 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000954 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000955 return false;
956 }
957
scroggo@google.coma1a51542013-08-07 21:02:32 +0000958 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000959 return false;
960 }
961
962 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000963 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000964
965 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000966 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000967 return false;
968 }
969
scroggo6d7e47a2015-01-22 10:27:25 -0800970 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
971 // We have a new cinfo, so set the error mgr again.
972 set_error_mgr(cinfo, &sk_err);
973
scroggo@google.com590a5af2013-08-07 21:09:13 +0000974 // FIXME: This sets cinfo->out_color_space, which we may change later
975 // based on the config in onDecodeSubset. This should be fine, since
976 // jpeg_init_read_tile_scanline will check out_color_space again after
977 // that change (when it calls jinit_color_deconverter).
reed6c225732014-06-09 19:52:07 -0700978 (void) this->getBitmapColorType(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000979
980 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000981
982 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000983 if (!imageIndex->startTileDecompress()) {
984 return false;
985 }
djsollen@google.com11399402013-03-20 17:45:27 +0000986
scroggo@google.coma1a51542013-08-07 21:02:32 +0000987 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000988 fImageWidth = cinfo->output_width;
989 fImageHeight = cinfo->output_height;
990
991 if (width) {
992 *width = fImageWidth;
993 }
994 if (height) {
995 *height = fImageHeight;
996 }
djsollen@google.com11399402013-03-20 17:45:27 +0000997
998 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000999 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +00001000
1001 return true;
1002}
1003
scroggo@google.com7e6fcee2013-05-03 20:14:28 +00001004bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +00001005 if (NULL == fImageIndex) {
1006 return false;
1007 }
1008 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
1009
1010 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
1011 if (!rect.intersect(region)) {
1012 // If the requested region is entirely outside the image return false
1013 return false;
1014 }
1015
1016
1017 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +00001018 set_error_mgr(cinfo, &errorManager);
1019
djsollen@google.com11399402013-03-20 17:45:27 +00001020 if (setjmp(errorManager.fJmpBuf)) {
1021 return false;
1022 }
1023
1024 int requestedSampleSize = this->getSampleSize();
1025 cinfo->scale_denom = requestedSampleSize;
1026
scroggo@google.com590a5af2013-08-07 21:09:13 +00001027 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +00001028
reed6c225732014-06-09 19:52:07 -07001029 const SkColorType colorType = this->getBitmapColorType(cinfo);
1030 adjust_out_color_space_and_dither(cinfo, colorType, *this);
djsollen@google.com11399402013-03-20 17:45:27 +00001031
1032 int startX = rect.fLeft;
1033 int startY = rect.fTop;
1034 int width = rect.width();
1035 int height = rect.height();
1036
1037 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
1038 &startX, &startY, &width, &height);
1039 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
1040 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
1041
1042 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
1043
1044 SkBitmap bitmap;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001045 // Assume an A8 bitmap is not opaque to avoid the check of each
1046 // individual pixel. It is very unlikely to be opaque, since
1047 // an opaque A8 bitmap would not be very interesting.
1048 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -07001049 bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType,
1050 kAlpha_8_SkColorType == colorType ?
1051 kPremul_SkAlphaType : kOpaque_SkAlphaType));
djsollen@google.com11399402013-03-20 17:45:27 +00001052
1053 // Check ahead of time if the swap(dest, src) is possible or not.
1054 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
1055 // swap happening. If no, then we will use alloc to allocate pixels to
1056 // prevent garbage collection.
1057 int w = rect.width() / actualSampleSize;
1058 int h = rect.height() / actualSampleSize;
1059 bool swapOnly = (rect == region) && bm->isNull() &&
1060 (w == bitmap.width()) && (h == bitmap.height()) &&
1061 ((startX - rect.x()) / actualSampleSize == 0) &&
1062 ((startY - rect.y()) / actualSampleSize == 0);
1063 if (swapOnly) {
1064 if (!this->allocPixelRef(&bitmap, NULL)) {
1065 return return_false(*cinfo, bitmap, "allocPixelRef");
1066 }
1067 } else {
reed84825042014-09-02 12:50:45 -07001068 if (!bitmap.tryAllocPixels()) {
djsollen@google.com11399402013-03-20 17:45:27 +00001069 return return_false(*cinfo, bitmap, "allocPixels");
1070 }
1071 }
1072
1073 SkAutoLockPixels alp(bitmap);
1074
1075#ifdef ANDROID_RGB
1076 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
1077 a significant performance boost.
1078 */
1079 if (skiaSampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -07001080 ((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||
1081 (kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565)))
djsollen@google.com11399402013-03-20 17:45:27 +00001082 {
1083 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
1084 INT32 const bpr = bitmap.rowBytes();
1085 int rowTotalCount = 0;
1086
1087 while (rowTotalCount < height) {
1088 int rowCount = jpeg_read_tile_scanline(cinfo,
1089 fImageIndex->huffmanIndex(),
1090 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001091 // if rowCount == 0, then we didn't get a scanline, so abort.
1092 // onDecodeSubset() relies on onBuildTileIndex(), which
1093 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001094 if (0 == rowCount) {
1095 return return_false(*cinfo, bitmap, "read_scanlines");
1096 }
1097 if (this->shouldCancelDecode()) {
1098 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1099 }
1100 rowTotalCount += rowCount;
1101 rowptr += bpr;
1102 }
1103
1104 if (swapOnly) {
1105 bm->swap(bitmap);
1106 } else {
1107 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1108 region.width(), region.height(), startX, startY);
1109 }
1110 return true;
1111 }
1112#endif
1113
1114 // check for supported formats
1115 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001116 int srcBytesPerPixel;
1117
1118 if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001119 return return_false(*cinfo, *bm, "jpeg colorspace");
1120 }
1121
scroggo@google.com8d239242013-10-01 17:27:15 +00001122 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001123 return return_false(*cinfo, bitmap, "sampler.begin");
1124 }
1125
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001126 SkAutoMalloc srcStorage(width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +00001127 uint8_t* srcRow = (uint8_t*)srcStorage.get();
1128
1129 // Possibly skip initial rows [sampler.srcY0]
1130 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
1131 return return_false(*cinfo, bitmap, "skip rows");
1132 }
1133
1134 // now loop through scanlines until y == bitmap->height() - 1
1135 for (int y = 0;; y++) {
1136 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
1137 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001138 // if row_count == 0, then we didn't get a scanline, so abort.
1139 // onDecodeSubset() relies on onBuildTileIndex(), which
1140 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001141 if (0 == row_count) {
1142 return return_false(*cinfo, bitmap, "read_scanlines");
1143 }
1144 if (this->shouldCancelDecode()) {
1145 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1146 }
1147
1148 if (JCS_CMYK == cinfo->out_color_space) {
1149 convert_CMYK_to_RGB(srcRow, width);
1150 }
1151
1152 sampler.next(srcRow);
1153 if (bitmap.height() - 1 == y) {
1154 // we're done
1155 break;
1156 }
1157
1158 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
1159 sampler.srcDY() - 1)) {
1160 return return_false(*cinfo, bitmap, "skip rows");
1161 }
1162 }
1163 if (swapOnly) {
1164 bm->swap(bitmap);
1165 } else {
1166 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1167 region.width(), region.height(), startX, startY);
1168 }
1169 return true;
1170}
1171#endif
1172
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001173///////////////////////////////////////////////////////////////////////////////
1174
1175#include "SkColorPriv.h"
1176
1177// taken from jcolor.c in libjpeg
1178#if 0 // 16bit - precise but slow
1179 #define CYR 19595 // 0.299
1180 #define CYG 38470 // 0.587
1181 #define CYB 7471 // 0.114
1182
1183 #define CUR -11059 // -0.16874
1184 #define CUG -21709 // -0.33126
1185 #define CUB 32768 // 0.5
1186
1187 #define CVR 32768 // 0.5
1188 #define CVG -27439 // -0.41869
1189 #define CVB -5329 // -0.08131
1190
1191 #define CSHIFT 16
1192#else // 8bit - fast, slightly less precise
1193 #define CYR 77 // 0.299
1194 #define CYG 150 // 0.587
1195 #define CYB 29 // 0.114
1196
1197 #define CUR -43 // -0.16874
1198 #define CUG -85 // -0.33126
1199 #define CUB 128 // 0.5
1200
1201 #define CVR 128 // 0.5
1202 #define CVG -107 // -0.41869
1203 #define CVB -21 // -0.08131
1204
1205 #define CSHIFT 8
1206#endif
1207
1208static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
1209 int r = SkGetPackedR32(c);
1210 int g = SkGetPackedG32(c);
1211 int b = SkGetPackedB32(c);
1212
1213 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
1214 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
1215 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
1216
1217 dst[0] = SkToU8(y);
1218 dst[1] = SkToU8(u + 128);
1219 dst[2] = SkToU8(v + 128);
1220}
1221
1222static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
1223 int r = SkGetPackedR4444(c);
1224 int g = SkGetPackedG4444(c);
1225 int b = SkGetPackedB4444(c);
1226
1227 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
1228 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
1229 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
1230
1231 dst[0] = SkToU8(y);
1232 dst[1] = SkToU8(u + 128);
1233 dst[2] = SkToU8(v + 128);
1234}
1235
1236static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
1237 int r = SkGetPackedR16(c);
1238 int g = SkGetPackedG16(c);
1239 int b = SkGetPackedB16(c);
1240
1241 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
1242 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
1243 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
1244
1245 dst[0] = SkToU8(y);
1246 dst[1] = SkToU8(u + 128);
1247 dst[2] = SkToU8(v + 128);
1248}
1249
1250///////////////////////////////////////////////////////////////////////////////
1251
1252typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1253 const void* SK_RESTRICT src, int width,
1254 const SkPMColor* SK_RESTRICT ctable);
1255
1256static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1257 const void* SK_RESTRICT srcRow, int width,
1258 const SkPMColor*) {
1259 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1260 while (--width >= 0) {
1261#ifdef WE_CONVERT_TO_YUV
1262 rgb2yuv_32(dst, *src++);
1263#else
1264 uint32_t c = *src++;
1265 dst[0] = SkGetPackedR32(c);
1266 dst[1] = SkGetPackedG32(c);
1267 dst[2] = SkGetPackedB32(c);
1268#endif
1269 dst += 3;
1270 }
1271}
1272
1273static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1274 const void* SK_RESTRICT srcRow, int width,
1275 const SkPMColor*) {
1276 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1277 while (--width >= 0) {
1278#ifdef WE_CONVERT_TO_YUV
1279 rgb2yuv_4444(dst, *src++);
1280#else
1281 SkPMColor16 c = *src++;
1282 dst[0] = SkPacked4444ToR32(c);
1283 dst[1] = SkPacked4444ToG32(c);
1284 dst[2] = SkPacked4444ToB32(c);
1285#endif
1286 dst += 3;
1287 }
1288}
1289
1290static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1291 const void* SK_RESTRICT srcRow, int width,
1292 const SkPMColor*) {
1293 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1294 while (--width >= 0) {
1295#ifdef WE_CONVERT_TO_YUV
1296 rgb2yuv_16(dst, *src++);
1297#else
1298 uint16_t c = *src++;
1299 dst[0] = SkPacked16ToR32(c);
1300 dst[1] = SkPacked16ToG32(c);
1301 dst[2] = SkPacked16ToB32(c);
1302#endif
1303 dst += 3;
1304 }
1305}
1306
1307static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1308 const void* SK_RESTRICT srcRow, int width,
1309 const SkPMColor* SK_RESTRICT ctable) {
1310 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1311 while (--width >= 0) {
1312#ifdef WE_CONVERT_TO_YUV
1313 rgb2yuv_32(dst, ctable[*src++]);
1314#else
1315 uint32_t c = ctable[*src++];
1316 dst[0] = SkGetPackedR32(c);
1317 dst[1] = SkGetPackedG32(c);
1318 dst[2] = SkGetPackedB32(c);
1319#endif
1320 dst += 3;
1321 }
1322}
1323
1324static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -07001325 switch (bm.colorType()) {
1326 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001327 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -07001328 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001329 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -07001330 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001331 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -07001332 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001333 return Write_Index_YUV;
1334 default:
1335 return NULL;
1336 }
1337}
1338
1339class SkJPEGImageEncoder : public SkImageEncoder {
1340protected:
1341 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1342#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001343 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001344#endif
1345
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001346 SkAutoLockPixels alp(bm);
1347 if (NULL == bm.getPixels()) {
1348 return false;
1349 }
1350
1351 jpeg_compress_struct cinfo;
1352 skjpeg_error_mgr sk_err;
1353 skjpeg_destination_mgr sk_wstream(stream);
1354
1355 // allocate these before set call setjmp
1356 SkAutoMalloc oneRow;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001357
1358 cinfo.err = jpeg_std_error(&sk_err);
1359 sk_err.error_exit = skjpeg_error_exit;
1360 if (setjmp(sk_err.fJmpBuf)) {
1361 return false;
1362 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001363
mtklein@google.com8d725b22013-07-24 16:20:05 +00001364 // Keep after setjmp or mark volatile.
1365 const WriteScanline writer = ChooseWriter(bm);
1366 if (NULL == writer) {
1367 return false;
1368 }
1369
1370 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001371 cinfo.dest = &sk_wstream;
1372 cinfo.image_width = bm.width();
1373 cinfo.image_height = bm.height();
1374 cinfo.input_components = 3;
1375#ifdef WE_CONVERT_TO_YUV
1376 cinfo.in_color_space = JCS_YCbCr;
1377#else
1378 cinfo.in_color_space = JCS_RGB;
1379#endif
1380 cinfo.input_gamma = 1;
1381
1382 jpeg_set_defaults(&cinfo);
1383 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001384#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001385 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001386#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001387
1388 jpeg_start_compress(&cinfo, TRUE);
1389
1390 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001391 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001392
mtklein775b8192014-12-02 09:11:25 -08001393 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : NULL;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001394 const void* srcRow = bm.getPixels();
1395
1396 while (cinfo.next_scanline < cinfo.image_height) {
1397 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1398
1399 writer(oneRowP, srcRow, width, colors);
1400 row_pointer[0] = oneRowP;
1401 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1402 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1403 }
1404
1405 jpeg_finish_compress(&cinfo);
1406 jpeg_destroy_compress(&cinfo);
1407
1408 return true;
1409 }
1410};
1411
1412///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001413DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1414DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1415///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001416
scroggo@google.comb5571b32013-09-25 21:34:24 +00001417static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001418 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001419 static const size_t HEADER_SIZE = sizeof(gHeader);
1420
1421 char buffer[HEADER_SIZE];
1422 size_t len = stream->read(buffer, HEADER_SIZE);
1423
1424 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001425 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001426 }
1427 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001428 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001429 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001430 return true;
1431}
1432
scroggo@google.comb5571b32013-09-25 21:34:24 +00001433
1434static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001435 if (is_jpeg(stream)) {
1436 return SkNEW(SkJPEGImageDecoder);
1437 }
1438 return NULL;
1439}
1440
scroggo@google.comb5571b32013-09-25 21:34:24 +00001441static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001442 if (is_jpeg(stream)) {
1443 return SkImageDecoder::kJPEG_Format;
1444 }
1445 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001446}
1447
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001448static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001449 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1450}
1451
mtklein@google.combd6343b2013-09-04 17:20:18 +00001452static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1453static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1454static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);