blob: 4103eefede3b0ffc5dd8586586d73f4537ce348d [file] [log] [blame]
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001/*
2 * Copyright 2007 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8
9#include "SkImageDecoder.h"
10#include "SkImageEncoder.h"
11#include "SkJpegUtility.h"
12#include "SkColorPriv.h"
13#include "SkDither.h"
14#include "SkScaledBitmapSampler.h"
15#include "SkStream.h"
16#include "SkTemplates.h"
djsollen@google.com11399402013-03-20 17:45:27 +000017#include "SkTime.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000018#include "SkUtils.h"
halcanary@google.com2a103182013-10-14 12:49:15 +000019#include "SkRTConf.h"
djsollen@google.com11399402013-03-20 17:45:27 +000020#include "SkRect.h"
21#include "SkCanvas.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000022
halcanary@google.comfed30372013-10-04 12:46:45 +000023
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000024#include <stdio.h>
25extern "C" {
26 #include "jpeglib.h"
27 #include "jerror.h"
28}
29
djsollen@google.com11399402013-03-20 17:45:27 +000030// These enable timing code that report milliseconds for an encoding/decoding
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000031//#define TIME_ENCODE
32//#define TIME_DECODE
33
34// this enables our rgb->yuv code, which is faster than libjpeg on ARM
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000035#define WE_CONVERT_TO_YUV
36
djsollen@google.com11399402013-03-20 17:45:27 +000037// If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers
38// support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565.
39
halcanary@google.comfed30372013-10-04 12:46:45 +000040#if defined(SK_DEBUG)
halcanary@google.com2a103182013-10-14 12:49:15 +000041#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS false
halcanary@google.com04b57f82013-10-14 20:08:48 +000042#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS false
halcanary@google.com2a103182013-10-14 12:49:15 +000043#else // !defined(SK_DEBUG)
44#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS true
halcanary@google.com04b57f82013-10-14 20:08:48 +000045#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS true
halcanary@google.comfed30372013-10-04 12:46:45 +000046#endif // defined(SK_DEBUG)
halcanary@google.com2a103182013-10-14 12:49:15 +000047SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderWarnings,
48 "images.jpeg.suppressDecoderWarnings",
49 DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS,
50 "Suppress most JPG warnings when calling decode functions.");
halcanary@google.com04b57f82013-10-14 20:08:48 +000051SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderErrors,
52 "images.jpeg.suppressDecoderErrors",
53 DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS,
54 "Suppress most JPG error messages when decode "
55 "function fails.");
halcanary@google.comfed30372013-10-04 12:46:45 +000056
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000057//////////////////////////////////////////////////////////////////////////
58//////////////////////////////////////////////////////////////////////////
59
scroggo@google.com590a5af2013-08-07 21:09:13 +000060static void overwrite_mem_buffer_size(jpeg_decompress_struct* cinfo) {
djsollen@google.com11399402013-03-20 17:45:27 +000061#ifdef SK_BUILD_FOR_ANDROID
62 /* Check if the device indicates that it has a large amount of system memory
63 * if so, increase the memory allocation to 30MB instead of the default 5MB.
64 */
65#ifdef ANDROID_LARGE_MEMORY_DEVICE
66 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
67#else
68 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
69#endif
70#endif // SK_BUILD_FOR_ANDROID
71}
72
73//////////////////////////////////////////////////////////////////////////
74//////////////////////////////////////////////////////////////////////////
75
halcanary@google.comfed30372013-10-04 12:46:45 +000076static void do_nothing_emit_message(jpeg_common_struct*, int) {
77 /* do nothing */
78}
halcanary@google.com04b57f82013-10-14 20:08:48 +000079static void do_nothing_output_message(j_common_ptr) {
80 /* do nothing */
81}
halcanary@google.comfed30372013-10-04 12:46:45 +000082
scroggo@google.com590a5af2013-08-07 21:09:13 +000083static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* src_mgr) {
84 SkASSERT(cinfo != NULL);
85 SkASSERT(src_mgr != NULL);
86 jpeg_create_decompress(cinfo);
87 overwrite_mem_buffer_size(cinfo);
88 cinfo->src = src_mgr;
halcanary@google.comfed30372013-10-04 12:46:45 +000089 /* To suppress warnings with a SK_DEBUG binary, set the
90 * environment variable "skia_images_jpeg_suppressDecoderWarnings"
91 * to "true". Inside a program that links to skia:
92 * SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); */
93 if (c_suppressJPEGImageDecoderWarnings) {
94 cinfo->err->emit_message = &do_nothing_emit_message;
95 }
halcanary@google.com04b57f82013-10-14 20:08:48 +000096 /* To suppress error messages with a SK_DEBUG binary, set the
97 * environment variable "skia_images_jpeg_suppressDecoderErrors"
98 * to "true". Inside a program that links to skia:
99 * SK_CONF_SET("images.jpeg.suppressDecoderErrors", true); */
100 if (c_suppressJPEGImageDecoderErrors) {
101 cinfo->err->output_message = &do_nothing_output_message;
102 }
scroggo@google.com590a5af2013-08-07 21:09:13 +0000103}
104
scroggo@google.coma1a51542013-08-07 21:02:32 +0000105#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000106class SkJPEGImageIndex {
107public:
scroggo@google.comb5571b32013-09-25 21:34:24 +0000108 SkJPEGImageIndex(SkStreamRewindable* stream, SkImageDecoder* decoder)
scroggo@google.coma1a51542013-08-07 21:02:32 +0000109 : fSrcMgr(stream, decoder)
110 , fInfoInitialized(false)
111 , fHuffmanCreated(false)
112 , fDecompressStarted(false)
113 {
114 SkDEBUGCODE(fReadHeaderSucceeded = false;)
115 }
djsollen@google.com11399402013-03-20 17:45:27 +0000116
117 ~SkJPEGImageIndex() {
scroggo@google.coma1a51542013-08-07 21:02:32 +0000118 if (fHuffmanCreated) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000119 // Set to false before calling the libjpeg function, in case
120 // the libjpeg function calls longjmp. Our setjmp handler may
121 // attempt to delete this SkJPEGImageIndex, thus entering this
122 // destructor again. Setting fHuffmanCreated to false first
123 // prevents an infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000124 fHuffmanCreated = false;
125 jpeg_destroy_huffman_index(&fHuffmanIndex);
126 }
127 if (fDecompressStarted) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000128 // Like fHuffmanCreated, set to false before calling libjpeg
129 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000130 fDecompressStarted = false;
131 jpeg_finish_decompress(&fCInfo);
132 }
133 if (fInfoInitialized) {
134 this->destroyInfo();
135 }
djsollen@google.com11399402013-03-20 17:45:27 +0000136 }
137
138 /**
scroggo@google.coma1a51542013-08-07 21:02:32 +0000139 * Destroy the cinfo struct.
140 * After this call, if a huffman index was already built, it
141 * can be used after calling initializeInfoAndReadHeader
142 * again. Must not be called after startTileDecompress except
143 * in the destructor.
djsollen@google.com11399402013-03-20 17:45:27 +0000144 */
scroggo@google.coma1a51542013-08-07 21:02:32 +0000145 void destroyInfo() {
146 SkASSERT(fInfoInitialized);
147 SkASSERT(!fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000148 // Like fHuffmanCreated, set to false before calling libjpeg
149 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000150 fInfoInitialized = false;
151 jpeg_destroy_decompress(&fCInfo);
152 SkDEBUGCODE(fReadHeaderSucceeded = false;)
153 }
154
155 /**
156 * Initialize the cinfo struct.
157 * Calls jpeg_create_decompress, makes customizations, and
158 * finally calls jpeg_read_header. Returns true if jpeg_read_header
159 * returns JPEG_HEADER_OK.
160 * If cinfo was already initialized, destroyInfo must be called to
161 * destroy the old one. Must not be called after startTileDecompress.
162 */
163 bool initializeInfoAndReadHeader() {
164 SkASSERT(!fInfoInitialized && !fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000165 initialize_info(&fCInfo, &fSrcMgr);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000166 fInfoInitialized = true;
167 const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true));
168 SkDEBUGCODE(fReadHeaderSucceeded = success;)
169 return success;
djsollen@google.com11399402013-03-20 17:45:27 +0000170 }
171
172 jpeg_decompress_struct* cinfo() { return &fCInfo; }
173
djsollen@google.com11399402013-03-20 17:45:27 +0000174 huffman_index* huffmanIndex() { return &fHuffmanIndex; }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000175
176 /**
177 * Build the index to be used for tile based decoding.
178 * Must only be called after a successful call to
179 * initializeInfoAndReadHeader and must not be called more
180 * than once.
181 */
182 bool buildHuffmanIndex() {
183 SkASSERT(fReadHeaderSucceeded);
184 SkASSERT(!fHuffmanCreated);
185 jpeg_create_huffman_index(&fCInfo, &fHuffmanIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000186 SkASSERT(1 == fCInfo.scale_num && 1 == fCInfo.scale_denom);
scroggo@google.com57a52982013-08-27 20:42:22 +0000187 fHuffmanCreated = jpeg_build_huffman_index(&fCInfo, &fHuffmanIndex);
188 return fHuffmanCreated;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000189 }
190
191 /**
192 * Start tile based decoding. Must only be called after a
193 * successful call to buildHuffmanIndex, and must only be
194 * called once.
195 */
196 bool startTileDecompress() {
197 SkASSERT(fHuffmanCreated);
198 SkASSERT(fReadHeaderSucceeded);
199 SkASSERT(!fDecompressStarted);
200 if (jpeg_start_tile_decompress(&fCInfo)) {
201 fDecompressStarted = true;
202 return true;
203 }
204 return false;
205 }
djsollen@google.com11399402013-03-20 17:45:27 +0000206
207private:
208 skjpeg_source_mgr fSrcMgr;
209 jpeg_decompress_struct fCInfo;
djsollen@google.com11399402013-03-20 17:45:27 +0000210 huffman_index fHuffmanIndex;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000211 bool fInfoInitialized;
212 bool fHuffmanCreated;
213 bool fDecompressStarted;
214 SkDEBUGCODE(bool fReadHeaderSucceeded;)
djsollen@google.com11399402013-03-20 17:45:27 +0000215};
scroggo@google.coma1a51542013-08-07 21:02:32 +0000216#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000217
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000218class SkJPEGImageDecoder : public SkImageDecoder {
219public:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000220#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000221 SkJPEGImageDecoder() {
222 fImageIndex = NULL;
223 fImageWidth = 0;
224 fImageHeight = 0;
225 }
226
227 virtual ~SkJPEGImageDecoder() {
228 SkDELETE(fImageIndex);
229 }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000230#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000231
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000232 virtual Format getFormat() const {
233 return kJPEG_Format;
234 }
235
236protected:
scroggo@google.comd79277f2013-08-07 19:53:53 +0000237#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000238 virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000239 virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
djsollen@google.com11399402013-03-20 17:45:27 +0000240#endif
241 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
sugoib227e372014-10-16 13:10:57 -0700242 virtual bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
243 void* planes[3], size_t rowBytes[3],
244 SkYUVColorSpace* colorSpace) SK_OVERRIDE;
djsollen@google.com11399402013-03-20 17:45:27 +0000245
246private:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000247#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000248 SkJPEGImageIndex* fImageIndex;
249 int fImageWidth;
250 int fImageHeight;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000251#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000252
scroggo@google.com590a5af2013-08-07 21:09:13 +0000253 /**
reed6c225732014-06-09 19:52:07 -0700254 * Determine the appropriate bitmap colortype and out_color_space based on
scroggo@google.com590a5af2013-08-07 21:09:13 +0000255 * both the preference of the caller and the jpeg_color_space on the
256 * jpeg_decompress_struct passed in.
257 * Must be called after jpeg_read_header.
258 */
reed6c225732014-06-09 19:52:07 -0700259 SkColorType getBitmapColorType(jpeg_decompress_struct*);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000260
djsollen@google.com11399402013-03-20 17:45:27 +0000261 typedef SkImageDecoder INHERITED;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000262};
263
264//////////////////////////////////////////////////////////////////////////
265
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000266/* Automatically clean up after throwing an exception */
267class JPEGAutoClean {
268public:
269 JPEGAutoClean(): cinfo_ptr(NULL) {}
270 ~JPEGAutoClean() {
271 if (cinfo_ptr) {
272 jpeg_destroy_decompress(cinfo_ptr);
273 }
274 }
275 void set(jpeg_decompress_struct* info) {
276 cinfo_ptr = info;
277 }
278private:
279 jpeg_decompress_struct* cinfo_ptr;
280};
281
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000282///////////////////////////////////////////////////////////////////////////////
283
284/* If we need to better match the request, we might examine the image and
285 output dimensions, and determine if the downsampling jpeg provided is
286 not sufficient. If so, we can recompute a modified sampleSize value to
287 make up the difference.
288
289 To skip this additional scaling, just set sampleSize = 1; below.
290 */
291static int recompute_sampleSize(int sampleSize,
292 const jpeg_decompress_struct& cinfo) {
293 return sampleSize * cinfo.output_width / cinfo.image_width;
294}
295
296static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
297 /* These are initialized to 0, so if they have non-zero values, we assume
298 they are "valid" (i.e. have been computed by libjpeg)
299 */
djsollen@google.com11399402013-03-20 17:45:27 +0000300 return 0 != cinfo.output_width && 0 != cinfo.output_height;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000301}
302
djsollen@google.com11399402013-03-20 17:45:27 +0000303static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000304 for (int i = 0; i < count; i++) {
305 JSAMPLE* rowptr = (JSAMPLE*)buffer;
306 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
djsollen@google.com11399402013-03-20 17:45:27 +0000307 if (1 != row_count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000308 return false;
309 }
310 }
311 return true;
312}
313
scroggo@google.comd79277f2013-08-07 19:53:53 +0000314#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000315static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
316 huffman_index *index, void* buffer, int count) {
317 for (int i = 0; i < count; i++) {
318 JSAMPLE* rowptr = (JSAMPLE*)buffer;
319 int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
320 if (1 != row_count) {
321 return false;
322 }
323 }
324 return true;
325}
326#endif
327
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000328// This guy exists just to aid in debugging, as it allows debuggers to just
329// set a break-point in one place to see all error exists.
330static bool return_false(const jpeg_decompress_struct& cinfo,
sugoib227e372014-10-16 13:10:57 -0700331 int width, int height, const char caller[]) {
halcanary@google.com04b57f82013-10-14 20:08:48 +0000332 if (!(c_suppressJPEGImageDecoderErrors)) {
333 char buffer[JMSG_LENGTH_MAX];
334 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
335 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
sugoib227e372014-10-16 13:10:57 -0700336 cinfo.err->msg_code, buffer, caller, width, height);
halcanary@google.com04b57f82013-10-14 20:08:48 +0000337 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000338 return false; // must always return false
339}
340
sugoib227e372014-10-16 13:10:57 -0700341static bool return_false(const jpeg_decompress_struct& cinfo,
342 const SkBitmap& bm, const char caller[]) {
343 return return_false(cinfo, bm.width(), bm.height(), caller);
344}
345
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000346// Convert a scanline of CMYK samples to RGBX in place. Note that this
347// method moves the "scanline" pointer in its processing
348static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
349 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000350 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000351 // from easyrgb.com):
352 // CMYK -> CMY
353 // C = ( C * (1 - K) + K ) // for each CMY component
354 // CMY -> RGB
355 // R = ( 1 - C ) * 255 // for each RGB component
356 // Unfortunately we are seeing inverted CMYK so all the original terms
357 // are 1-. This yields:
358 // CMYK -> CMY
359 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
360 // The conversion from CMY->RGB remains the same
361 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
362 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
363 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
364 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
365 scanline[3] = 255;
366 }
367}
368
scroggo@google.com590a5af2013-08-07 21:09:13 +0000369/**
370 * Common code for setting the error manager.
371 */
372static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
373 SkASSERT(cinfo != NULL);
374 SkASSERT(errorManager != NULL);
375 cinfo->err = jpeg_std_error(errorManager);
376 errorManager->error_exit = skjpeg_error_exit;
377}
378
379/**
380 * Common code for turning off upsampling and smoothing. Turning these
381 * off helps performance without showing noticable differences in the
382 * resulting bitmap.
383 */
384static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
385 SkASSERT(cinfo != NULL);
386 /* this gives about 30% performance improvement. In theory it may
387 reduce the visual quality, in practice I'm not seeing a difference
388 */
389 cinfo->do_fancy_upsampling = 0;
390
391 /* this gives another few percents */
392 cinfo->do_block_smoothing = 0;
393}
394
395/**
396 * Common code for setting the dct method.
397 */
398static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
399 SkASSERT(cinfo != NULL);
400#ifdef DCT_IFAST_SUPPORTED
401 if (decoder.getPreferQualityOverSpeed()) {
402 cinfo->dct_method = JDCT_ISLOW;
403 } else {
404 cinfo->dct_method = JDCT_IFAST;
405 }
406#else
407 cinfo->dct_method = JDCT_ISLOW;
408#endif
409}
410
reed6c225732014-06-09 19:52:07 -0700411SkColorType SkJPEGImageDecoder::getBitmapColorType(jpeg_decompress_struct* cinfo) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000412 SkASSERT(cinfo != NULL);
413
414 SrcDepth srcDepth = k32Bit_SrcDepth;
415 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
416 srcDepth = k8BitGray_SrcDepth;
417 }
418
reed6c225732014-06-09 19:52:07 -0700419 SkColorType colorType = this->getPrefColorType(srcDepth, /*hasAlpha*/ false);
420 switch (colorType) {
421 case kAlpha_8_SkColorType:
422 // Only respect A8 colortype if the original is grayscale,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000423 // in which case we will treat the grayscale as alpha
424 // values.
425 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
reed6c225732014-06-09 19:52:07 -0700426 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000427 }
428 break;
reed6c225732014-06-09 19:52:07 -0700429 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000430 // Fall through.
reed6c225732014-06-09 19:52:07 -0700431 case kARGB_4444_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000432 // Fall through.
reed6c225732014-06-09 19:52:07 -0700433 case kRGB_565_SkColorType:
434 // These are acceptable destination colortypes.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000435 break;
436 default:
reed6c225732014-06-09 19:52:07 -0700437 // Force all other colortypes to 8888.
438 colorType = kN32_SkColorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000439 break;
440 }
441
442 switch (cinfo->jpeg_color_space) {
443 case JCS_CMYK:
444 // Fall through.
445 case JCS_YCCK:
446 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
447 // so libjpeg will give us CMYK samples back and we will later
448 // manually convert them to RGB
449 cinfo->out_color_space = JCS_CMYK;
450 break;
451 case JCS_GRAYSCALE:
reed6c225732014-06-09 19:52:07 -0700452 if (kAlpha_8_SkColorType == colorType) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000453 cinfo->out_color_space = JCS_GRAYSCALE;
454 break;
455 }
456 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
reed6c225732014-06-09 19:52:07 -0700457 // colortype. Fall through to set to the default.
scroggo@google.com590a5af2013-08-07 21:09:13 +0000458 default:
459 cinfo->out_color_space = JCS_RGB;
460 break;
461 }
reed6c225732014-06-09 19:52:07 -0700462 return colorType;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000463}
464
scroggo@google.com590a5af2013-08-07 21:09:13 +0000465/**
reed6c225732014-06-09 19:52:07 -0700466 * Based on the colortype and dither mode, adjust out_color_space and
467 * dither_mode of cinfo. Only does work in ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000468 */
469static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
reed6c225732014-06-09 19:52:07 -0700470 SkColorType colorType,
scroggo@google.com590a5af2013-08-07 21:09:13 +0000471 const SkImageDecoder& decoder) {
472 SkASSERT(cinfo != NULL);
reed6c225732014-06-09 19:52:07 -0700473#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000474 cinfo->dither_mode = JDITHER_NONE;
475 if (JCS_CMYK == cinfo->out_color_space) {
476 return;
477 }
reed6c225732014-06-09 19:52:07 -0700478 switch (colorType) {
479 case kN32_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000480 cinfo->out_color_space = JCS_RGBA_8888;
481 break;
reed6c225732014-06-09 19:52:07 -0700482 case kRGB_565_SkColorType:
scroggo@google.com590a5af2013-08-07 21:09:13 +0000483 cinfo->out_color_space = JCS_RGB_565;
484 if (decoder.getDitherImage()) {
485 cinfo->dither_mode = JDITHER_ORDERED;
486 }
487 break;
488 default:
489 break;
490 }
scroggo@google.com590a5af2013-08-07 21:09:13 +0000491#endif
reed6c225732014-06-09 19:52:07 -0700492}
scroggo@google.com590a5af2013-08-07 21:09:13 +0000493
halcanary@google.comfed30372013-10-04 12:46:45 +0000494
495/**
496 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
497 Used when decoding fails partway through reading scanlines to fill
498 remaining lines. */
499static void fill_below_level(int y, SkBitmap* bitmap) {
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000500 SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
halcanary@google.comfed30372013-10-04 12:46:45 +0000501 SkCanvas canvas(*bitmap);
halcanary@google.com2dcf36e2013-10-04 14:35:38 +0000502 canvas.clipRect(SkRect::Make(rect));
halcanary@google.comfed30372013-10-04 12:46:45 +0000503 canvas.drawColor(SK_ColorWHITE);
504}
505
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000506/**
507 * Get the config and bytes per pixel of the source data. Return
508 * whether the data is supported.
509 */
510static bool get_src_config(const jpeg_decompress_struct& cinfo,
511 SkScaledBitmapSampler::SrcConfig* sc,
512 int* srcBytesPerPixel) {
513 SkASSERT(sc != NULL && srcBytesPerPixel != NULL);
514 if (JCS_CMYK == cinfo.out_color_space) {
515 // In this case we will manually convert the CMYK values to RGB
516 *sc = SkScaledBitmapSampler::kRGBX;
517 // The CMYK work-around relies on 4 components per pixel here
518 *srcBytesPerPixel = 4;
519 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
520 *sc = SkScaledBitmapSampler::kRGB;
521 *srcBytesPerPixel = 3;
522#ifdef ANDROID_RGB
523 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
524 *sc = SkScaledBitmapSampler::kRGBX;
525 *srcBytesPerPixel = 4;
526 } else if (JCS_RGB_565 == cinfo.out_color_space) {
527 *sc = SkScaledBitmapSampler::kRGB_565;
528 *srcBytesPerPixel = 2;
529#endif
530 } else if (1 == cinfo.out_color_components &&
531 JCS_GRAYSCALE == cinfo.out_color_space) {
532 *sc = SkScaledBitmapSampler::kGray;
533 *srcBytesPerPixel = 1;
534 } else {
535 return false;
536 }
537 return true;
538}
halcanary@google.comfed30372013-10-04 12:46:45 +0000539
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000540bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
541#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000542 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000543#endif
544
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000545 JPEGAutoClean autoClean;
546
547 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000548 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000549
scroggo@google.com590a5af2013-08-07 21:09:13 +0000550 skjpeg_error_mgr errorManager;
551 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000552
553 // All objects need to be instantiated before this setjmp call so that
554 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000555 if (setjmp(errorManager.fJmpBuf)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000556 return return_false(cinfo, *bm, "setjmp");
557 }
558
scroggo@google.com590a5af2013-08-07 21:09:13 +0000559 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000560 autoClean.set(&cinfo);
561
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000562 int status = jpeg_read_header(&cinfo, true);
563 if (status != JPEG_HEADER_OK) {
564 return return_false(cinfo, *bm, "read_header");
565 }
566
567 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
568 can) much faster that we, just use their num/denom api to approximate
569 the size.
570 */
571 int sampleSize = this->getSampleSize();
572
scroggo@google.com590a5af2013-08-07 21:09:13 +0000573 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000574
scroggo@google.com590a5af2013-08-07 21:09:13 +0000575 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000576 cinfo.scale_denom = sampleSize;
577
scroggo@google.com590a5af2013-08-07 21:09:13 +0000578 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000579
reed6c225732014-06-09 19:52:07 -0700580 const SkColorType colorType = this->getBitmapColorType(&cinfo);
581 const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ?
582 kPremul_SkAlphaType : kOpaque_SkAlphaType;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000583
reed6c225732014-06-09 19:52:07 -0700584 adjust_out_color_space_and_dither(&cinfo, colorType, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000585
djsollen@google.com11399402013-03-20 17:45:27 +0000586 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000587 // Assume an A8 bitmap is not opaque to avoid the check of each
588 // individual pixel. It is very unlikely to be opaque, since
589 // an opaque A8 bitmap would not be very interesting.
590 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700591 return bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
592 colorType, alphaType));
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000593 }
594
595 /* image_width and image_height are the original dimensions, available
596 after jpeg_read_header(). To see the scaled dimensions, we have to call
597 jpeg_start_decompress(), and then read output_width and output_height.
598 */
599 if (!jpeg_start_decompress(&cinfo)) {
600 /* If we failed here, we may still have enough information to return
601 to the caller if they just wanted (subsampled bounds). If sampleSize
602 was 1, then we would have already returned. Thus we just check if
603 we're in kDecodeBounds_Mode, and that we have valid output sizes.
604
605 One reason to fail here is that we have insufficient stream data
606 to complete the setup. However, output dimensions seem to get
607 computed very early, which is why this special check can pay off.
608 */
djsollen@google.com11399402013-03-20 17:45:27 +0000609 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000610 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
611 recompute_sampleSize(sampleSize, cinfo));
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000612 // Assume an A8 bitmap is not opaque to avoid the check of each
613 // individual pixel. It is very unlikely to be opaque, since
614 // an opaque A8 bitmap would not be very interesting.
615 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700616 return bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
617 colorType, alphaType));
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000618 } else {
619 return return_false(cinfo, *bm, "start_decompress");
620 }
621 }
622 sampleSize = recompute_sampleSize(sampleSize, cinfo);
623
reed5926b862014-06-11 10:33:13 -0700624#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed6c225732014-06-09 19:52:07 -0700625 // should we allow the Chooser (if present) to pick a colortype for us???
626 if (!this->chooseFromOneChoice(colorType, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000627 return return_false(cinfo, *bm, "chooseFromOneChoice");
628 }
reed5926b862014-06-11 10:33:13 -0700629#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000630
djsollen@google.com11399402013-03-20 17:45:27 +0000631 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000632 // Assume an A8 bitmap is not opaque to avoid the check of each
633 // individual pixel. It is very unlikely to be opaque, since
634 // an opaque A8 bitmap would not be very interesting.
635 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -0700636 bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
637 colorType, alphaType));
scroggo@google.combc69ce92013-07-09 15:45:14 +0000638 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
639 return true;
640 }
641 if (!this->allocPixelRef(bm, NULL)) {
642 return return_false(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000643 }
644
645 SkAutoLockPixels alp(*bm);
646
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000647#ifdef ANDROID_RGB
648 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
649 a significant performance boost.
650 */
651 if (sampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -0700652 ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||
653 (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000654 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000655 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000656 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000657
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000658 while (cinfo.output_scanline < cinfo.output_height) {
659 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000660 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000661 // if row_count == 0, then we didn't get a scanline,
662 // so return early. We will return a partial image.
663 fill_below_level(cinfo.output_scanline, bm);
664 cinfo.output_scanline = cinfo.output_height;
665 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000666 }
667 if (this->shouldCancelDecode()) {
668 return return_false(cinfo, *bm, "shouldCancelDecode");
669 }
670 rowptr += bpr;
671 }
672 jpeg_finish_decompress(&cinfo);
673 return true;
674 }
675#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000676
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000677 // check for supported formats
678 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000679 int srcBytesPerPixel;
680
681 if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000682 return return_false(cinfo, *bm, "jpeg colorspace");
683 }
684
scroggo@google.com8d239242013-10-01 17:27:15 +0000685 if (!sampler.begin(bm, sc, *this)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000686 return return_false(cinfo, *bm, "sampler.begin");
687 }
688
scroggo@google.com5ee18dd2013-10-21 20:47:31 +0000689 SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +0000690 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000691
692 // Possibly skip initial rows [sampler.srcY0]
693 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
694 return return_false(cinfo, *bm, "skip rows");
695 }
696
697 // now loop through scanlines until y == bm->height() - 1
698 for (int y = 0;; y++) {
699 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
700 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
701 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000702 // if row_count == 0, then we didn't get a scanline,
703 // so return early. We will return a partial image.
704 fill_below_level(y, bm);
705 cinfo.output_scanline = cinfo.output_height;
706 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000707 }
708 if (this->shouldCancelDecode()) {
709 return return_false(cinfo, *bm, "shouldCancelDecode");
710 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000711
712 if (JCS_CMYK == cinfo.out_color_space) {
713 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
714 }
715
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000716 sampler.next(srcRow);
717 if (bm->height() - 1 == y) {
718 // we're done
719 break;
720 }
721
722 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
723 return return_false(cinfo, *bm, "skip rows");
724 }
725 }
726
727 // we formally skip the rest, so we don't get a complaint from libjpeg
728 if (!skip_src_rows(&cinfo, srcRow,
729 cinfo.output_height - cinfo.output_scanline)) {
730 return return_false(cinfo, *bm, "skip rows");
731 }
732 jpeg_finish_decompress(&cinfo);
733
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000734 return true;
735}
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
sugoib227e372014-10-16 13:10:57 -0700752static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
753 SizeType sizeType) {
754 for (int i = 0; i < 3; ++i) {
755 componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
756 }
757}
758
759static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) {
760 // U size and V size have to be the same if we're calling output_raw_data()
761 SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType);
762 SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType));
763
764 JSAMPARRAY bufferraw[3];
765 JSAMPROW bufferraw2[32];
766 bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16)
767 bufferraw[1] = &bufferraw2[16]; // U channel rows (8)
768 bufferraw[2] = &bufferraw2[24]; // V channel rows (8)
769 int yWidth = cinfo.output_width;
770 int yHeight = cinfo.output_height;
771 int yMaxH = yHeight - 1;
772 int v = cinfo.cur_comp_info[0]->v_samp_factor;
773 int uvMaxH = uvSize.height() - 1;
774 JSAMPROW outputY = static_cast<JSAMPROW>(planes[0]);
775 JSAMPROW outputU = static_cast<JSAMPROW>(planes[1]);
776 JSAMPROW outputV = static_cast<JSAMPROW>(planes[2]);
777 size_t rowBytesY = rowBytes[0];
778 size_t rowBytesU = rowBytes[1];
779 size_t rowBytesV = rowBytes[2];
780
781 int yScanlinesToRead = DCTSIZE * v;
782 SkAutoMalloc lastRowStorage(yWidth * 8);
783 JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get();
784 JSAMPROW uLastRow = yLastRow + 2 * yWidth;
785 JSAMPROW vLastRow = uLastRow + 2 * yWidth;
786 JSAMPROW dummyRow = vLastRow + 2 * yWidth;
787
788 while (cinfo.output_scanline < cinfo.output_height) {
789 // Request 8 or 16 scanlines: returns 0 or more scanlines.
790 bool hasYLastRow(false), hasUVLastRow(false);
791 // Assign 8 or 16 rows of memory to read the Y channel.
792 for (int i = 0; i < yScanlinesToRead; ++i) {
793 int scanline = (cinfo.output_scanline + i);
794 if (scanline < yMaxH) {
795 bufferraw2[i] = &outputY[scanline * rowBytesY];
796 } else if (scanline == yMaxH) {
797 bufferraw2[i] = yLastRow;
798 hasYLastRow = true;
799 } else {
800 bufferraw2[i] = dummyRow;
801 }
802 }
803 int scaledScanline = cinfo.output_scanline / v;
804 // Assign 8 rows of memory to read the U and V channels.
805 for (int i = 0; i < 8; ++i) {
806 int scanline = (scaledScanline + i);
807 if (scanline < uvMaxH) {
808 bufferraw2[16 + i] = &outputU[scanline * rowBytesU];
809 bufferraw2[24 + i] = &outputV[scanline * rowBytesV];
810 } else if (scanline == uvMaxH) {
811 bufferraw2[16 + i] = uLastRow;
812 bufferraw2[24 + i] = vLastRow;
813 hasUVLastRow = true;
814 } else {
815 bufferraw2[16 + i] = dummyRow;
816 bufferraw2[24 + i] = dummyRow;
817 }
818 }
819 JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead);
820
821 if (scanlinesRead == 0)
822 return false;
823
824 if (hasYLastRow) {
825 memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth);
826 }
827 if (hasUVLastRow) {
828 memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width());
829 memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width());
830 }
831 }
832
833 cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height);
834
835 return true;
836}
837
838bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
839 void* planes[3], size_t rowBytes[3],
840 SkYUVColorSpace* colorSpace) {
841#ifdef TIME_DECODE
842 SkAutoTime atm("JPEG YUV8 Decode");
843#endif
844
845 if (this->getSampleSize() != 1) {
846 return false; // Resizing not supported
847 }
848
849 JPEGAutoClean autoClean;
850
851 jpeg_decompress_struct cinfo;
852 skjpeg_source_mgr srcManager(stream, this);
853
854 skjpeg_error_mgr errorManager;
855 set_error_mgr(&cinfo, &errorManager);
856
857 // All objects need to be instantiated before this setjmp call so that
858 // they will be cleaned up properly if an error occurs.
859 if (setjmp(errorManager.fJmpBuf)) {
860 return return_false(cinfo, 0, 0, "setjmp YUV8");
861 }
862
863 initialize_info(&cinfo, &srcManager);
864 autoClean.set(&cinfo);
865
866 int status = jpeg_read_header(&cinfo, true);
867 if (status != JPEG_HEADER_OK) {
868 return return_false(cinfo, 0, 0, "read_header YUV8");
869 }
870
871 if (cinfo.jpeg_color_space != JCS_YCbCr) {
872 // It's not an error to not be encoded in YUV, so no need to use return_false()
873 return false;
874 }
875
876 cinfo.out_color_space = JCS_YCbCr;
877 cinfo.raw_data_out = TRUE;
878
879 if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only
880 update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType);
881 return true;
882 }
883
884 set_dct_method(*this, &cinfo);
885
886 SkASSERT(1 == cinfo.scale_num);
887 cinfo.scale_denom = 1;
888
889 turn_off_visual_optimizations(&cinfo);
890
891#ifdef ANDROID_RGB
892 cinfo.dither_mode = JDITHER_NONE;
893#endif
894
895 /* image_width and image_height are the original dimensions, available
896 after jpeg_read_header(). To see the scaled dimensions, we have to call
897 jpeg_start_decompress(), and then read output_width and output_height.
898 */
899 if (!jpeg_start_decompress(&cinfo)) {
900 return return_false(cinfo, 0, 0, "start_decompress YUV8");
901 }
902
903 if (!output_raw_data(cinfo, planes, rowBytes)) {
904 return return_false(cinfo, 0, 0, "output_raw_data");
905 }
906
907 update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
908 jpeg_finish_decompress(&cinfo);
909
910 if (NULL != colorSpace) {
911 *colorSpace = kJPEG_SkYUVColorSpace;
912 }
913
914 return true;
915}
916
scroggo@google.comd79277f2013-08-07 19:53:53 +0000917#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000918bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000919
scroggo@google.coma1a51542013-08-07 21:02:32 +0000920 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000921 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000922
923 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000924 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000925
926 // All objects need to be instantiated before this setjmp call so that
927 // they will be cleaned up properly if an error occurs.
928 if (setjmp(sk_err.fJmpBuf)) {
929 return false;
930 }
931
932 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000933 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000934 return false;
935 }
936
scroggo@google.coma1a51542013-08-07 21:02:32 +0000937 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000938 return false;
939 }
940
941 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000942 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000943
944 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000945 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000946 return false;
947 }
948
scroggo@google.com590a5af2013-08-07 21:09:13 +0000949 // FIXME: This sets cinfo->out_color_space, which we may change later
950 // based on the config in onDecodeSubset. This should be fine, since
951 // jpeg_init_read_tile_scanline will check out_color_space again after
952 // that change (when it calls jinit_color_deconverter).
reed6c225732014-06-09 19:52:07 -0700953 (void) this->getBitmapColorType(cinfo);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000954
955 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000956
957 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000958 if (!imageIndex->startTileDecompress()) {
959 return false;
960 }
djsollen@google.com11399402013-03-20 17:45:27 +0000961
scroggo@google.coma1a51542013-08-07 21:02:32 +0000962 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000963 fImageWidth = cinfo->output_width;
964 fImageHeight = cinfo->output_height;
965
966 if (width) {
967 *width = fImageWidth;
968 }
969 if (height) {
970 *height = fImageHeight;
971 }
djsollen@google.com11399402013-03-20 17:45:27 +0000972
973 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000974 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +0000975
976 return true;
977}
978
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000979bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000980 if (NULL == fImageIndex) {
981 return false;
982 }
983 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
984
985 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
986 if (!rect.intersect(region)) {
987 // If the requested region is entirely outside the image return false
988 return false;
989 }
990
991
992 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000993 set_error_mgr(cinfo, &errorManager);
994
djsollen@google.com11399402013-03-20 17:45:27 +0000995 if (setjmp(errorManager.fJmpBuf)) {
996 return false;
997 }
998
999 int requestedSampleSize = this->getSampleSize();
1000 cinfo->scale_denom = requestedSampleSize;
1001
scroggo@google.com590a5af2013-08-07 21:09:13 +00001002 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +00001003
reed6c225732014-06-09 19:52:07 -07001004 const SkColorType colorType = this->getBitmapColorType(cinfo);
1005 adjust_out_color_space_and_dither(cinfo, colorType, *this);
djsollen@google.com11399402013-03-20 17:45:27 +00001006
1007 int startX = rect.fLeft;
1008 int startY = rect.fTop;
1009 int width = rect.width();
1010 int height = rect.height();
1011
1012 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
1013 &startX, &startY, &width, &height);
1014 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
1015 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
1016
1017 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
1018
1019 SkBitmap bitmap;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001020 // Assume an A8 bitmap is not opaque to avoid the check of each
1021 // individual pixel. It is very unlikely to be opaque, since
1022 // an opaque A8 bitmap would not be very interesting.
1023 // Otherwise, a jpeg image is opaque.
reed6c225732014-06-09 19:52:07 -07001024 bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType,
1025 kAlpha_8_SkColorType == colorType ?
1026 kPremul_SkAlphaType : kOpaque_SkAlphaType));
djsollen@google.com11399402013-03-20 17:45:27 +00001027
1028 // Check ahead of time if the swap(dest, src) is possible or not.
1029 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
1030 // swap happening. If no, then we will use alloc to allocate pixels to
1031 // prevent garbage collection.
1032 int w = rect.width() / actualSampleSize;
1033 int h = rect.height() / actualSampleSize;
1034 bool swapOnly = (rect == region) && bm->isNull() &&
1035 (w == bitmap.width()) && (h == bitmap.height()) &&
1036 ((startX - rect.x()) / actualSampleSize == 0) &&
1037 ((startY - rect.y()) / actualSampleSize == 0);
1038 if (swapOnly) {
1039 if (!this->allocPixelRef(&bitmap, NULL)) {
1040 return return_false(*cinfo, bitmap, "allocPixelRef");
1041 }
1042 } else {
reed84825042014-09-02 12:50:45 -07001043 if (!bitmap.tryAllocPixels()) {
djsollen@google.com11399402013-03-20 17:45:27 +00001044 return return_false(*cinfo, bitmap, "allocPixels");
1045 }
1046 }
1047
1048 SkAutoLockPixels alp(bitmap);
1049
1050#ifdef ANDROID_RGB
1051 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
1052 a significant performance boost.
1053 */
1054 if (skiaSampleSize == 1 &&
reed6c225732014-06-09 19:52:07 -07001055 ((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||
1056 (kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565)))
djsollen@google.com11399402013-03-20 17:45:27 +00001057 {
1058 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
1059 INT32 const bpr = bitmap.rowBytes();
1060 int rowTotalCount = 0;
1061
1062 while (rowTotalCount < height) {
1063 int rowCount = jpeg_read_tile_scanline(cinfo,
1064 fImageIndex->huffmanIndex(),
1065 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001066 // if rowCount == 0, then we didn't get a scanline, so abort.
1067 // onDecodeSubset() relies on onBuildTileIndex(), which
1068 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001069 if (0 == rowCount) {
1070 return return_false(*cinfo, bitmap, "read_scanlines");
1071 }
1072 if (this->shouldCancelDecode()) {
1073 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1074 }
1075 rowTotalCount += rowCount;
1076 rowptr += bpr;
1077 }
1078
1079 if (swapOnly) {
1080 bm->swap(bitmap);
1081 } else {
1082 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1083 region.width(), region.height(), startX, startY);
1084 }
1085 return true;
1086 }
1087#endif
1088
1089 // check for supported formats
1090 SkScaledBitmapSampler::SrcConfig sc;
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001091 int srcBytesPerPixel;
1092
1093 if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001094 return return_false(*cinfo, *bm, "jpeg colorspace");
1095 }
1096
scroggo@google.com8d239242013-10-01 17:27:15 +00001097 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +00001098 return return_false(*cinfo, bitmap, "sampler.begin");
1099 }
1100
scroggo@google.com5ee18dd2013-10-21 20:47:31 +00001101 SkAutoMalloc srcStorage(width * srcBytesPerPixel);
djsollen@google.com11399402013-03-20 17:45:27 +00001102 uint8_t* srcRow = (uint8_t*)srcStorage.get();
1103
1104 // Possibly skip initial rows [sampler.srcY0]
1105 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
1106 return return_false(*cinfo, bitmap, "skip rows");
1107 }
1108
1109 // now loop through scanlines until y == bitmap->height() - 1
1110 for (int y = 0;; y++) {
1111 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
1112 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +00001113 // if row_count == 0, then we didn't get a scanline, so abort.
1114 // onDecodeSubset() relies on onBuildTileIndex(), which
1115 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +00001116 if (0 == row_count) {
1117 return return_false(*cinfo, bitmap, "read_scanlines");
1118 }
1119 if (this->shouldCancelDecode()) {
1120 return return_false(*cinfo, bitmap, "shouldCancelDecode");
1121 }
1122
1123 if (JCS_CMYK == cinfo->out_color_space) {
1124 convert_CMYK_to_RGB(srcRow, width);
1125 }
1126
1127 sampler.next(srcRow);
1128 if (bitmap.height() - 1 == y) {
1129 // we're done
1130 break;
1131 }
1132
1133 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
1134 sampler.srcDY() - 1)) {
1135 return return_false(*cinfo, bitmap, "skip rows");
1136 }
1137 }
1138 if (swapOnly) {
1139 bm->swap(bitmap);
1140 } else {
1141 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
1142 region.width(), region.height(), startX, startY);
1143 }
1144 return true;
1145}
1146#endif
1147
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001148///////////////////////////////////////////////////////////////////////////////
1149
1150#include "SkColorPriv.h"
1151
1152// taken from jcolor.c in libjpeg
1153#if 0 // 16bit - precise but slow
1154 #define CYR 19595 // 0.299
1155 #define CYG 38470 // 0.587
1156 #define CYB 7471 // 0.114
1157
1158 #define CUR -11059 // -0.16874
1159 #define CUG -21709 // -0.33126
1160 #define CUB 32768 // 0.5
1161
1162 #define CVR 32768 // 0.5
1163 #define CVG -27439 // -0.41869
1164 #define CVB -5329 // -0.08131
1165
1166 #define CSHIFT 16
1167#else // 8bit - fast, slightly less precise
1168 #define CYR 77 // 0.299
1169 #define CYG 150 // 0.587
1170 #define CYB 29 // 0.114
1171
1172 #define CUR -43 // -0.16874
1173 #define CUG -85 // -0.33126
1174 #define CUB 128 // 0.5
1175
1176 #define CVR 128 // 0.5
1177 #define CVG -107 // -0.41869
1178 #define CVB -21 // -0.08131
1179
1180 #define CSHIFT 8
1181#endif
1182
1183static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
1184 int r = SkGetPackedR32(c);
1185 int g = SkGetPackedG32(c);
1186 int b = SkGetPackedB32(c);
1187
1188 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
1189 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
1190 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
1191
1192 dst[0] = SkToU8(y);
1193 dst[1] = SkToU8(u + 128);
1194 dst[2] = SkToU8(v + 128);
1195}
1196
1197static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
1198 int r = SkGetPackedR4444(c);
1199 int g = SkGetPackedG4444(c);
1200 int b = SkGetPackedB4444(c);
1201
1202 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
1203 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
1204 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
1205
1206 dst[0] = SkToU8(y);
1207 dst[1] = SkToU8(u + 128);
1208 dst[2] = SkToU8(v + 128);
1209}
1210
1211static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
1212 int r = SkGetPackedR16(c);
1213 int g = SkGetPackedG16(c);
1214 int b = SkGetPackedB16(c);
1215
1216 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
1217 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
1218 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
1219
1220 dst[0] = SkToU8(y);
1221 dst[1] = SkToU8(u + 128);
1222 dst[2] = SkToU8(v + 128);
1223}
1224
1225///////////////////////////////////////////////////////////////////////////////
1226
1227typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1228 const void* SK_RESTRICT src, int width,
1229 const SkPMColor* SK_RESTRICT ctable);
1230
1231static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1232 const void* SK_RESTRICT srcRow, int width,
1233 const SkPMColor*) {
1234 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1235 while (--width >= 0) {
1236#ifdef WE_CONVERT_TO_YUV
1237 rgb2yuv_32(dst, *src++);
1238#else
1239 uint32_t c = *src++;
1240 dst[0] = SkGetPackedR32(c);
1241 dst[1] = SkGetPackedG32(c);
1242 dst[2] = SkGetPackedB32(c);
1243#endif
1244 dst += 3;
1245 }
1246}
1247
1248static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1249 const void* SK_RESTRICT srcRow, int width,
1250 const SkPMColor*) {
1251 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1252 while (--width >= 0) {
1253#ifdef WE_CONVERT_TO_YUV
1254 rgb2yuv_4444(dst, *src++);
1255#else
1256 SkPMColor16 c = *src++;
1257 dst[0] = SkPacked4444ToR32(c);
1258 dst[1] = SkPacked4444ToG32(c);
1259 dst[2] = SkPacked4444ToB32(c);
1260#endif
1261 dst += 3;
1262 }
1263}
1264
1265static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1266 const void* SK_RESTRICT srcRow, int width,
1267 const SkPMColor*) {
1268 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1269 while (--width >= 0) {
1270#ifdef WE_CONVERT_TO_YUV
1271 rgb2yuv_16(dst, *src++);
1272#else
1273 uint16_t c = *src++;
1274 dst[0] = SkPacked16ToR32(c);
1275 dst[1] = SkPacked16ToG32(c);
1276 dst[2] = SkPacked16ToB32(c);
1277#endif
1278 dst += 3;
1279 }
1280}
1281
1282static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1283 const void* SK_RESTRICT srcRow, int width,
1284 const SkPMColor* SK_RESTRICT ctable) {
1285 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1286 while (--width >= 0) {
1287#ifdef WE_CONVERT_TO_YUV
1288 rgb2yuv_32(dst, ctable[*src++]);
1289#else
1290 uint32_t c = ctable[*src++];
1291 dst[0] = SkGetPackedR32(c);
1292 dst[1] = SkGetPackedG32(c);
1293 dst[2] = SkGetPackedB32(c);
1294#endif
1295 dst += 3;
1296 }
1297}
1298
1299static WriteScanline ChooseWriter(const SkBitmap& bm) {
reed6c225732014-06-09 19:52:07 -07001300 switch (bm.colorType()) {
1301 case kN32_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001302 return Write_32_YUV;
reed6c225732014-06-09 19:52:07 -07001303 case kRGB_565_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001304 return Write_16_YUV;
reed6c225732014-06-09 19:52:07 -07001305 case kARGB_4444_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001306 return Write_4444_YUV;
reed6c225732014-06-09 19:52:07 -07001307 case kIndex_8_SkColorType:
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001308 return Write_Index_YUV;
1309 default:
1310 return NULL;
1311 }
1312}
1313
1314class SkJPEGImageEncoder : public SkImageEncoder {
1315protected:
1316 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1317#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001318 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001319#endif
1320
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001321 SkAutoLockPixels alp(bm);
1322 if (NULL == bm.getPixels()) {
1323 return false;
1324 }
1325
1326 jpeg_compress_struct cinfo;
1327 skjpeg_error_mgr sk_err;
1328 skjpeg_destination_mgr sk_wstream(stream);
1329
1330 // allocate these before set call setjmp
1331 SkAutoMalloc oneRow;
1332 SkAutoLockColors ctLocker;
1333
1334 cinfo.err = jpeg_std_error(&sk_err);
1335 sk_err.error_exit = skjpeg_error_exit;
1336 if (setjmp(sk_err.fJmpBuf)) {
1337 return false;
1338 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001339
mtklein@google.com8d725b22013-07-24 16:20:05 +00001340 // Keep after setjmp or mark volatile.
1341 const WriteScanline writer = ChooseWriter(bm);
1342 if (NULL == writer) {
1343 return false;
1344 }
1345
1346 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001347 cinfo.dest = &sk_wstream;
1348 cinfo.image_width = bm.width();
1349 cinfo.image_height = bm.height();
1350 cinfo.input_components = 3;
1351#ifdef WE_CONVERT_TO_YUV
1352 cinfo.in_color_space = JCS_YCbCr;
1353#else
1354 cinfo.in_color_space = JCS_RGB;
1355#endif
1356 cinfo.input_gamma = 1;
1357
1358 jpeg_set_defaults(&cinfo);
1359 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001360#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001361 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001362#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001363
1364 jpeg_start_compress(&cinfo, TRUE);
1365
1366 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001367 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001368
1369 const SkPMColor* colors = ctLocker.lockColors(bm);
1370 const void* srcRow = bm.getPixels();
1371
1372 while (cinfo.next_scanline < cinfo.image_height) {
1373 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1374
1375 writer(oneRowP, srcRow, width, colors);
1376 row_pointer[0] = oneRowP;
1377 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1378 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1379 }
1380
1381 jpeg_finish_compress(&cinfo);
1382 jpeg_destroy_compress(&cinfo);
1383
1384 return true;
1385 }
1386};
1387
1388///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001389DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1390DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1391///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001392
scroggo@google.comb5571b32013-09-25 21:34:24 +00001393static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001394 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001395 static const size_t HEADER_SIZE = sizeof(gHeader);
1396
1397 char buffer[HEADER_SIZE];
1398 size_t len = stream->read(buffer, HEADER_SIZE);
1399
1400 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001401 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001402 }
1403 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001404 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001405 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001406 return true;
1407}
1408
scroggo@google.comb5571b32013-09-25 21:34:24 +00001409
1410static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001411 if (is_jpeg(stream)) {
1412 return SkNEW(SkJPEGImageDecoder);
1413 }
1414 return NULL;
1415}
1416
scroggo@google.comb5571b32013-09-25 21:34:24 +00001417static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001418 if (is_jpeg(stream)) {
1419 return SkImageDecoder::kJPEG_Format;
1420 }
1421 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001422}
1423
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001424static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001425 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1426}
1427
mtklein@google.combd6343b2013-09-04 17:20:18 +00001428static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1429static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1430static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);