blob: 3b3ea887e3cde437d7469d2a43126a2cb604a6eb [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"
djsollen@google.com11399402013-03-20 17:45:27 +000019#include "SkRect.h"
20#include "SkCanvas.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000021
22#include <stdio.h>
23extern "C" {
24 #include "jpeglib.h"
25 #include "jerror.h"
26}
27
djsollen@google.com11399402013-03-20 17:45:27 +000028// These enable timing code that report milliseconds for an encoding/decoding
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000029//#define TIME_ENCODE
30//#define TIME_DECODE
31
32// this enables our rgb->yuv code, which is faster than libjpeg on ARM
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000033#define WE_CONVERT_TO_YUV
34
djsollen@google.com11399402013-03-20 17:45:27 +000035// If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers
36// support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565.
37
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000038//////////////////////////////////////////////////////////////////////////
39//////////////////////////////////////////////////////////////////////////
40
scroggo@google.com590a5af2013-08-07 21:09:13 +000041static void overwrite_mem_buffer_size(jpeg_decompress_struct* cinfo) {
djsollen@google.com11399402013-03-20 17:45:27 +000042#ifdef SK_BUILD_FOR_ANDROID
43 /* Check if the device indicates that it has a large amount of system memory
44 * if so, increase the memory allocation to 30MB instead of the default 5MB.
45 */
46#ifdef ANDROID_LARGE_MEMORY_DEVICE
47 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
48#else
49 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
50#endif
51#endif // SK_BUILD_FOR_ANDROID
52}
53
54//////////////////////////////////////////////////////////////////////////
55//////////////////////////////////////////////////////////////////////////
56
scroggo@google.com590a5af2013-08-07 21:09:13 +000057static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* src_mgr) {
58 SkASSERT(cinfo != NULL);
59 SkASSERT(src_mgr != NULL);
60 jpeg_create_decompress(cinfo);
61 overwrite_mem_buffer_size(cinfo);
62 cinfo->src = src_mgr;
63}
64
scroggo@google.coma1a51542013-08-07 21:02:32 +000065#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +000066class SkJPEGImageIndex {
67public:
scroggo@google.comb5571b32013-09-25 21:34:24 +000068 SkJPEGImageIndex(SkStreamRewindable* stream, SkImageDecoder* decoder)
scroggo@google.coma1a51542013-08-07 21:02:32 +000069 : fSrcMgr(stream, decoder)
70 , fInfoInitialized(false)
71 , fHuffmanCreated(false)
72 , fDecompressStarted(false)
73 {
74 SkDEBUGCODE(fReadHeaderSucceeded = false;)
75 }
djsollen@google.com11399402013-03-20 17:45:27 +000076
77 ~SkJPEGImageIndex() {
scroggo@google.coma1a51542013-08-07 21:02:32 +000078 if (fHuffmanCreated) {
scroggo@google.com590a5af2013-08-07 21:09:13 +000079 // Set to false before calling the libjpeg function, in case
80 // the libjpeg function calls longjmp. Our setjmp handler may
81 // attempt to delete this SkJPEGImageIndex, thus entering this
82 // destructor again. Setting fHuffmanCreated to false first
83 // prevents an infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +000084 fHuffmanCreated = false;
85 jpeg_destroy_huffman_index(&fHuffmanIndex);
86 }
87 if (fDecompressStarted) {
scroggo@google.com590a5af2013-08-07 21:09:13 +000088 // Like fHuffmanCreated, set to false before calling libjpeg
89 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +000090 fDecompressStarted = false;
91 jpeg_finish_decompress(&fCInfo);
92 }
93 if (fInfoInitialized) {
94 this->destroyInfo();
95 }
djsollen@google.com11399402013-03-20 17:45:27 +000096 }
97
98 /**
scroggo@google.coma1a51542013-08-07 21:02:32 +000099 * Destroy the cinfo struct.
100 * After this call, if a huffman index was already built, it
101 * can be used after calling initializeInfoAndReadHeader
102 * again. Must not be called after startTileDecompress except
103 * in the destructor.
djsollen@google.com11399402013-03-20 17:45:27 +0000104 */
scroggo@google.coma1a51542013-08-07 21:02:32 +0000105 void destroyInfo() {
106 SkASSERT(fInfoInitialized);
107 SkASSERT(!fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000108 // Like fHuffmanCreated, set to false before calling libjpeg
109 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000110 fInfoInitialized = false;
111 jpeg_destroy_decompress(&fCInfo);
112 SkDEBUGCODE(fReadHeaderSucceeded = false;)
113 }
114
115 /**
116 * Initialize the cinfo struct.
117 * Calls jpeg_create_decompress, makes customizations, and
118 * finally calls jpeg_read_header. Returns true if jpeg_read_header
119 * returns JPEG_HEADER_OK.
120 * If cinfo was already initialized, destroyInfo must be called to
121 * destroy the old one. Must not be called after startTileDecompress.
122 */
123 bool initializeInfoAndReadHeader() {
124 SkASSERT(!fInfoInitialized && !fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000125 initialize_info(&fCInfo, &fSrcMgr);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000126 fInfoInitialized = true;
127 const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true));
128 SkDEBUGCODE(fReadHeaderSucceeded = success;)
129 return success;
djsollen@google.com11399402013-03-20 17:45:27 +0000130 }
131
132 jpeg_decompress_struct* cinfo() { return &fCInfo; }
133
djsollen@google.com11399402013-03-20 17:45:27 +0000134 huffman_index* huffmanIndex() { return &fHuffmanIndex; }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000135
136 /**
137 * Build the index to be used for tile based decoding.
138 * Must only be called after a successful call to
139 * initializeInfoAndReadHeader and must not be called more
140 * than once.
141 */
142 bool buildHuffmanIndex() {
143 SkASSERT(fReadHeaderSucceeded);
144 SkASSERT(!fHuffmanCreated);
145 jpeg_create_huffman_index(&fCInfo, &fHuffmanIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000146 SkASSERT(1 == fCInfo.scale_num && 1 == fCInfo.scale_denom);
scroggo@google.com57a52982013-08-27 20:42:22 +0000147 fHuffmanCreated = jpeg_build_huffman_index(&fCInfo, &fHuffmanIndex);
148 return fHuffmanCreated;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000149 }
150
151 /**
152 * Start tile based decoding. Must only be called after a
153 * successful call to buildHuffmanIndex, and must only be
154 * called once.
155 */
156 bool startTileDecompress() {
157 SkASSERT(fHuffmanCreated);
158 SkASSERT(fReadHeaderSucceeded);
159 SkASSERT(!fDecompressStarted);
160 if (jpeg_start_tile_decompress(&fCInfo)) {
161 fDecompressStarted = true;
162 return true;
163 }
164 return false;
165 }
djsollen@google.com11399402013-03-20 17:45:27 +0000166
167private:
168 skjpeg_source_mgr fSrcMgr;
169 jpeg_decompress_struct fCInfo;
djsollen@google.com11399402013-03-20 17:45:27 +0000170 huffman_index fHuffmanIndex;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000171 bool fInfoInitialized;
172 bool fHuffmanCreated;
173 bool fDecompressStarted;
174 SkDEBUGCODE(bool fReadHeaderSucceeded;)
djsollen@google.com11399402013-03-20 17:45:27 +0000175};
scroggo@google.coma1a51542013-08-07 21:02:32 +0000176#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000177
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000178class SkJPEGImageDecoder : public SkImageDecoder {
179public:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000180#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000181 SkJPEGImageDecoder() {
182 fImageIndex = NULL;
183 fImageWidth = 0;
184 fImageHeight = 0;
185 }
186
187 virtual ~SkJPEGImageDecoder() {
188 SkDELETE(fImageIndex);
189 }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000190#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000191
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000192 virtual Format getFormat() const {
193 return kJPEG_Format;
194 }
195
196protected:
scroggo@google.comd79277f2013-08-07 19:53:53 +0000197#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000198 virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000199 virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
djsollen@google.com11399402013-03-20 17:45:27 +0000200#endif
201 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
202
203private:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000204#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000205 SkJPEGImageIndex* fImageIndex;
206 int fImageWidth;
207 int fImageHeight;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000208#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000209
scroggo@google.com590a5af2013-08-07 21:09:13 +0000210 /**
211 * Determine the appropriate bitmap config and out_color_space based on
212 * both the preference of the caller and the jpeg_color_space on the
213 * jpeg_decompress_struct passed in.
214 * Must be called after jpeg_read_header.
215 */
216 SkBitmap::Config getBitmapConfig(jpeg_decompress_struct*);
217
djsollen@google.com11399402013-03-20 17:45:27 +0000218 typedef SkImageDecoder INHERITED;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000219};
220
221//////////////////////////////////////////////////////////////////////////
222
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000223/* Automatically clean up after throwing an exception */
224class JPEGAutoClean {
225public:
226 JPEGAutoClean(): cinfo_ptr(NULL) {}
227 ~JPEGAutoClean() {
228 if (cinfo_ptr) {
229 jpeg_destroy_decompress(cinfo_ptr);
230 }
231 }
232 void set(jpeg_decompress_struct* info) {
233 cinfo_ptr = info;
234 }
235private:
236 jpeg_decompress_struct* cinfo_ptr;
237};
238
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000239///////////////////////////////////////////////////////////////////////////////
240
241/* If we need to better match the request, we might examine the image and
242 output dimensions, and determine if the downsampling jpeg provided is
243 not sufficient. If so, we can recompute a modified sampleSize value to
244 make up the difference.
245
246 To skip this additional scaling, just set sampleSize = 1; below.
247 */
248static int recompute_sampleSize(int sampleSize,
249 const jpeg_decompress_struct& cinfo) {
250 return sampleSize * cinfo.output_width / cinfo.image_width;
251}
252
253static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
254 /* These are initialized to 0, so if they have non-zero values, we assume
255 they are "valid" (i.e. have been computed by libjpeg)
256 */
djsollen@google.com11399402013-03-20 17:45:27 +0000257 return 0 != cinfo.output_width && 0 != cinfo.output_height;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000258}
259
djsollen@google.com11399402013-03-20 17:45:27 +0000260static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000261 for (int i = 0; i < count; i++) {
262 JSAMPLE* rowptr = (JSAMPLE*)buffer;
263 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
djsollen@google.com11399402013-03-20 17:45:27 +0000264 if (1 != row_count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000265 return false;
266 }
267 }
268 return true;
269}
270
scroggo@google.comd79277f2013-08-07 19:53:53 +0000271#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000272static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
273 huffman_index *index, void* buffer, int count) {
274 for (int i = 0; i < count; i++) {
275 JSAMPLE* rowptr = (JSAMPLE*)buffer;
276 int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
277 if (1 != row_count) {
278 return false;
279 }
280 }
281 return true;
282}
283#endif
284
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000285// This guy exists just to aid in debugging, as it allows debuggers to just
286// set a break-point in one place to see all error exists.
287static bool return_false(const jpeg_decompress_struct& cinfo,
scroggo@google.com228f2b82013-09-25 21:27:31 +0000288 const SkBitmap& bm, const char caller[]) {
djsollen@google.com11399402013-03-20 17:45:27 +0000289#ifdef SK_DEBUG
scroggo@google.com228f2b82013-09-25 21:27:31 +0000290 char buffer[JMSG_LENGTH_MAX];
291 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
scroggo@google.come8f34712013-07-01 14:44:54 +0000292 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n", cinfo.err->msg_code,
scroggo@google.com228f2b82013-09-25 21:27:31 +0000293 buffer, caller, bm.width(), bm.height());
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000294#endif
295 return false; // must always return false
296}
297
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000298// Convert a scanline of CMYK samples to RGBX in place. Note that this
299// method moves the "scanline" pointer in its processing
300static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
301 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000302 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000303 // from easyrgb.com):
304 // CMYK -> CMY
305 // C = ( C * (1 - K) + K ) // for each CMY component
306 // CMY -> RGB
307 // R = ( 1 - C ) * 255 // for each RGB component
308 // Unfortunately we are seeing inverted CMYK so all the original terms
309 // are 1-. This yields:
310 // CMYK -> CMY
311 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
312 // The conversion from CMY->RGB remains the same
313 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
314 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
315 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
316 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
317 scanline[3] = 255;
318 }
319}
320
scroggo@google.com590a5af2013-08-07 21:09:13 +0000321/**
322 * Common code for setting the error manager.
323 */
324static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
325 SkASSERT(cinfo != NULL);
326 SkASSERT(errorManager != NULL);
327 cinfo->err = jpeg_std_error(errorManager);
328 errorManager->error_exit = skjpeg_error_exit;
329}
330
331/**
332 * Common code for turning off upsampling and smoothing. Turning these
333 * off helps performance without showing noticable differences in the
334 * resulting bitmap.
335 */
336static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
337 SkASSERT(cinfo != NULL);
338 /* this gives about 30% performance improvement. In theory it may
339 reduce the visual quality, in practice I'm not seeing a difference
340 */
341 cinfo->do_fancy_upsampling = 0;
342
343 /* this gives another few percents */
344 cinfo->do_block_smoothing = 0;
345}
346
347/**
348 * Common code for setting the dct method.
349 */
350static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
351 SkASSERT(cinfo != NULL);
352#ifdef DCT_IFAST_SUPPORTED
353 if (decoder.getPreferQualityOverSpeed()) {
354 cinfo->dct_method = JDCT_ISLOW;
355 } else {
356 cinfo->dct_method = JDCT_IFAST;
357 }
358#else
359 cinfo->dct_method = JDCT_ISLOW;
360#endif
361}
362
363SkBitmap::Config SkJPEGImageDecoder::getBitmapConfig(jpeg_decompress_struct* cinfo) {
364 SkASSERT(cinfo != NULL);
365
366 SrcDepth srcDepth = k32Bit_SrcDepth;
367 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
368 srcDepth = k8BitGray_SrcDepth;
369 }
370
371 SkBitmap::Config config = this->getPrefConfig(srcDepth, /*hasAlpha*/ false);
372 switch (config) {
373 case SkBitmap::kA8_Config:
374 // Only respect A8 config if the original is grayscale,
375 // in which case we will treat the grayscale as alpha
376 // values.
377 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
378 config = SkBitmap::kARGB_8888_Config;
379 }
380 break;
381 case SkBitmap::kARGB_8888_Config:
382 // Fall through.
383 case SkBitmap::kARGB_4444_Config:
384 // Fall through.
385 case SkBitmap::kRGB_565_Config:
386 // These are acceptable destination configs.
387 break;
388 default:
389 // Force all other configs to 8888.
390 config = SkBitmap::kARGB_8888_Config;
391 break;
392 }
393
394 switch (cinfo->jpeg_color_space) {
395 case JCS_CMYK:
396 // Fall through.
397 case JCS_YCCK:
398 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
399 // so libjpeg will give us CMYK samples back and we will later
400 // manually convert them to RGB
401 cinfo->out_color_space = JCS_CMYK;
402 break;
403 case JCS_GRAYSCALE:
404 if (SkBitmap::kA8_Config == config) {
405 cinfo->out_color_space = JCS_GRAYSCALE;
406 break;
407 }
408 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
409 // config. Fall through to set to the default.
410 default:
411 cinfo->out_color_space = JCS_RGB;
412 break;
413 }
414 return config;
415}
416
417#ifdef ANDROID_RGB
418/**
419 * Based on the config and dither mode, adjust out_color_space and
420 * dither_mode of cinfo.
421 */
422static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
423 SkBitmap::Config config,
424 const SkImageDecoder& decoder) {
425 SkASSERT(cinfo != NULL);
426 cinfo->dither_mode = JDITHER_NONE;
427 if (JCS_CMYK == cinfo->out_color_space) {
428 return;
429 }
430 switch(config) {
431 case SkBitmap::kARGB_8888_Config:
432 cinfo->out_color_space = JCS_RGBA_8888;
433 break;
434 case SkBitmap::kRGB_565_Config:
435 cinfo->out_color_space = JCS_RGB_565;
436 if (decoder.getDitherImage()) {
437 cinfo->dither_mode = JDITHER_ORDERED;
438 }
439 break;
440 default:
441 break;
442 }
443}
444#endif
445
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000446bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
447#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000448 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000449#endif
450
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000451 JPEGAutoClean autoClean;
452
453 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000454 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000455
scroggo@google.com590a5af2013-08-07 21:09:13 +0000456 skjpeg_error_mgr errorManager;
457 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000458
459 // All objects need to be instantiated before this setjmp call so that
460 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000461 if (setjmp(errorManager.fJmpBuf)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000462 return return_false(cinfo, *bm, "setjmp");
463 }
464
scroggo@google.com590a5af2013-08-07 21:09:13 +0000465 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000466 autoClean.set(&cinfo);
467
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000468 int status = jpeg_read_header(&cinfo, true);
469 if (status != JPEG_HEADER_OK) {
470 return return_false(cinfo, *bm, "read_header");
471 }
472
473 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
474 can) much faster that we, just use their num/denom api to approximate
475 the size.
476 */
477 int sampleSize = this->getSampleSize();
478
scroggo@google.com590a5af2013-08-07 21:09:13 +0000479 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000480
scroggo@google.com590a5af2013-08-07 21:09:13 +0000481 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000482 cinfo.scale_denom = sampleSize;
483
scroggo@google.com590a5af2013-08-07 21:09:13 +0000484 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000485
scroggo@google.com590a5af2013-08-07 21:09:13 +0000486 const SkBitmap::Config config = this->getBitmapConfig(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000487
488#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000489 adjust_out_color_space_and_dither(&cinfo, config, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000490#endif
491
djsollen@google.com11399402013-03-20 17:45:27 +0000492 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000493 bm->setConfig(config, cinfo.image_width, cinfo.image_height);
scroggo@google.com1f6b9952013-07-18 19:37:46 +0000494 bm->setIsOpaque(config != SkBitmap::kA8_Config);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000495 return true;
496 }
497
498 /* image_width and image_height are the original dimensions, available
499 after jpeg_read_header(). To see the scaled dimensions, we have to call
500 jpeg_start_decompress(), and then read output_width and output_height.
501 */
502 if (!jpeg_start_decompress(&cinfo)) {
503 /* If we failed here, we may still have enough information to return
504 to the caller if they just wanted (subsampled bounds). If sampleSize
505 was 1, then we would have already returned. Thus we just check if
506 we're in kDecodeBounds_Mode, and that we have valid output sizes.
507
508 One reason to fail here is that we have insufficient stream data
509 to complete the setup. However, output dimensions seem to get
510 computed very early, which is why this special check can pay off.
511 */
djsollen@google.com11399402013-03-20 17:45:27 +0000512 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000513 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
514 recompute_sampleSize(sampleSize, cinfo));
515 bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
scroggo@google.com1f6b9952013-07-18 19:37:46 +0000516 bm->setIsOpaque(config != SkBitmap::kA8_Config);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000517 return true;
518 } else {
519 return return_false(cinfo, *bm, "start_decompress");
520 }
521 }
522 sampleSize = recompute_sampleSize(sampleSize, cinfo);
523
524 // should we allow the Chooser (if present) to pick a config for us???
djsollen@google.com11399402013-03-20 17:45:27 +0000525 if (!this->chooseFromOneChoice(config, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000526 return return_false(cinfo, *bm, "chooseFromOneChoice");
527 }
528
djsollen@google.com11399402013-03-20 17:45:27 +0000529 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000530 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
scroggo@google.com1f6b9952013-07-18 19:37:46 +0000531 bm->setIsOpaque(config != SkBitmap::kA8_Config);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000532 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
533 return true;
534 }
535 if (!this->allocPixelRef(bm, NULL)) {
536 return return_false(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000537 }
538
539 SkAutoLockPixels alp(*bm);
540
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000541#ifdef ANDROID_RGB
542 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
543 a significant performance boost.
544 */
545 if (sampleSize == 1 &&
rmistry@google.comd6176b02012-08-23 18:14:13 +0000546 ((config == SkBitmap::kARGB_8888_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000547 cinfo.out_color_space == JCS_RGBA_8888) ||
rmistry@google.comd6176b02012-08-23 18:14:13 +0000548 (config == SkBitmap::kRGB_565_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000549 cinfo.out_color_space == JCS_RGB_565)))
550 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000551 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000552 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000553
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000554 while (cinfo.output_scanline < cinfo.output_height) {
555 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
556 // if row_count == 0, then we didn't get a scanline, so abort.
557 // if we supported partial images, we might return true in this case
558 if (0 == row_count) {
559 return return_false(cinfo, *bm, "read_scanlines");
560 }
561 if (this->shouldCancelDecode()) {
562 return return_false(cinfo, *bm, "shouldCancelDecode");
563 }
564 rowptr += bpr;
565 }
566 jpeg_finish_decompress(&cinfo);
567 return true;
568 }
569#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000570
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000571 // check for supported formats
572 SkScaledBitmapSampler::SrcConfig sc;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000573 if (JCS_CMYK == cinfo.out_color_space) {
574 // In this case we will manually convert the CMYK values to RGB
575 sc = SkScaledBitmapSampler::kRGBX;
576 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000577 sc = SkScaledBitmapSampler::kRGB;
578#ifdef ANDROID_RGB
579 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
580 sc = SkScaledBitmapSampler::kRGBX;
581 } else if (JCS_RGB_565 == cinfo.out_color_space) {
582 sc = SkScaledBitmapSampler::kRGB_565;
583#endif
584 } else if (1 == cinfo.out_color_components &&
585 JCS_GRAYSCALE == cinfo.out_color_space) {
586 sc = SkScaledBitmapSampler::kGray;
587 } else {
588 return return_false(cinfo, *bm, "jpeg colorspace");
589 }
590
scroggo@google.com8d239242013-10-01 17:27:15 +0000591 if (!sampler.begin(bm, sc, *this)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000592 return return_false(cinfo, *bm, "sampler.begin");
593 }
594
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000595 // The CMYK work-around relies on 4 components per pixel here
djsollen@google.com11399402013-03-20 17:45:27 +0000596 SkAutoMalloc srcStorage(cinfo.output_width * 4);
597 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000598
599 // Possibly skip initial rows [sampler.srcY0]
600 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
601 return return_false(cinfo, *bm, "skip rows");
602 }
603
604 // now loop through scanlines until y == bm->height() - 1
605 for (int y = 0;; y++) {
606 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
607 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
608 if (0 == row_count) {
609 return return_false(cinfo, *bm, "read_scanlines");
610 }
611 if (this->shouldCancelDecode()) {
612 return return_false(cinfo, *bm, "shouldCancelDecode");
613 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000614
615 if (JCS_CMYK == cinfo.out_color_space) {
616 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
617 }
618
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000619 sampler.next(srcRow);
620 if (bm->height() - 1 == y) {
621 // we're done
622 break;
623 }
624
625 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
626 return return_false(cinfo, *bm, "skip rows");
627 }
628 }
629
630 // we formally skip the rest, so we don't get a complaint from libjpeg
631 if (!skip_src_rows(&cinfo, srcRow,
632 cinfo.output_height - cinfo.output_scanline)) {
633 return return_false(cinfo, *bm, "skip rows");
634 }
635 jpeg_finish_decompress(&cinfo);
636
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000637 return true;
638}
639
scroggo@google.comd79277f2013-08-07 19:53:53 +0000640#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000641bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000642
scroggo@google.coma1a51542013-08-07 21:02:32 +0000643 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000644 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000645
646 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000647 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000648
649 // All objects need to be instantiated before this setjmp call so that
650 // they will be cleaned up properly if an error occurs.
651 if (setjmp(sk_err.fJmpBuf)) {
652 return false;
653 }
654
655 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000656 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000657 return false;
658 }
659
scroggo@google.coma1a51542013-08-07 21:02:32 +0000660 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000661 return false;
662 }
663
664 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000665 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000666
667 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000668 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000669 return false;
670 }
671
scroggo@google.com590a5af2013-08-07 21:09:13 +0000672 // FIXME: This sets cinfo->out_color_space, which we may change later
673 // based on the config in onDecodeSubset. This should be fine, since
674 // jpeg_init_read_tile_scanline will check out_color_space again after
675 // that change (when it calls jinit_color_deconverter).
676 (void) this->getBitmapConfig(cinfo);
677
678 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000679
680 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000681 if (!imageIndex->startTileDecompress()) {
682 return false;
683 }
djsollen@google.com11399402013-03-20 17:45:27 +0000684
scroggo@google.coma1a51542013-08-07 21:02:32 +0000685 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000686 fImageWidth = cinfo->output_width;
687 fImageHeight = cinfo->output_height;
688
689 if (width) {
690 *width = fImageWidth;
691 }
692 if (height) {
693 *height = fImageHeight;
694 }
djsollen@google.com11399402013-03-20 17:45:27 +0000695
696 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000697 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +0000698
699 return true;
700}
701
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000702bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000703 if (NULL == fImageIndex) {
704 return false;
705 }
706 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
707
708 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
709 if (!rect.intersect(region)) {
710 // If the requested region is entirely outside the image return false
711 return false;
712 }
713
714
715 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000716 set_error_mgr(cinfo, &errorManager);
717
djsollen@google.com11399402013-03-20 17:45:27 +0000718 if (setjmp(errorManager.fJmpBuf)) {
719 return false;
720 }
721
722 int requestedSampleSize = this->getSampleSize();
723 cinfo->scale_denom = requestedSampleSize;
724
scroggo@google.com590a5af2013-08-07 21:09:13 +0000725 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000726
scroggo@google.com590a5af2013-08-07 21:09:13 +0000727 const SkBitmap::Config config = this->getBitmapConfig(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000728#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000729 adjust_out_color_space_and_dither(cinfo, config, *this);
djsollen@google.com11399402013-03-20 17:45:27 +0000730#endif
731
732 int startX = rect.fLeft;
733 int startY = rect.fTop;
734 int width = rect.width();
735 int height = rect.height();
736
737 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
738 &startX, &startY, &width, &height);
739 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
740 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
741
742 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
743
744 SkBitmap bitmap;
745 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
746 bitmap.setIsOpaque(true);
747
748 // Check ahead of time if the swap(dest, src) is possible or not.
749 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
750 // swap happening. If no, then we will use alloc to allocate pixels to
751 // prevent garbage collection.
752 int w = rect.width() / actualSampleSize;
753 int h = rect.height() / actualSampleSize;
754 bool swapOnly = (rect == region) && bm->isNull() &&
755 (w == bitmap.width()) && (h == bitmap.height()) &&
756 ((startX - rect.x()) / actualSampleSize == 0) &&
757 ((startY - rect.y()) / actualSampleSize == 0);
758 if (swapOnly) {
759 if (!this->allocPixelRef(&bitmap, NULL)) {
760 return return_false(*cinfo, bitmap, "allocPixelRef");
761 }
762 } else {
763 if (!bitmap.allocPixels()) {
764 return return_false(*cinfo, bitmap, "allocPixels");
765 }
766 }
767
768 SkAutoLockPixels alp(bitmap);
769
770#ifdef ANDROID_RGB
771 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
772 a significant performance boost.
773 */
774 if (skiaSampleSize == 1 &&
775 ((config == SkBitmap::kARGB_8888_Config &&
776 cinfo->out_color_space == JCS_RGBA_8888) ||
777 (config == SkBitmap::kRGB_565_Config &&
778 cinfo->out_color_space == JCS_RGB_565)))
779 {
780 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
781 INT32 const bpr = bitmap.rowBytes();
782 int rowTotalCount = 0;
783
784 while (rowTotalCount < height) {
785 int rowCount = jpeg_read_tile_scanline(cinfo,
786 fImageIndex->huffmanIndex(),
787 &rowptr);
788 // if row_count == 0, then we didn't get a scanline, so abort.
789 // if we supported partial images, we might return true in this case
790 if (0 == rowCount) {
791 return return_false(*cinfo, bitmap, "read_scanlines");
792 }
793 if (this->shouldCancelDecode()) {
794 return return_false(*cinfo, bitmap, "shouldCancelDecode");
795 }
796 rowTotalCount += rowCount;
797 rowptr += bpr;
798 }
799
800 if (swapOnly) {
801 bm->swap(bitmap);
802 } else {
803 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
804 region.width(), region.height(), startX, startY);
805 }
806 return true;
807 }
808#endif
809
810 // check for supported formats
811 SkScaledBitmapSampler::SrcConfig sc;
812 if (JCS_CMYK == cinfo->out_color_space) {
813 // In this case we will manually convert the CMYK values to RGB
814 sc = SkScaledBitmapSampler::kRGBX;
815 } else if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_space) {
816 sc = SkScaledBitmapSampler::kRGB;
817#ifdef ANDROID_RGB
818 } else if (JCS_RGBA_8888 == cinfo->out_color_space) {
819 sc = SkScaledBitmapSampler::kRGBX;
820 } else if (JCS_RGB_565 == cinfo->out_color_space) {
821 sc = SkScaledBitmapSampler::kRGB_565;
822#endif
823 } else if (1 == cinfo->out_color_components &&
824 JCS_GRAYSCALE == cinfo->out_color_space) {
825 sc = SkScaledBitmapSampler::kGray;
826 } else {
827 return return_false(*cinfo, *bm, "jpeg colorspace");
828 }
829
scroggo@google.com8d239242013-10-01 17:27:15 +0000830 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +0000831 return return_false(*cinfo, bitmap, "sampler.begin");
832 }
833
834 // The CMYK work-around relies on 4 components per pixel here
835 SkAutoMalloc srcStorage(width * 4);
836 uint8_t* srcRow = (uint8_t*)srcStorage.get();
837
838 // Possibly skip initial rows [sampler.srcY0]
839 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
840 return return_false(*cinfo, bitmap, "skip rows");
841 }
842
843 // now loop through scanlines until y == bitmap->height() - 1
844 for (int y = 0;; y++) {
845 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
846 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
847 if (0 == row_count) {
848 return return_false(*cinfo, bitmap, "read_scanlines");
849 }
850 if (this->shouldCancelDecode()) {
851 return return_false(*cinfo, bitmap, "shouldCancelDecode");
852 }
853
854 if (JCS_CMYK == cinfo->out_color_space) {
855 convert_CMYK_to_RGB(srcRow, width);
856 }
857
858 sampler.next(srcRow);
859 if (bitmap.height() - 1 == y) {
860 // we're done
861 break;
862 }
863
864 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
865 sampler.srcDY() - 1)) {
866 return return_false(*cinfo, bitmap, "skip rows");
867 }
868 }
869 if (swapOnly) {
870 bm->swap(bitmap);
871 } else {
872 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
873 region.width(), region.height(), startX, startY);
874 }
875 return true;
876}
877#endif
878
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000879///////////////////////////////////////////////////////////////////////////////
880
881#include "SkColorPriv.h"
882
883// taken from jcolor.c in libjpeg
884#if 0 // 16bit - precise but slow
885 #define CYR 19595 // 0.299
886 #define CYG 38470 // 0.587
887 #define CYB 7471 // 0.114
888
889 #define CUR -11059 // -0.16874
890 #define CUG -21709 // -0.33126
891 #define CUB 32768 // 0.5
892
893 #define CVR 32768 // 0.5
894 #define CVG -27439 // -0.41869
895 #define CVB -5329 // -0.08131
896
897 #define CSHIFT 16
898#else // 8bit - fast, slightly less precise
899 #define CYR 77 // 0.299
900 #define CYG 150 // 0.587
901 #define CYB 29 // 0.114
902
903 #define CUR -43 // -0.16874
904 #define CUG -85 // -0.33126
905 #define CUB 128 // 0.5
906
907 #define CVR 128 // 0.5
908 #define CVG -107 // -0.41869
909 #define CVB -21 // -0.08131
910
911 #define CSHIFT 8
912#endif
913
914static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
915 int r = SkGetPackedR32(c);
916 int g = SkGetPackedG32(c);
917 int b = SkGetPackedB32(c);
918
919 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
920 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
921 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
922
923 dst[0] = SkToU8(y);
924 dst[1] = SkToU8(u + 128);
925 dst[2] = SkToU8(v + 128);
926}
927
928static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
929 int r = SkGetPackedR4444(c);
930 int g = SkGetPackedG4444(c);
931 int b = SkGetPackedB4444(c);
932
933 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
934 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
935 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
936
937 dst[0] = SkToU8(y);
938 dst[1] = SkToU8(u + 128);
939 dst[2] = SkToU8(v + 128);
940}
941
942static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
943 int r = SkGetPackedR16(c);
944 int g = SkGetPackedG16(c);
945 int b = SkGetPackedB16(c);
946
947 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
948 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
949 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
950
951 dst[0] = SkToU8(y);
952 dst[1] = SkToU8(u + 128);
953 dst[2] = SkToU8(v + 128);
954}
955
956///////////////////////////////////////////////////////////////////////////////
957
958typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
959 const void* SK_RESTRICT src, int width,
960 const SkPMColor* SK_RESTRICT ctable);
961
962static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
963 const void* SK_RESTRICT srcRow, int width,
964 const SkPMColor*) {
965 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
966 while (--width >= 0) {
967#ifdef WE_CONVERT_TO_YUV
968 rgb2yuv_32(dst, *src++);
969#else
970 uint32_t c = *src++;
971 dst[0] = SkGetPackedR32(c);
972 dst[1] = SkGetPackedG32(c);
973 dst[2] = SkGetPackedB32(c);
974#endif
975 dst += 3;
976 }
977}
978
979static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
980 const void* SK_RESTRICT srcRow, int width,
981 const SkPMColor*) {
982 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
983 while (--width >= 0) {
984#ifdef WE_CONVERT_TO_YUV
985 rgb2yuv_4444(dst, *src++);
986#else
987 SkPMColor16 c = *src++;
988 dst[0] = SkPacked4444ToR32(c);
989 dst[1] = SkPacked4444ToG32(c);
990 dst[2] = SkPacked4444ToB32(c);
991#endif
992 dst += 3;
993 }
994}
995
996static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
997 const void* SK_RESTRICT srcRow, int width,
998 const SkPMColor*) {
999 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1000 while (--width >= 0) {
1001#ifdef WE_CONVERT_TO_YUV
1002 rgb2yuv_16(dst, *src++);
1003#else
1004 uint16_t c = *src++;
1005 dst[0] = SkPacked16ToR32(c);
1006 dst[1] = SkPacked16ToG32(c);
1007 dst[2] = SkPacked16ToB32(c);
1008#endif
1009 dst += 3;
1010 }
1011}
1012
1013static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1014 const void* SK_RESTRICT srcRow, int width,
1015 const SkPMColor* SK_RESTRICT ctable) {
1016 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1017 while (--width >= 0) {
1018#ifdef WE_CONVERT_TO_YUV
1019 rgb2yuv_32(dst, ctable[*src++]);
1020#else
1021 uint32_t c = ctable[*src++];
1022 dst[0] = SkGetPackedR32(c);
1023 dst[1] = SkGetPackedG32(c);
1024 dst[2] = SkGetPackedB32(c);
1025#endif
1026 dst += 3;
1027 }
1028}
1029
1030static WriteScanline ChooseWriter(const SkBitmap& bm) {
1031 switch (bm.config()) {
1032 case SkBitmap::kARGB_8888_Config:
1033 return Write_32_YUV;
1034 case SkBitmap::kRGB_565_Config:
1035 return Write_16_YUV;
1036 case SkBitmap::kARGB_4444_Config:
1037 return Write_4444_YUV;
1038 case SkBitmap::kIndex8_Config:
1039 return Write_Index_YUV;
1040 default:
1041 return NULL;
1042 }
1043}
1044
1045class SkJPEGImageEncoder : public SkImageEncoder {
1046protected:
1047 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1048#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001049 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001050#endif
1051
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001052 SkAutoLockPixels alp(bm);
1053 if (NULL == bm.getPixels()) {
1054 return false;
1055 }
1056
1057 jpeg_compress_struct cinfo;
1058 skjpeg_error_mgr sk_err;
1059 skjpeg_destination_mgr sk_wstream(stream);
1060
1061 // allocate these before set call setjmp
1062 SkAutoMalloc oneRow;
1063 SkAutoLockColors ctLocker;
1064
1065 cinfo.err = jpeg_std_error(&sk_err);
1066 sk_err.error_exit = skjpeg_error_exit;
1067 if (setjmp(sk_err.fJmpBuf)) {
1068 return false;
1069 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001070
mtklein@google.com8d725b22013-07-24 16:20:05 +00001071 // Keep after setjmp or mark volatile.
1072 const WriteScanline writer = ChooseWriter(bm);
1073 if (NULL == writer) {
1074 return false;
1075 }
1076
1077 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001078 cinfo.dest = &sk_wstream;
1079 cinfo.image_width = bm.width();
1080 cinfo.image_height = bm.height();
1081 cinfo.input_components = 3;
1082#ifdef WE_CONVERT_TO_YUV
1083 cinfo.in_color_space = JCS_YCbCr;
1084#else
1085 cinfo.in_color_space = JCS_RGB;
1086#endif
1087 cinfo.input_gamma = 1;
1088
1089 jpeg_set_defaults(&cinfo);
1090 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001091#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001092 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001093#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001094
1095 jpeg_start_compress(&cinfo, TRUE);
1096
1097 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001098 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001099
1100 const SkPMColor* colors = ctLocker.lockColors(bm);
1101 const void* srcRow = bm.getPixels();
1102
1103 while (cinfo.next_scanline < cinfo.image_height) {
1104 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1105
1106 writer(oneRowP, srcRow, width, colors);
1107 row_pointer[0] = oneRowP;
1108 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1109 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1110 }
1111
1112 jpeg_finish_compress(&cinfo);
1113 jpeg_destroy_compress(&cinfo);
1114
1115 return true;
1116 }
1117};
1118
1119///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001120DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1121DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1122///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001123
scroggo@google.comb5571b32013-09-25 21:34:24 +00001124static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001125 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001126 static const size_t HEADER_SIZE = sizeof(gHeader);
1127
1128 char buffer[HEADER_SIZE];
1129 size_t len = stream->read(buffer, HEADER_SIZE);
1130
1131 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001132 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001133 }
1134 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001135 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001136 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001137 return true;
1138}
1139
scroggo@google.comb5571b32013-09-25 21:34:24 +00001140
1141static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001142 if (is_jpeg(stream)) {
1143 return SkNEW(SkJPEGImageDecoder);
1144 }
1145 return NULL;
1146}
1147
scroggo@google.comb5571b32013-09-25 21:34:24 +00001148static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001149 if (is_jpeg(stream)) {
1150 return SkImageDecoder::kJPEG_Format;
1151 }
1152 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001153}
1154
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001155static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001156 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1157}
1158
mtklein@google.combd6343b2013-09-04 17:20:18 +00001159static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1160static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1161static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);