blob: d403b7451ddd253ae6f8fab99a47e8efa95efde0 [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
halcanary385fe4d2015-08-26 13:07:48 -0700213 virtual ~SkJPEGImageDecoder() { delete fImageIndex; }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000214#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000215
mtklein36352bf2015-03-25 18:17:31 -0700216 Format getFormat() const override {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000217 return kJPEG_Format;
218 }
219
220protected:
scroggo@google.comd79277f2013-08-07 19:53:53 +0000221#ifdef SK_BUILD_FOR_ANDROID
mtklein36352bf2015-03-25 18:17:31 -0700222 bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) override;
223 bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) override;
djsollen@google.com11399402013-03-20 17:45:27 +0000224#endif
mtklein36352bf2015-03-25 18:17:31 -0700225 Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
tfarina7831a4b2015-04-27 07:53:07 -0700226 bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
227 void* planes[3], size_t rowBytes[3],
228 SkYUVColorSpace* colorSpace) override;
djsollen@google.com11399402013-03-20 17:45:27 +0000229
230private:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000231#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000232 SkJPEGImageIndex* fImageIndex;
233 int fImageWidth;
234 int fImageHeight;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000235#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000236
scroggo@google.com590a5af2013-08-07 21:09:13 +0000237 /**
reed6c225732014-06-09 19:52:07 -0700238 * Determine the appropriate bitmap colortype and out_color_space based on
scroggo@google.com590a5af2013-08-07 21:09:13 +0000239 * both the preference of the caller and the jpeg_color_space on the
240 * jpeg_decompress_struct passed in.
241 * Must be called after jpeg_read_header.
242 */
reed6c225732014-06-09 19:52:07 -0700243 SkColorType getBitmapColorType(jpeg_decompress_struct*);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000244
djsollen@google.com11399402013-03-20 17:45:27 +0000245 typedef SkImageDecoder INHERITED;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000246};
247
248//////////////////////////////////////////////////////////////////////////
249
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000250/* Automatically clean up after throwing an exception */
251class JPEGAutoClean {
252public:
253 JPEGAutoClean(): cinfo_ptr(NULL) {}
254 ~JPEGAutoClean() {
255 if (cinfo_ptr) {
256 jpeg_destroy_decompress(cinfo_ptr);
257 }
258 }
259 void set(jpeg_decompress_struct* info) {
260 cinfo_ptr = info;
261 }
262private:
263 jpeg_decompress_struct* cinfo_ptr;
264};
265
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000266///////////////////////////////////////////////////////////////////////////////
267
268/* If we need to better match the request, we might examine the image and
269 output dimensions, and determine if the downsampling jpeg provided is
270 not sufficient. If so, we can recompute a modified sampleSize value to
271 make up the difference.
272
273 To skip this additional scaling, just set sampleSize = 1; below.
274 */
275static int recompute_sampleSize(int sampleSize,
276 const jpeg_decompress_struct& cinfo) {
277 return sampleSize * cinfo.output_width / cinfo.image_width;
278}
279
280static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
281 /* These are initialized to 0, so if they have non-zero values, we assume
282 they are "valid" (i.e. have been computed by libjpeg)
283 */
djsollen@google.com11399402013-03-20 17:45:27 +0000284 return 0 != cinfo.output_width && 0 != cinfo.output_height;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000285}
286
djsollen@google.com11399402013-03-20 17:45:27 +0000287static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000288 for (int i = 0; i < count; i++) {
289 JSAMPLE* rowptr = (JSAMPLE*)buffer;
290 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
djsollen@google.com11399402013-03-20 17:45:27 +0000291 if (1 != row_count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000292 return false;
293 }
294 }
295 return true;
296}
297
scroggo@google.comd79277f2013-08-07 19:53:53 +0000298#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000299static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
300 huffman_index *index, void* buffer, int count) {
301 for (int i = 0; i < count; i++) {
302 JSAMPLE* rowptr = (JSAMPLE*)buffer;
303 int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
304 if (1 != row_count) {
305 return false;
306 }
307 }
308 return true;
309}
310#endif
311
scroggo2a120802014-10-22 12:07:00 -0700312///////////////////////////////////////////////////////////////////////////////
313
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000314// This guy exists just to aid in debugging, as it allows debuggers to just
315// set a break-point in one place to see all error exists.
scroggo2a120802014-10-22 12:07:00 -0700316static void print_jpeg_decoder_errors(const jpeg_decompress_struct& cinfo,
sugoib227e372014-10-16 13:10:57 -0700317 int width, int height, const char caller[]) {
halcanary@google.com04b57f82013-10-14 20:08:48 +0000318 if (!(c_suppressJPEGImageDecoderErrors)) {
319 char buffer[JMSG_LENGTH_MAX];
320 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
321 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
sugoib227e372014-10-16 13:10:57 -0700322 cinfo.err->msg_code, buffer, caller, width, height);
halcanary@google.com04b57f82013-10-14 20:08:48 +0000323 }
scroggo2a120802014-10-22 12:07:00 -0700324}
325
326static bool return_false(const jpeg_decompress_struct& cinfo,
327 const char caller[]) {
328 print_jpeg_decoder_errors(cinfo, 0, 0, caller);
329 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000330}
331
mtkleindf78d0a2014-10-23 10:13:21 -0700332#ifdef SK_BUILD_FOR_ANDROID
sugoib227e372014-10-16 13:10:57 -0700333static bool return_false(const jpeg_decompress_struct& cinfo,
334 const SkBitmap& bm, const char caller[]) {
scroggo2a120802014-10-22 12:07:00 -0700335 print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
336 return false;
sugoib227e372014-10-16 13:10:57 -0700337}
mtkleindf78d0a2014-10-23 10:13:21 -0700338#endif
sugoib227e372014-10-16 13:10:57 -0700339
scroggo2a120802014-10-22 12:07:00 -0700340static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo,
341 const SkBitmap& bm, const char caller[]) {
342 print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
343 return SkImageDecoder::kFailure;
344}
345
346///////////////////////////////////////////////////////////////////////////////
347
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000348// Convert a scanline of CMYK samples to RGBX in place. Note that this
349// method moves the "scanline" pointer in its processing
350static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
351 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000352 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000353 // from easyrgb.com):
354 // CMYK -> CMY
355 // C = ( C * (1 - K) + K ) // for each CMY component
356 // CMY -> RGB
357 // R = ( 1 - C ) * 255 // for each RGB component
358 // Unfortunately we are seeing inverted CMYK so all the original terms
359 // are 1-. This yields:
360 // CMYK -> CMY
361 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
362 // The conversion from CMY->RGB remains the same
363 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
364 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
365 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
366 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
367 scanline[3] = 255;
368 }
369}
370
scroggo@google.com590a5af2013-08-07 21:09:13 +0000371/**
372 * Common code for setting the error manager.
373 */
374static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
375 SkASSERT(cinfo != NULL);
376 SkASSERT(errorManager != NULL);
377 cinfo->err = jpeg_std_error(errorManager);
378 errorManager->error_exit = skjpeg_error_exit;
379}
380
381/**
382 * Common code for turning off upsampling and smoothing. Turning these
383 * off helps performance without showing noticable differences in the
384 * resulting bitmap.
385 */
386static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
387 SkASSERT(cinfo != NULL);
388 /* this gives about 30% performance improvement. In theory it may
389 reduce the visual quality, in practice I'm not seeing a difference
390 */
391 cinfo->do_fancy_upsampling = 0;
392
393 /* this gives another few percents */
394 cinfo->do_block_smoothing = 0;
395}
396
397/**
398 * Common code for setting the dct method.
399 */
400static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
401 SkASSERT(cinfo != NULL);
402#ifdef DCT_IFAST_SUPPORTED
403 if (decoder.getPreferQualityOverSpeed()) {
404 cinfo->dct_method = JDCT_ISLOW;
405 } else {
406 cinfo->dct_method = JDCT_IFAST;
407 }
408#else
409 cinfo->dct_method = JDCT_ISLOW;
410#endif
411}
412
reed6c225732014-06-09 19:52:07 -0700413SkColorType SkJPEGImageDecoder::getBitmapColorType(jpeg_decompress_struct* cinfo) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000414 SkASSERT(cinfo != NULL);
415
416 SrcDepth srcDepth = k32Bit_SrcDepth;
417 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
418 srcDepth = k8BitGray_SrcDepth;
419 }
420
reed6c225732014-06-09 19:52:07 -0700421 SkColorType colorType = this->getPrefColorType(srcDepth, /*hasAlpha*/ false);
422 switch (colorType) {
423 case kAlpha_8_SkColorType:
424 // Only respect A8 colortype if the original is grayscale,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000425 // in which case we will treat the grayscale as alpha
426 // values.
427 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
reed6c225732014-06-09 19:52:07 -0700428 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000429 }
430 break;
reed6c225732014-06-09 19:52:07 -0700431 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000432 // Fall through.
reed6c225732014-06-09 19:52:07 -0700433 case kARGB_4444_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000434 // Fall through.
reed6c225732014-06-09 19:52:07 -0700435 case kRGB_565_SkColorType:
436 // These are acceptable destination colortypes.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000437 break;
438 default:
reed6c225732014-06-09 19:52:07 -0700439 // Force all other colortypes to 8888.
440 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000441 break;
442 }
443
444 switch (cinfo->jpeg_color_space) {
445 case JCS_CMYK:
446 // Fall through.
447 case JCS_YCCK:
448 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
449 // so libjpeg will give us CMYK samples back and we will later
450 // manually convert them to RGB
451 cinfo->out_color_space = JCS_CMYK;
452 break;
453 case JCS_GRAYSCALE:
reed6c225732014-06-09 19:52:07 -0700454 if (kAlpha_8_SkColorType == colorType) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000455 cinfo->out_color_space = JCS_GRAYSCALE;
456 break;
457 }
458 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
reed6c225732014-06-09 19:52:07 -0700459 // colortype. Fall through to set to the default.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000460 default:
461 cinfo->out_color_space = JCS_RGB;
462 break;
463 }
reed6c225732014-06-09 19:52:07 -0700464 return colorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000465}
466
scroggo@google.com590a5af2013-08-07 21:09:13 +0000467/**
reed6c225732014-06-09 19:52:07 -0700468 * Based on the colortype and dither mode, adjust out_color_space and
469 * dither_mode of cinfo. Only does work in ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000470 */
471static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
reed6c225732014-06-09 19:52:07 -0700472 SkColorType colorType,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000473 const SkImageDecoder& decoder) {
474 SkASSERT(cinfo != NULL);
reed6c225732014-06-09 19:52:07 -0700475#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000476 cinfo->dither_mode = JDITHER_NONE;
477 if (JCS_CMYK == cinfo->out_color_space) {
478 return;
479 }
reed6c225732014-06-09 19:52:07 -0700480 switch (colorType) {
481 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000482 cinfo->out_color_space = JCS_RGBA_8888;
483 break;
reed6c225732014-06-09 19:52:07 -0700484 case kRGB_565_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000485 cinfo->out_color_space = JCS_RGB_565;
486 if (decoder.getDitherImage()) {
487 cinfo->dither_mode = JDITHER_ORDERED;
488 }
489 break;
490 default:
491 break;
492 }
scroggo@google.com590a5af2013-08-07 21:09:13 +0000493#endif
reed6c225732014-06-09 19:52:07 -0700494}
scroggo@google.com590a5af2013-08-07 21:09:13 +0000495
halcanary@google.comfed30372013-10-04 12:46:45 +0000496/**
497 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
498 Used when decoding fails partway through reading scanlines to fill
499 remaining lines. */
500static void fill_below_level(int y, SkBitmap* bitmap) {
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000501 SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
halcanary@google.comfed30372013-10-04 12:46:45 +0000502 SkCanvas canvas(*bitmap);
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000503 canvas.clipRect(SkRect::Make(rect));
halcanary@google.comfed30372013-10-04 12:46:45 +0000504 canvas.drawColor(SK_ColorWHITE);
505}
506
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000507/**
508 * Get the config and bytes per pixel of the source data. Return
509 * whether the data is supported.
510 */
511static bool get_src_config(const jpeg_decompress_struct& cinfo,
512 SkScaledBitmapSampler::SrcConfig* sc,
513 int* srcBytesPerPixel) {
514 SkASSERT(sc != NULL && srcBytesPerPixel != NULL);
515 if (JCS_CMYK == cinfo.out_color_space) {
516 // In this case we will manually convert the CMYK values to RGB
517 *sc = SkScaledBitmapSampler::kRGBX;
518 // The CMYK work-around relies on 4 components per pixel here
519 *srcBytesPerPixel = 4;
520 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
521 *sc = SkScaledBitmapSampler::kRGB;
522 *srcBytesPerPixel = 3;
523#ifdef ANDROID_RGB
524 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
525 *sc = SkScaledBitmapSampler::kRGBX;
526 *srcBytesPerPixel = 4;
527 } else if (JCS_RGB_565 == cinfo.out_color_space) {
528 *sc = SkScaledBitmapSampler::kRGB_565;
529 *srcBytesPerPixel = 2;
530#endif
531 } else if (1 == cinfo.out_color_components &&
532 JCS_GRAYSCALE == cinfo.out_color_space) {
533 *sc = SkScaledBitmapSampler::kGray;
534 *srcBytesPerPixel = 1;
535 } else {
536 return false;
537 }
538 return true;
539}
halcanary@google.comfed30372013-10-04 12:46:45 +0000540
scroggo2a120802014-10-22 12:07:00 -0700541SkImageDecoder::Result SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000542#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000543 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000544#endif
545
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000546 JPEGAutoClean autoClean;
547
548 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000549 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000550
scroggo@google.com590a5af2013-08-07 21:09:13 +0000551 skjpeg_error_mgr errorManager;
552 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000553
554 // All objects need to be instantiated before this setjmp call so that
555 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000556 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700557 return return_failure(cinfo, *bm, "setjmp");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000558 }
559
scroggo@google.com590a5af2013-08-07 21:09:13 +0000560 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000561 autoClean.set(&cinfo);
562
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000563 int status = jpeg_read_header(&cinfo, true);
564 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700565 return return_failure(cinfo, *bm, "read_header");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000566 }
567
568 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
569 can) much faster that we, just use their num/denom api to approximate
570 the size.
571 */
572 int sampleSize = this->getSampleSize();
573
scroggo@google.com590a5af2013-08-07 21:09:13 +0000574 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000575
scroggo@google.com590a5af2013-08-07 21:09:13 +0000576 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000577 cinfo.scale_denom = sampleSize;
578
scroggo@google.com590a5af2013-08-07 21:09:13 +0000579 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000580
reed6c225732014-06-09 19:52:07 -0700581 const SkColorType colorType = this->getBitmapColorType(&cinfo);
582 const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ?
583 kPremul_SkAlphaType : kOpaque_SkAlphaType;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000584
reed6c225732014-06-09 19:52:07 -0700585 adjust_out_color_space_and_dither(&cinfo, colorType, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000586
djsollen@google.com11399402013-03-20 17:45:27 +0000587 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000588 // Assume an A8 bitmap is not opaque to avoid the check of each
589 // individual pixel. It is very unlikely to be opaque, since
590 // an opaque A8 bitmap would not be very interesting.
591 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700592 bool success = bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
593 colorType, alphaType));
594 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000595 }
596
597 /* image_width and image_height are the original dimensions, available
598 after jpeg_read_header(). To see the scaled dimensions, we have to call
599 jpeg_start_decompress(), and then read output_width and output_height.
600 */
601 if (!jpeg_start_decompress(&cinfo)) {
602 /* If we failed here, we may still have enough information to return
603 to the caller if they just wanted (subsampled bounds). If sampleSize
604 was 1, then we would have already returned. Thus we just check if
605 we're in kDecodeBounds_Mode, and that we have valid output sizes.
606
607 One reason to fail here is that we have insufficient stream data
608 to complete the setup. However, output dimensions seem to get
609 computed very early, which is why this special check can pay off.
610 */
djsollen@google.com11399402013-03-20 17:45:27 +0000611 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000612 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
613 recompute_sampleSize(sampleSize, cinfo));
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000614 // Assume an A8 bitmap is not opaque to avoid the check of each
615 // individual pixel. It is very unlikely to be opaque, since
616 // an opaque A8 bitmap would not be very interesting.
617 // Otherwise, a jpeg image is opaque.
scroggo2a120802014-10-22 12:07:00 -0700618 bool success = bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
619 colorType, alphaType));
620 return success ? kSuccess : kFailure;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000621 } else {
scroggo2a120802014-10-22 12:07:00 -0700622 return return_failure(cinfo, *bm, "start_decompress");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000623 }
624 }
625 sampleSize = recompute_sampleSize(sampleSize, cinfo);
626
djsollen@google.com11399402013-03-20 17:45:27 +0000627 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000628 // Assume an A8 bitmap is not opaque to avoid the check of each
629 // individual pixel. It is very unlikely to be opaque, since
630 // an opaque A8 bitmap would not be very interesting.
631 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700632 bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
633 colorType, alphaType));
scroggo@google.combc69ce92013-07-09 15:45:14 +0000634 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo2a120802014-10-22 12:07:00 -0700635 return kSuccess;
scroggo@google.combc69ce92013-07-09 15:45:14 +0000636 }
637 if (!this->allocPixelRef(bm, NULL)) {
scroggo2a120802014-10-22 12:07:00 -0700638 return return_failure(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000639 }
640
641 SkAutoLockPixels alp(*bm);
642
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000643#ifdef ANDROID_RGB
644 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
645 a significant performance boost.
646 */
647 if (sampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700648 ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||
649 (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000650 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000651 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000652 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000653
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000654 while (cinfo.output_scanline < cinfo.output_height) {
655 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000656 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000657 // if row_count == 0, then we didn't get a scanline,
658 // so return early. We will return a partial image.
659 fill_below_level(cinfo.output_scanline, bm);
660 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700661 jpeg_finish_decompress(&cinfo);
662 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000663 }
664 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700665 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000666 }
667 rowptr += bpr;
668 }
669 jpeg_finish_decompress(&cinfo);
scroggo2a120802014-10-22 12:07:00 -0700670 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000671 }
672#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000673
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000674 // check for supported formats
675 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000676 int srcBytesPerPixel;
677
678 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
scroggo2a120802014-10-22 12:07:00 -0700679 return return_failure(cinfo, *bm, "jpeg colorspace");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000680 }
681
scroggo@google.com8d239242013-10-01 17:27:15 +0000682 if (!sampler.begin(bm, sc, *this)) {
scroggo2a120802014-10-22 12:07:00 -0700683 return return_failure(cinfo, *bm, "sampler.begin");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000684 }
685
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000686 SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000687 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000688
689 // Possibly skip initial rows [sampler.srcY0]
690 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
scroggo2a120802014-10-22 12:07:00 -0700691 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000692 }
693
694 // now loop through scanlines until y == bm->height() - 1
695 for (int y = 0;; y++) {
696 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
697 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
698 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000699 // if row_count == 0, then we didn't get a scanline,
700 // so return early. We will return a partial image.
701 fill_below_level(y, bm);
702 cinfo.output_scanline = cinfo.output_height;
scroggo2a120802014-10-22 12:07:00 -0700703 jpeg_finish_decompress(&cinfo);
scroggoc6b8ffa2014-12-17 06:55:02 -0800704 return kPartialSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000705 }
706 if (this->shouldCancelDecode()) {
scroggo2a120802014-10-22 12:07:00 -0700707 return return_failure(cinfo, *bm, "shouldCancelDecode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000708 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000709
710 if (JCS_CMYK == cinfo.out_color_space) {
711 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
712 }
713
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000714 sampler.next(srcRow);
715 if (bm->height() - 1 == y) {
716 // we're done
717 break;
718 }
719
720 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
scroggo2a120802014-10-22 12:07:00 -0700721 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000722 }
723 }
724
725 // we formally skip the rest, so we don't get a complaint from libjpeg
726 if (!skip_src_rows(&cinfo, srcRow,
727 cinfo.output_height - cinfo.output_scanline)) {
scroggo2a120802014-10-22 12:07:00 -0700728 return return_failure(cinfo, *bm, "skip rows");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000729 }
730 jpeg_finish_decompress(&cinfo);
731
scroggo2a120802014-10-22 12:07:00 -0700732 return kSuccess;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000733}
734
scroggo2a120802014-10-22 12:07:00 -0700735///////////////////////////////////////////////////////////////////////////////
736
sugoib227e372014-10-16 13:10:57 -0700737enum SizeType {
738 kSizeForMemoryAllocation_SizeType,
739 kActualSize_SizeType
740};
741
742static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component,
743 SizeType sizeType) {
744 if (sizeType == kSizeForMemoryAllocation_SizeType) {
745 return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE,
746 info.cur_comp_info[component]->height_in_blocks * DCTSIZE);
747 }
748 return SkISize::Make(info.cur_comp_info[component]->downsampled_width,
749 info.cur_comp_info[component]->downsampled_height);
750}
751
mtkleinb3e5e4d2015-03-25 13:13:43 -0700752static bool appears_to_be_yuv(const jpeg_decompress_struct& info) {
753 return (info.jpeg_color_space == JCS_YCbCr)
754 && (DCTSIZE == 8)
755 && (info.num_components == 3)
756 && (info.comps_in_scan >= info.num_components)
757 && (info.scale_denom <= 8)
758 && (info.cur_comp_info[0])
759 && (info.cur_comp_info[1])
760 && (info.cur_comp_info[2])
761 && (info.cur_comp_info[1]->h_samp_factor == 1)
762 && (info.cur_comp_info[1]->v_samp_factor == 1)
763 && (info.cur_comp_info[2]->h_samp_factor == 1)
764 && (info.cur_comp_info[2]->v_samp_factor == 1);
765}
766
sugoib227e372014-10-16 13:10:57 -0700767static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
768 SizeType sizeType) {
mtkleinb3e5e4d2015-03-25 13:13:43 -0700769 SkASSERT(appears_to_be_yuv(cinfo));
sugoib227e372014-10-16 13:10:57 -0700770 for (int i = 0; i < 3; ++i) {
771 componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
772 }
773}
774
775static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) {
mtkleinb3e5e4d2015-03-25 13:13:43 -0700776 SkASSERT(appears_to_be_yuv(cinfo));
sugoib227e372014-10-16 13:10:57 -0700777 // U size and V size have to be the same if we're calling output_raw_data()
778 SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType);
779 SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType));
780
781 JSAMPARRAY bufferraw[3];
782 JSAMPROW bufferraw2[32];
783 bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16)
784 bufferraw[1] = &bufferraw2[16]; // U channel rows (8)
785 bufferraw[2] = &bufferraw2[24]; // V channel rows (8)
786 int yWidth = cinfo.output_width;
787 int yHeight = cinfo.output_height;
788 int yMaxH = yHeight - 1;
789 int v = cinfo.cur_comp_info[0]->v_samp_factor;
790 int uvMaxH = uvSize.height() - 1;
791 JSAMPROW outputY = static_cast<JSAMPROW>(planes[0]);
792 JSAMPROW outputU = static_cast<JSAMPROW>(planes[1]);
793 JSAMPROW outputV = static_cast<JSAMPROW>(planes[2]);
794 size_t rowBytesY = rowBytes[0];
795 size_t rowBytesU = rowBytes[1];
796 size_t rowBytesV = rowBytes[2];
797
798 int yScanlinesToRead = DCTSIZE * v;
sugoif421ec62015-02-19 05:32:08 -0800799 SkAutoMalloc lastRowStorage(rowBytesY * 4);
sugoib227e372014-10-16 13:10:57 -0700800 JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get();
sugoif421ec62015-02-19 05:32:08 -0800801 JSAMPROW uLastRow = yLastRow + rowBytesY;
802 JSAMPROW vLastRow = uLastRow + rowBytesY;
803 JSAMPROW dummyRow = vLastRow + rowBytesY;
sugoib227e372014-10-16 13:10:57 -0700804
805 while (cinfo.output_scanline < cinfo.output_height) {
806 // Request 8 or 16 scanlines: returns 0 or more scanlines.
807 bool hasYLastRow(false), hasUVLastRow(false);
808 // Assign 8 or 16 rows of memory to read the Y channel.
809 for (int i = 0; i < yScanlinesToRead; ++i) {
810 int scanline = (cinfo.output_scanline + i);
811 if (scanline < yMaxH) {
812 bufferraw2[i] = &outputY[scanline * rowBytesY];
813 } else if (scanline == yMaxH) {
814 bufferraw2[i] = yLastRow;
815 hasYLastRow = true;
816 } else {
817 bufferraw2[i] = dummyRow;
818 }
819 }
820 int scaledScanline = cinfo.output_scanline / v;
821 // Assign 8 rows of memory to read the U and V channels.
822 for (int i = 0; i < 8; ++i) {
823 int scanline = (scaledScanline + i);
824 if (scanline < uvMaxH) {
825 bufferraw2[16 + i] = &outputU[scanline * rowBytesU];
826 bufferraw2[24 + i] = &outputV[scanline * rowBytesV];
827 } else if (scanline == uvMaxH) {
828 bufferraw2[16 + i] = uLastRow;
829 bufferraw2[24 + i] = vLastRow;
830 hasUVLastRow = true;
831 } else {
832 bufferraw2[16 + i] = dummyRow;
833 bufferraw2[24 + i] = dummyRow;
834 }
835 }
836 JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead);
837
scroggo2a120802014-10-22 12:07:00 -0700838 if (scanlinesRead == 0) {
sugoib227e372014-10-16 13:10:57 -0700839 return false;
scroggo2a120802014-10-22 12:07:00 -0700840 }
sugoib227e372014-10-16 13:10:57 -0700841
842 if (hasYLastRow) {
843 memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth);
844 }
845 if (hasUVLastRow) {
846 memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width());
847 memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width());
848 }
849 }
850
851 cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height);
852
853 return true;
854}
855
856bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
857 void* planes[3], size_t rowBytes[3],
858 SkYUVColorSpace* colorSpace) {
859#ifdef TIME_DECODE
860 SkAutoTime atm("JPEG YUV8 Decode");
861#endif
sugoib227e372014-10-16 13:10:57 -0700862 if (this->getSampleSize() != 1) {
863 return false; // Resizing not supported
864 }
865
866 JPEGAutoClean autoClean;
867
868 jpeg_decompress_struct cinfo;
869 skjpeg_source_mgr srcManager(stream, this);
870
871 skjpeg_error_mgr errorManager;
872 set_error_mgr(&cinfo, &errorManager);
873
874 // All objects need to be instantiated before this setjmp call so that
875 // they will be cleaned up properly if an error occurs.
876 if (setjmp(errorManager.fJmpBuf)) {
scroggo2a120802014-10-22 12:07:00 -0700877 return return_false(cinfo, "setjmp YUV8");
sugoib227e372014-10-16 13:10:57 -0700878 }
879
880 initialize_info(&cinfo, &srcManager);
881 autoClean.set(&cinfo);
882
883 int status = jpeg_read_header(&cinfo, true);
884 if (status != JPEG_HEADER_OK) {
scroggo2a120802014-10-22 12:07:00 -0700885 return return_false(cinfo, "read_header YUV8");
sugoib227e372014-10-16 13:10:57 -0700886 }
887
mtkleinb3e5e4d2015-03-25 13:13:43 -0700888 if (!appears_to_be_yuv(cinfo)) {
sugoib227e372014-10-16 13:10:57 -0700889 // It's not an error to not be encoded in YUV, so no need to use return_false()
890 return false;
891 }
892
893 cinfo.out_color_space = JCS_YCbCr;
894 cinfo.raw_data_out = TRUE;
895
896 if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only
897 update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType);
898 return true;
899 }
900
901 set_dct_method(*this, &cinfo);
902
903 SkASSERT(1 == cinfo.scale_num);
904 cinfo.scale_denom = 1;
905
906 turn_off_visual_optimizations(&cinfo);
907
908#ifdef ANDROID_RGB
909 cinfo.dither_mode = JDITHER_NONE;
910#endif
911
912 /* image_width and image_height are the original dimensions, available
913 after jpeg_read_header(). To see the scaled dimensions, we have to call
914 jpeg_start_decompress(), and then read output_width and output_height.
915 */
916 if (!jpeg_start_decompress(&cinfo)) {
scroggo2a120802014-10-22 12:07:00 -0700917 return return_false(cinfo, "start_decompress YUV8");
sugoib227e372014-10-16 13:10:57 -0700918 }
919
mtkleinb3e5e4d2015-03-25 13:13:43 -0700920 // Seems like jpeg_start_decompress is updating our opinion of whether cinfo represents YUV.
921 // Again, not really an error.
922 if (!appears_to_be_yuv(cinfo)) {
923 return false;
924 }
925
sugoib227e372014-10-16 13:10:57 -0700926 if (!output_raw_data(cinfo, planes, rowBytes)) {
scroggo2a120802014-10-22 12:07:00 -0700927 return return_false(cinfo, "output_raw_data");
sugoib227e372014-10-16 13:10:57 -0700928 }
929
930 update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
931 jpeg_finish_decompress(&cinfo);
932
933 if (NULL != colorSpace) {
934 *colorSpace = kJPEG_SkYUVColorSpace;
935 }
936
937 return true;
938}
939
scroggo2a120802014-10-22 12:07:00 -0700940///////////////////////////////////////////////////////////////////////////////
941
scroggo@google.comd79277f2013-08-07 19:53:53 +0000942#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000943bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
halcanary385fe4d2015-08-26 13:07:48 -0700944 SkAutoTDelete<SkJPEGImageIndex> imageIndex(new SkJPEGImageIndex(stream, this));
djsollen@google.com11399402013-03-20 17:45:27 +0000945
946 skjpeg_error_mgr sk_err;
scroggo6d7e47a2015-01-22 10:27:25 -0800947 set_error_mgr(imageIndex->cinfo(), &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000948
949 // All objects need to be instantiated before this setjmp call so that
950 // they will be cleaned up properly if an error occurs.
951 if (setjmp(sk_err.fJmpBuf)) {
952 return false;
953 }
954
955 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000956 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000957 return false;
958 }
959
scroggo@google.coma1a51542013-08-07 21:02:32 +0000960 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000961 return false;
962 }
963
964 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000965 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000966
967 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000968 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000969 return false;
970 }
971
scroggo6d7e47a2015-01-22 10:27:25 -0800972 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
973 // We have a new cinfo, so set the error mgr again.
974 set_error_mgr(cinfo, &sk_err);
975
scroggo@google.com590a5af2013-08-07 21:09:13 +0000976 // FIXME: This sets cinfo->out_color_space, which we may change later
977 // based on the config in onDecodeSubset. This should be fine, since
978 // jpeg_init_read_tile_scanline will check out_color_space again after
979 // that change (when it calls jinit_color_deconverter).
reed6c225732014-06-09 19:52:07 -0700980 (void) this->getBitmapColorType(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000981
982 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000983
984 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000985 if (!imageIndex->startTileDecompress()) {
986 return false;
987 }
djsollen@google.com11399402013-03-20 17:45:27 +0000988
scroggo@google.coma1a51542013-08-07 21:02:32 +0000989 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000990 fImageWidth = cinfo->output_width;
991 fImageHeight = cinfo->output_height;
992
993 if (width) {
994 *width = fImageWidth;
995 }
996 if (height) {
997 *height = fImageHeight;
998 }
djsollen@google.com11399402013-03-20 17:45:27 +0000999
halcanary385fe4d2015-08-26 13:07:48 -07001000 delete fImageIndex;
scroggo@google.coma1a51542013-08-07 21:02:32 +00001001 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +00001002
1003 return true;
1004}
1005
scroggo@google.com7e6fcee2013-05-03 20:14:28 +00001006bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +00001007 if (NULL == fImageIndex) {
1008 return false;
1009 }
1010 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
1011
1012 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
1013 if (!rect.intersect(region)) {
1014 // If the requested region is entirely outside the image return false
1015 return false;
1016 }
1017
1018
1019 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +00001020 set_error_mgr(cinfo, &errorManager);
1021
djsollen@google.com11399402013-03-20 17:45:27 +00001022 if (setjmp(errorManager.fJmpBuf)) {
1023 return false;
1024 }
1025
1026 int requestedSampleSize = this->getSampleSize();
1027 cinfo->scale_denom = requestedSampleSize;
1028
scroggo@google.com590a5af2013-08-07 21:09:13 +00001029 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +00001030
reed6c225732014-06-09 19:52:07 -07001031 const SkColorType colorType = this->getBitmapColorType(cinfo);
1032 adjust_out_color_space_and_dither(cinfo, colorType, *this);
djsollen@google.com11399402013-03-20 17:45:27 +00001033
1034 int startX = rect.fLeft;
1035 int startY = rect.fTop;
1036 int width = rect.width();
1037 int height = rect.height();
1038
1039 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
1040 &startX, &startY, &width, &height);
1041 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
1042 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
1043
1044 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
1045
1046 SkBitmap bitmap;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001047 // Assume an A8 bitmap is not opaque to avoid the check of each
1048 // individual pixel. It is very unlikely to be opaque, since
1049 // an opaque A8 bitmap would not be very interesting.
1050 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -07001051 bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType,
1052 kAlpha_8_SkColorType == colorType ?
1053 kPremul_SkAlphaType : kOpaque_SkAlphaType));
djsollen@google.com11399402013-03-20 17:45:27 +00001054
1055 // Check ahead of time if the swap(dest, src) is possible or not.
1056 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
1057 // swap happening. If no, then we will use alloc to allocate pixels to
1058 // prevent garbage collection.
1059 int w = rect.width() / actualSampleSize;
1060 int h = rect.height() / actualSampleSize;
1061 bool swapOnly = (rect == region) && bm->isNull() &&
1062 (w == bitmap.width()) && (h == bitmap.height()) &&
1063 ((startX - rect.x()) / actualSampleSize == 0) &&
1064 ((startY - rect.y()) / actualSampleSize == 0);
1065 if (swapOnly) {
1066 if (!this->allocPixelRef(&bitmap, NULL)) {
1067 return return_false(*cinfo, bitmap, "allocPixelRef");
1068 }
1069 } else {
reed84825042014-09-02 12:50:45 -07001070 if (!bitmap.tryAllocPixels()) {
djsollen@google.com11399402013-03-20 17:45:27 +00001071 return return_false(*cinfo, bitmap, "allocPixels");
1072 }
1073 }
1074
1075 SkAutoLockPixels alp(bitmap);
1076
1077#ifdef ANDROID_RGB
1078 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
1079 a significant performance boost.
1080 */
1081 if (skiaSampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -07001082 ((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||
1083 (kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565)))
djsollen@google.com11399402013-03-20 17:45:27 +00001084 {
1085 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
1086 INT32 const bpr = bitmap.rowBytes();
1087 int rowTotalCount = 0;
1088
1089 while (rowTotalCount < height) {
1090 int rowCount = jpeg_read_tile_scanline(cinfo,
1091 fImageIndex->huffmanIndex(),
1092 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001093 // if rowCount == 0, then we didn't get a scanline, so abort.
1094 // onDecodeSubset() relies on onBuildTileIndex(), which
1095 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001096 if (0 == rowCount) {
1097 return return_false(*cinfo, bitmap, "read_scanlines");
1098 }
1099 if (this->shouldCancelDecode()) {
1100 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1101 }
1102 rowTotalCount += rowCount;
1103 rowptr += bpr;
1104 }
1105
1106 if (swapOnly) {
1107 bm->swap(bitmap);
zoran.jovanovic7f00acb2015-04-15 05:48:54 -07001108 return true;
djsollen@google.com11399402013-03-20 17:45:27 +00001109 }
zoran.jovanovic7f00acb2015-04-15 05:48:54 -07001110
1111 return cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1112 region.width(), region.height(), startX, startY);
djsollen@google.com11399402013-03-20 17:45:27 +00001113 }
1114#endif
1115
1116 // check for supported formats
1117 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001118 int srcBytesPerPixel;
1119
1120 if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001121 return return_false(*cinfo, *bm, "jpeg colorspace");
1122 }
1123
scroggo@google.com8d239242013-10-01 17:27:15 +00001124 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001125 return return_false(*cinfo, bitmap, "sampler.begin");
1126 }
1127
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001128 SkAutoMalloc srcStorage(width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +00001129 uint8_t* srcRow = (uint8_t*)srcStorage.get();
1130
1131 // Possibly skip initial rows [sampler.srcY0]
1132 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
1133 return return_false(*cinfo, bitmap, "skip rows");
1134 }
1135
1136 // now loop through scanlines until y == bitmap->height() - 1
1137 for (int y = 0;; y++) {
1138 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
1139 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001140 // if row_count == 0, then we didn't get a scanline, so abort.
1141 // onDecodeSubset() relies on onBuildTileIndex(), which
1142 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001143 if (0 == row_count) {
1144 return return_false(*cinfo, bitmap, "read_scanlines");
1145 }
1146 if (this->shouldCancelDecode()) {
1147 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1148 }
1149
1150 if (JCS_CMYK == cinfo->out_color_space) {
1151 convert_CMYK_to_RGB(srcRow, width);
1152 }
1153
1154 sampler.next(srcRow);
1155 if (bitmap.height() - 1 == y) {
1156 // we're done
1157 break;
1158 }
1159
1160 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
1161 sampler.srcDY() - 1)) {
1162 return return_false(*cinfo, bitmap, "skip rows");
1163 }
1164 }
1165 if (swapOnly) {
1166 bm->swap(bitmap);
zoran.jovanovic7f00acb2015-04-15 05:48:54 -07001167 return true;
djsollen@google.com11399402013-03-20 17:45:27 +00001168 }
zoran.jovanovic7f00acb2015-04-15 05:48:54 -07001169 return cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1170 region.width(), region.height(), startX, startY);
djsollen@google.com11399402013-03-20 17:45:27 +00001171}
1172#endif
1173
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001174///////////////////////////////////////////////////////////////////////////////
1175
1176#include "SkColorPriv.h"
1177
1178// taken from jcolor.c in libjpeg
1179#if 0 // 16bit - precise but slow
1180 #define CYR 19595 // 0.299
1181 #define CYG 38470 // 0.587
1182 #define CYB 7471 // 0.114
1183
1184 #define CUR -11059 // -0.16874
1185 #define CUG -21709 // -0.33126
1186 #define CUB 32768 // 0.5
1187
1188 #define CVR 32768 // 0.5
1189 #define CVG -27439 // -0.41869
1190 #define CVB -5329 // -0.08131
1191
1192 #define CSHIFT 16
1193#else // 8bit - fast, slightly less precise
1194 #define CYR 77 // 0.299
1195 #define CYG 150 // 0.587
1196 #define CYB 29 // 0.114
1197
1198 #define CUR -43 // -0.16874
1199 #define CUG -85 // -0.33126
1200 #define CUB 128 // 0.5
1201
1202 #define CVR 128 // 0.5
1203 #define CVG -107 // -0.41869
1204 #define CVB -21 // -0.08131
1205
1206 #define CSHIFT 8
1207#endif
1208
1209static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
1210 int r = SkGetPackedR32(c);
1211 int g = SkGetPackedG32(c);
1212 int b = SkGetPackedB32(c);
1213
1214 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
1215 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
1216 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
1217
1218 dst[0] = SkToU8(y);
1219 dst[1] = SkToU8(u + 128);
1220 dst[2] = SkToU8(v + 128);
1221}
1222
1223static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
1224 int r = SkGetPackedR4444(c);
1225 int g = SkGetPackedG4444(c);
1226 int b = SkGetPackedB4444(c);
1227
1228 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
1229 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
1230 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
1231
1232 dst[0] = SkToU8(y);
1233 dst[1] = SkToU8(u + 128);
1234 dst[2] = SkToU8(v + 128);
1235}
1236
1237static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
1238 int r = SkGetPackedR16(c);
1239 int g = SkGetPackedG16(c);
1240 int b = SkGetPackedB16(c);
1241
1242 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
1243 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
1244 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
1245
1246 dst[0] = SkToU8(y);
1247 dst[1] = SkToU8(u + 128);
1248 dst[2] = SkToU8(v + 128);
1249}
1250
1251///////////////////////////////////////////////////////////////////////////////
1252
1253typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1254 const void* SK_RESTRICT src, int width,
1255 const SkPMColor* SK_RESTRICT ctable);
1256
1257static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1258 const void* SK_RESTRICT srcRow, int width,
1259 const SkPMColor*) {
1260 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1261 while (--width >= 0) {
1262#ifdef WE_CONVERT_TO_YUV
1263 rgb2yuv_32(dst, *src++);
1264#else
1265 uint32_t c = *src++;
1266 dst[0] = SkGetPackedR32(c);
1267 dst[1] = SkGetPackedG32(c);
1268 dst[2] = SkGetPackedB32(c);
1269#endif
1270 dst += 3;
1271 }
1272}
1273
1274static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1275 const void* SK_RESTRICT srcRow, int width,
1276 const SkPMColor*) {
1277 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1278 while (--width >= 0) {
1279#ifdef WE_CONVERT_TO_YUV
1280 rgb2yuv_4444(dst, *src++);
1281#else
1282 SkPMColor16 c = *src++;
1283 dst[0] = SkPacked4444ToR32(c);
1284 dst[1] = SkPacked4444ToG32(c);
1285 dst[2] = SkPacked4444ToB32(c);
1286#endif
1287 dst += 3;
1288 }
1289}
1290
1291static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1292 const void* SK_RESTRICT srcRow, int width,
1293 const SkPMColor*) {
1294 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1295 while (--width >= 0) {
1296#ifdef WE_CONVERT_TO_YUV
1297 rgb2yuv_16(dst, *src++);
1298#else
1299 uint16_t c = *src++;
1300 dst[0] = SkPacked16ToR32(c);
1301 dst[1] = SkPacked16ToG32(c);
1302 dst[2] = SkPacked16ToB32(c);
1303#endif
1304 dst += 3;
1305 }
1306}
1307
1308static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1309 const void* SK_RESTRICT srcRow, int width,
1310 const SkPMColor* SK_RESTRICT ctable) {
1311 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1312 while (--width >= 0) {
1313#ifdef WE_CONVERT_TO_YUV
1314 rgb2yuv_32(dst, ctable[*src++]);
1315#else
1316 uint32_t c = ctable[*src++];
1317 dst[0] = SkGetPackedR32(c);
1318 dst[1] = SkGetPackedG32(c);
1319 dst[2] = SkGetPackedB32(c);
1320#endif
1321 dst += 3;
1322 }
1323}
1324
1325static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -07001326 switch (bm.colorType()) {
1327 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001328 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -07001329 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001330 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -07001331 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001332 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -07001333 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001334 return Write_Index_YUV;
1335 default:
1336 return NULL;
1337 }
1338}
1339
1340class SkJPEGImageEncoder : public SkImageEncoder {
1341protected:
1342 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1343#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001344 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001345#endif
1346
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001347 SkAutoLockPixels alp(bm);
1348 if (NULL == bm.getPixels()) {
1349 return false;
1350 }
1351
1352 jpeg_compress_struct cinfo;
1353 skjpeg_error_mgr sk_err;
1354 skjpeg_destination_mgr sk_wstream(stream);
1355
1356 // allocate these before set call setjmp
1357 SkAutoMalloc oneRow;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001358
1359 cinfo.err = jpeg_std_error(&sk_err);
1360 sk_err.error_exit = skjpeg_error_exit;
1361 if (setjmp(sk_err.fJmpBuf)) {
1362 return false;
1363 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001364
mtklein@google.com8d725b22013-07-24 16:20:05 +00001365 // Keep after setjmp or mark volatile.
1366 const WriteScanline writer = ChooseWriter(bm);
1367 if (NULL == writer) {
1368 return false;
1369 }
1370
1371 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001372 cinfo.dest = &sk_wstream;
1373 cinfo.image_width = bm.width();
1374 cinfo.image_height = bm.height();
1375 cinfo.input_components = 3;
1376#ifdef WE_CONVERT_TO_YUV
1377 cinfo.in_color_space = JCS_YCbCr;
1378#else
1379 cinfo.in_color_space = JCS_RGB;
1380#endif
1381 cinfo.input_gamma = 1;
1382
1383 jpeg_set_defaults(&cinfo);
1384 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001385#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001386 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001387#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001388
1389 jpeg_start_compress(&cinfo, TRUE);
1390
1391 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001392 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001393
mtklein775b8192014-12-02 09:11:25 -08001394 const SkPMColor* colors = bm.getColorTable() ? bm.getColorTable()->readColors() : NULL;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001395 const void* srcRow = bm.getPixels();
1396
1397 while (cinfo.next_scanline < cinfo.image_height) {
1398 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1399
1400 writer(oneRowP, srcRow, width, colors);
1401 row_pointer[0] = oneRowP;
1402 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1403 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1404 }
1405
1406 jpeg_finish_compress(&cinfo);
1407 jpeg_destroy_compress(&cinfo);
1408
1409 return true;
1410 }
1411};
1412
1413///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001414DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1415DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1416///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001417
scroggo@google.comb5571b32013-09-25 21:34:24 +00001418static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001419 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001420 static const size_t HEADER_SIZE = sizeof(gHeader);
1421
1422 char buffer[HEADER_SIZE];
1423 size_t len = stream->read(buffer, HEADER_SIZE);
1424
1425 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001426 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001427 }
1428 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001429 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001430 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001431 return true;
1432}
1433
scroggo@google.comb5571b32013-09-25 21:34:24 +00001434
1435static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001436 if (is_jpeg(stream)) {
halcanary385fe4d2015-08-26 13:07:48 -07001437 return new SkJPEGImageDecoder;
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001438 }
1439 return NULL;
1440}
1441
scroggo@google.comb5571b32013-09-25 21:34:24 +00001442static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001443 if (is_jpeg(stream)) {
1444 return SkImageDecoder::kJPEG_Format;
1445 }
1446 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001447}
1448
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001449static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
halcanary385fe4d2015-08-26 13:07:48 -07001450 return (SkImageEncoder::kJPEG_Type == t) ? new SkJPEGImageEncoder : NULL;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001451}
1452
mtklein@google.combd6343b2013-09-04 17:20:18 +00001453static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1454static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1455static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);