blob: 2bd48186ac5079431bd5c7832d40c99e6d884ee6 [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:
68 SkJPEGImageIndex(SkStream* 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
djsollen@google.com11399402013-03-20 17:45:27 +0000198 virtual bool onBuildTileIndex(SkStream *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,
288 const SkBitmap& bm, const char msg[]) {
djsollen@google.com11399402013-03-20 17:45:27 +0000289#ifdef SK_DEBUG
scroggo@google.come8f34712013-07-01 14:44:54 +0000290 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n", cinfo.err->msg_code,
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000291 cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
292 bm.width(), bm.height());
293#endif
294 return false; // must always return false
295}
296
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000297// Convert a scanline of CMYK samples to RGBX in place. Note that this
298// method moves the "scanline" pointer in its processing
299static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
300 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000301 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000302 // from easyrgb.com):
303 // CMYK -> CMY
304 // C = ( C * (1 - K) + K ) // for each CMY component
305 // CMY -> RGB
306 // R = ( 1 - C ) * 255 // for each RGB component
307 // Unfortunately we are seeing inverted CMYK so all the original terms
308 // are 1-. This yields:
309 // CMYK -> CMY
310 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
311 // The conversion from CMY->RGB remains the same
312 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
313 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
314 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
315 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
316 scanline[3] = 255;
317 }
318}
319
scroggo@google.com590a5af2013-08-07 21:09:13 +0000320/**
321 * Common code for setting the error manager.
322 */
323static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
324 SkASSERT(cinfo != NULL);
325 SkASSERT(errorManager != NULL);
326 cinfo->err = jpeg_std_error(errorManager);
327 errorManager->error_exit = skjpeg_error_exit;
328}
329
330/**
331 * Common code for turning off upsampling and smoothing. Turning these
332 * off helps performance without showing noticable differences in the
333 * resulting bitmap.
334 */
335static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
336 SkASSERT(cinfo != NULL);
337 /* this gives about 30% performance improvement. In theory it may
338 reduce the visual quality, in practice I'm not seeing a difference
339 */
340 cinfo->do_fancy_upsampling = 0;
341
342 /* this gives another few percents */
343 cinfo->do_block_smoothing = 0;
344}
345
346/**
347 * Common code for setting the dct method.
348 */
349static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
350 SkASSERT(cinfo != NULL);
351#ifdef DCT_IFAST_SUPPORTED
352 if (decoder.getPreferQualityOverSpeed()) {
353 cinfo->dct_method = JDCT_ISLOW;
354 } else {
355 cinfo->dct_method = JDCT_IFAST;
356 }
357#else
358 cinfo->dct_method = JDCT_ISLOW;
359#endif
360}
361
362SkBitmap::Config SkJPEGImageDecoder::getBitmapConfig(jpeg_decompress_struct* cinfo) {
363 SkASSERT(cinfo != NULL);
364
365 SrcDepth srcDepth = k32Bit_SrcDepth;
366 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
367 srcDepth = k8BitGray_SrcDepth;
368 }
369
370 SkBitmap::Config config = this->getPrefConfig(srcDepth, /*hasAlpha*/ false);
371 switch (config) {
372 case SkBitmap::kA8_Config:
373 // Only respect A8 config if the original is grayscale,
374 // in which case we will treat the grayscale as alpha
375 // values.
376 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
377 config = SkBitmap::kARGB_8888_Config;
378 }
379 break;
380 case SkBitmap::kARGB_8888_Config:
381 // Fall through.
382 case SkBitmap::kARGB_4444_Config:
383 // Fall through.
384 case SkBitmap::kRGB_565_Config:
385 // These are acceptable destination configs.
386 break;
387 default:
388 // Force all other configs to 8888.
389 config = SkBitmap::kARGB_8888_Config;
390 break;
391 }
392
393 switch (cinfo->jpeg_color_space) {
394 case JCS_CMYK:
395 // Fall through.
396 case JCS_YCCK:
397 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
398 // so libjpeg will give us CMYK samples back and we will later
399 // manually convert them to RGB
400 cinfo->out_color_space = JCS_CMYK;
401 break;
402 case JCS_GRAYSCALE:
403 if (SkBitmap::kA8_Config == config) {
404 cinfo->out_color_space = JCS_GRAYSCALE;
405 break;
406 }
407 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
408 // config. Fall through to set to the default.
409 default:
410 cinfo->out_color_space = JCS_RGB;
411 break;
412 }
413 return config;
414}
415
416#ifdef ANDROID_RGB
417/**
418 * Based on the config and dither mode, adjust out_color_space and
419 * dither_mode of cinfo.
420 */
421static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
422 SkBitmap::Config config,
423 const SkImageDecoder& decoder) {
424 SkASSERT(cinfo != NULL);
425 cinfo->dither_mode = JDITHER_NONE;
426 if (JCS_CMYK == cinfo->out_color_space) {
427 return;
428 }
429 switch(config) {
430 case SkBitmap::kARGB_8888_Config:
431 cinfo->out_color_space = JCS_RGBA_8888;
432 break;
433 case SkBitmap::kRGB_565_Config:
434 cinfo->out_color_space = JCS_RGB_565;
435 if (decoder.getDitherImage()) {
436 cinfo->dither_mode = JDITHER_ORDERED;
437 }
438 break;
439 default:
440 break;
441 }
442}
443#endif
444
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000445bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
446#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000447 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000448#endif
449
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000450 JPEGAutoClean autoClean;
451
452 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000453 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000454
scroggo@google.com590a5af2013-08-07 21:09:13 +0000455 skjpeg_error_mgr errorManager;
456 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000457
458 // All objects need to be instantiated before this setjmp call so that
459 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000460 if (setjmp(errorManager.fJmpBuf)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000461 return return_false(cinfo, *bm, "setjmp");
462 }
463
scroggo@google.com590a5af2013-08-07 21:09:13 +0000464 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000465 autoClean.set(&cinfo);
466
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000467 int status = jpeg_read_header(&cinfo, true);
468 if (status != JPEG_HEADER_OK) {
469 return return_false(cinfo, *bm, "read_header");
470 }
471
472 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
473 can) much faster that we, just use their num/denom api to approximate
474 the size.
475 */
476 int sampleSize = this->getSampleSize();
477
scroggo@google.com590a5af2013-08-07 21:09:13 +0000478 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000479
scroggo@google.com590a5af2013-08-07 21:09:13 +0000480 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000481 cinfo.scale_denom = sampleSize;
482
scroggo@google.com590a5af2013-08-07 21:09:13 +0000483 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000484
scroggo@google.com590a5af2013-08-07 21:09:13 +0000485 const SkBitmap::Config config = this->getBitmapConfig(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000486
487#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000488 adjust_out_color_space_and_dither(&cinfo, config, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000489#endif
490
djsollen@google.com11399402013-03-20 17:45:27 +0000491 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000492 bm->setConfig(config, cinfo.image_width, cinfo.image_height);
scroggo@google.com1f6b9952013-07-18 19:37:46 +0000493 bm->setIsOpaque(config != SkBitmap::kA8_Config);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000494 return true;
495 }
496
497 /* image_width and image_height are the original dimensions, available
498 after jpeg_read_header(). To see the scaled dimensions, we have to call
499 jpeg_start_decompress(), and then read output_width and output_height.
500 */
501 if (!jpeg_start_decompress(&cinfo)) {
502 /* If we failed here, we may still have enough information to return
503 to the caller if they just wanted (subsampled bounds). If sampleSize
504 was 1, then we would have already returned. Thus we just check if
505 we're in kDecodeBounds_Mode, and that we have valid output sizes.
506
507 One reason to fail here is that we have insufficient stream data
508 to complete the setup. However, output dimensions seem to get
509 computed very early, which is why this special check can pay off.
510 */
djsollen@google.com11399402013-03-20 17:45:27 +0000511 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000512 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
513 recompute_sampleSize(sampleSize, cinfo));
514 bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
scroggo@google.com1f6b9952013-07-18 19:37:46 +0000515 bm->setIsOpaque(config != SkBitmap::kA8_Config);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000516 return true;
517 } else {
518 return return_false(cinfo, *bm, "start_decompress");
519 }
520 }
521 sampleSize = recompute_sampleSize(sampleSize, cinfo);
522
523 // should we allow the Chooser (if present) to pick a config for us???
djsollen@google.com11399402013-03-20 17:45:27 +0000524 if (!this->chooseFromOneChoice(config, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000525 return return_false(cinfo, *bm, "chooseFromOneChoice");
526 }
527
djsollen@google.com11399402013-03-20 17:45:27 +0000528 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000529 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
scroggo@google.com1f6b9952013-07-18 19:37:46 +0000530 bm->setIsOpaque(config != SkBitmap::kA8_Config);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000531 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
532 return true;
533 }
534 if (!this->allocPixelRef(bm, NULL)) {
535 return return_false(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000536 }
537
538 SkAutoLockPixels alp(*bm);
539
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000540#ifdef ANDROID_RGB
541 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
542 a significant performance boost.
543 */
544 if (sampleSize == 1 &&
rmistry@google.comd6176b02012-08-23 18:14:13 +0000545 ((config == SkBitmap::kARGB_8888_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000546 cinfo.out_color_space == JCS_RGBA_8888) ||
rmistry@google.comd6176b02012-08-23 18:14:13 +0000547 (config == SkBitmap::kRGB_565_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000548 cinfo.out_color_space == JCS_RGB_565)))
549 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000550 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000551 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000552
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000553 while (cinfo.output_scanline < cinfo.output_height) {
554 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
555 // if row_count == 0, then we didn't get a scanline, so abort.
556 // if we supported partial images, we might return true in this case
557 if (0 == row_count) {
558 return return_false(cinfo, *bm, "read_scanlines");
559 }
560 if (this->shouldCancelDecode()) {
561 return return_false(cinfo, *bm, "shouldCancelDecode");
562 }
563 rowptr += bpr;
564 }
565 jpeg_finish_decompress(&cinfo);
566 return true;
567 }
568#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000569
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000570 // check for supported formats
571 SkScaledBitmapSampler::SrcConfig sc;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000572 if (JCS_CMYK == cinfo.out_color_space) {
573 // In this case we will manually convert the CMYK values to RGB
574 sc = SkScaledBitmapSampler::kRGBX;
575 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000576 sc = SkScaledBitmapSampler::kRGB;
577#ifdef ANDROID_RGB
578 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
579 sc = SkScaledBitmapSampler::kRGBX;
580 } else if (JCS_RGB_565 == cinfo.out_color_space) {
581 sc = SkScaledBitmapSampler::kRGB_565;
582#endif
583 } else if (1 == cinfo.out_color_components &&
584 JCS_GRAYSCALE == cinfo.out_color_space) {
585 sc = SkScaledBitmapSampler::kGray;
586 } else {
587 return return_false(cinfo, *bm, "jpeg colorspace");
588 }
589
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000590 if (!sampler.begin(bm, sc, this->getDitherImage())) {
591 return return_false(cinfo, *bm, "sampler.begin");
592 }
593
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000594 // The CMYK work-around relies on 4 components per pixel here
djsollen@google.com11399402013-03-20 17:45:27 +0000595 SkAutoMalloc srcStorage(cinfo.output_width * 4);
596 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000597
598 // Possibly skip initial rows [sampler.srcY0]
599 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
600 return return_false(cinfo, *bm, "skip rows");
601 }
602
603 // now loop through scanlines until y == bm->height() - 1
604 for (int y = 0;; y++) {
605 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
606 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
607 if (0 == row_count) {
608 return return_false(cinfo, *bm, "read_scanlines");
609 }
610 if (this->shouldCancelDecode()) {
611 return return_false(cinfo, *bm, "shouldCancelDecode");
612 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000613
614 if (JCS_CMYK == cinfo.out_color_space) {
615 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
616 }
617
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000618 sampler.next(srcRow);
619 if (bm->height() - 1 == y) {
620 // we're done
621 break;
622 }
623
624 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
625 return return_false(cinfo, *bm, "skip rows");
626 }
627 }
628
629 // we formally skip the rest, so we don't get a complaint from libjpeg
630 if (!skip_src_rows(&cinfo, srcRow,
631 cinfo.output_height - cinfo.output_scanline)) {
632 return return_false(cinfo, *bm, "skip rows");
633 }
634 jpeg_finish_decompress(&cinfo);
635
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000636 return true;
637}
638
scroggo@google.comd79277f2013-08-07 19:53:53 +0000639#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000640bool SkJPEGImageDecoder::onBuildTileIndex(SkStream* stream, int *width, int *height) {
641
scroggo@google.coma1a51542013-08-07 21:02:32 +0000642 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000643 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000644
645 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000646 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000647
648 // All objects need to be instantiated before this setjmp call so that
649 // they will be cleaned up properly if an error occurs.
650 if (setjmp(sk_err.fJmpBuf)) {
651 return false;
652 }
653
654 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000655 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000656 return false;
657 }
658
scroggo@google.coma1a51542013-08-07 21:02:32 +0000659 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000660 return false;
661 }
662
663 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000664 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000665
666 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000667 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000668 return false;
669 }
670
scroggo@google.com590a5af2013-08-07 21:09:13 +0000671 // FIXME: This sets cinfo->out_color_space, which we may change later
672 // based on the config in onDecodeSubset. This should be fine, since
673 // jpeg_init_read_tile_scanline will check out_color_space again after
674 // that change (when it calls jinit_color_deconverter).
675 (void) this->getBitmapConfig(cinfo);
676
677 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000678
679 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000680 if (!imageIndex->startTileDecompress()) {
681 return false;
682 }
djsollen@google.com11399402013-03-20 17:45:27 +0000683
scroggo@google.coma1a51542013-08-07 21:02:32 +0000684 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000685 fImageWidth = cinfo->output_width;
686 fImageHeight = cinfo->output_height;
687
688 if (width) {
689 *width = fImageWidth;
690 }
691 if (height) {
692 *height = fImageHeight;
693 }
djsollen@google.com11399402013-03-20 17:45:27 +0000694
695 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000696 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +0000697
698 return true;
699}
700
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000701bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000702 if (NULL == fImageIndex) {
703 return false;
704 }
705 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
706
707 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
708 if (!rect.intersect(region)) {
709 // If the requested region is entirely outside the image return false
710 return false;
711 }
712
713
714 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000715 set_error_mgr(cinfo, &errorManager);
716
djsollen@google.com11399402013-03-20 17:45:27 +0000717 if (setjmp(errorManager.fJmpBuf)) {
718 return false;
719 }
720
721 int requestedSampleSize = this->getSampleSize();
722 cinfo->scale_denom = requestedSampleSize;
723
scroggo@google.com590a5af2013-08-07 21:09:13 +0000724 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000725
scroggo@google.com590a5af2013-08-07 21:09:13 +0000726 const SkBitmap::Config config = this->getBitmapConfig(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000727#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000728 adjust_out_color_space_and_dither(cinfo, config, *this);
djsollen@google.com11399402013-03-20 17:45:27 +0000729#endif
730
731 int startX = rect.fLeft;
732 int startY = rect.fTop;
733 int width = rect.width();
734 int height = rect.height();
735
736 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
737 &startX, &startY, &width, &height);
738 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
739 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
740
741 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
742
743 SkBitmap bitmap;
744 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
745 bitmap.setIsOpaque(true);
746
747 // Check ahead of time if the swap(dest, src) is possible or not.
748 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
749 // swap happening. If no, then we will use alloc to allocate pixels to
750 // prevent garbage collection.
751 int w = rect.width() / actualSampleSize;
752 int h = rect.height() / actualSampleSize;
753 bool swapOnly = (rect == region) && bm->isNull() &&
754 (w == bitmap.width()) && (h == bitmap.height()) &&
755 ((startX - rect.x()) / actualSampleSize == 0) &&
756 ((startY - rect.y()) / actualSampleSize == 0);
757 if (swapOnly) {
758 if (!this->allocPixelRef(&bitmap, NULL)) {
759 return return_false(*cinfo, bitmap, "allocPixelRef");
760 }
761 } else {
762 if (!bitmap.allocPixels()) {
763 return return_false(*cinfo, bitmap, "allocPixels");
764 }
765 }
766
767 SkAutoLockPixels alp(bitmap);
768
769#ifdef ANDROID_RGB
770 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
771 a significant performance boost.
772 */
773 if (skiaSampleSize == 1 &&
774 ((config == SkBitmap::kARGB_8888_Config &&
775 cinfo->out_color_space == JCS_RGBA_8888) ||
776 (config == SkBitmap::kRGB_565_Config &&
777 cinfo->out_color_space == JCS_RGB_565)))
778 {
779 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
780 INT32 const bpr = bitmap.rowBytes();
781 int rowTotalCount = 0;
782
783 while (rowTotalCount < height) {
784 int rowCount = jpeg_read_tile_scanline(cinfo,
785 fImageIndex->huffmanIndex(),
786 &rowptr);
787 // if row_count == 0, then we didn't get a scanline, so abort.
788 // if we supported partial images, we might return true in this case
789 if (0 == rowCount) {
790 return return_false(*cinfo, bitmap, "read_scanlines");
791 }
792 if (this->shouldCancelDecode()) {
793 return return_false(*cinfo, bitmap, "shouldCancelDecode");
794 }
795 rowTotalCount += rowCount;
796 rowptr += bpr;
797 }
798
799 if (swapOnly) {
800 bm->swap(bitmap);
801 } else {
802 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
803 region.width(), region.height(), startX, startY);
804 }
805 return true;
806 }
807#endif
808
809 // check for supported formats
810 SkScaledBitmapSampler::SrcConfig sc;
811 if (JCS_CMYK == cinfo->out_color_space) {
812 // In this case we will manually convert the CMYK values to RGB
813 sc = SkScaledBitmapSampler::kRGBX;
814 } else if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_space) {
815 sc = SkScaledBitmapSampler::kRGB;
816#ifdef ANDROID_RGB
817 } else if (JCS_RGBA_8888 == cinfo->out_color_space) {
818 sc = SkScaledBitmapSampler::kRGBX;
819 } else if (JCS_RGB_565 == cinfo->out_color_space) {
820 sc = SkScaledBitmapSampler::kRGB_565;
821#endif
822 } else if (1 == cinfo->out_color_components &&
823 JCS_GRAYSCALE == cinfo->out_color_space) {
824 sc = SkScaledBitmapSampler::kGray;
825 } else {
826 return return_false(*cinfo, *bm, "jpeg colorspace");
827 }
828
829 if (!sampler.begin(&bitmap, sc, this->getDitherImage())) {
830 return return_false(*cinfo, bitmap, "sampler.begin");
831 }
832
833 // The CMYK work-around relies on 4 components per pixel here
834 SkAutoMalloc srcStorage(width * 4);
835 uint8_t* srcRow = (uint8_t*)srcStorage.get();
836
837 // Possibly skip initial rows [sampler.srcY0]
838 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
839 return return_false(*cinfo, bitmap, "skip rows");
840 }
841
842 // now loop through scanlines until y == bitmap->height() - 1
843 for (int y = 0;; y++) {
844 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
845 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
846 if (0 == row_count) {
847 return return_false(*cinfo, bitmap, "read_scanlines");
848 }
849 if (this->shouldCancelDecode()) {
850 return return_false(*cinfo, bitmap, "shouldCancelDecode");
851 }
852
853 if (JCS_CMYK == cinfo->out_color_space) {
854 convert_CMYK_to_RGB(srcRow, width);
855 }
856
857 sampler.next(srcRow);
858 if (bitmap.height() - 1 == y) {
859 // we're done
860 break;
861 }
862
863 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
864 sampler.srcDY() - 1)) {
865 return return_false(*cinfo, bitmap, "skip rows");
866 }
867 }
868 if (swapOnly) {
869 bm->swap(bitmap);
870 } else {
871 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
872 region.width(), region.height(), startX, startY);
873 }
874 return true;
875}
876#endif
877
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000878///////////////////////////////////////////////////////////////////////////////
879
880#include "SkColorPriv.h"
881
882// taken from jcolor.c in libjpeg
883#if 0 // 16bit - precise but slow
884 #define CYR 19595 // 0.299
885 #define CYG 38470 // 0.587
886 #define CYB 7471 // 0.114
887
888 #define CUR -11059 // -0.16874
889 #define CUG -21709 // -0.33126
890 #define CUB 32768 // 0.5
891
892 #define CVR 32768 // 0.5
893 #define CVG -27439 // -0.41869
894 #define CVB -5329 // -0.08131
895
896 #define CSHIFT 16
897#else // 8bit - fast, slightly less precise
898 #define CYR 77 // 0.299
899 #define CYG 150 // 0.587
900 #define CYB 29 // 0.114
901
902 #define CUR -43 // -0.16874
903 #define CUG -85 // -0.33126
904 #define CUB 128 // 0.5
905
906 #define CVR 128 // 0.5
907 #define CVG -107 // -0.41869
908 #define CVB -21 // -0.08131
909
910 #define CSHIFT 8
911#endif
912
913static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
914 int r = SkGetPackedR32(c);
915 int g = SkGetPackedG32(c);
916 int b = SkGetPackedB32(c);
917
918 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
919 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
920 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
921
922 dst[0] = SkToU8(y);
923 dst[1] = SkToU8(u + 128);
924 dst[2] = SkToU8(v + 128);
925}
926
927static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
928 int r = SkGetPackedR4444(c);
929 int g = SkGetPackedG4444(c);
930 int b = SkGetPackedB4444(c);
931
932 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
933 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
934 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
935
936 dst[0] = SkToU8(y);
937 dst[1] = SkToU8(u + 128);
938 dst[2] = SkToU8(v + 128);
939}
940
941static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
942 int r = SkGetPackedR16(c);
943 int g = SkGetPackedG16(c);
944 int b = SkGetPackedB16(c);
945
946 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
947 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
948 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
949
950 dst[0] = SkToU8(y);
951 dst[1] = SkToU8(u + 128);
952 dst[2] = SkToU8(v + 128);
953}
954
955///////////////////////////////////////////////////////////////////////////////
956
957typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
958 const void* SK_RESTRICT src, int width,
959 const SkPMColor* SK_RESTRICT ctable);
960
961static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
962 const void* SK_RESTRICT srcRow, int width,
963 const SkPMColor*) {
964 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
965 while (--width >= 0) {
966#ifdef WE_CONVERT_TO_YUV
967 rgb2yuv_32(dst, *src++);
968#else
969 uint32_t c = *src++;
970 dst[0] = SkGetPackedR32(c);
971 dst[1] = SkGetPackedG32(c);
972 dst[2] = SkGetPackedB32(c);
973#endif
974 dst += 3;
975 }
976}
977
978static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
979 const void* SK_RESTRICT srcRow, int width,
980 const SkPMColor*) {
981 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
982 while (--width >= 0) {
983#ifdef WE_CONVERT_TO_YUV
984 rgb2yuv_4444(dst, *src++);
985#else
986 SkPMColor16 c = *src++;
987 dst[0] = SkPacked4444ToR32(c);
988 dst[1] = SkPacked4444ToG32(c);
989 dst[2] = SkPacked4444ToB32(c);
990#endif
991 dst += 3;
992 }
993}
994
995static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
996 const void* SK_RESTRICT srcRow, int width,
997 const SkPMColor*) {
998 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
999 while (--width >= 0) {
1000#ifdef WE_CONVERT_TO_YUV
1001 rgb2yuv_16(dst, *src++);
1002#else
1003 uint16_t c = *src++;
1004 dst[0] = SkPacked16ToR32(c);
1005 dst[1] = SkPacked16ToG32(c);
1006 dst[2] = SkPacked16ToB32(c);
1007#endif
1008 dst += 3;
1009 }
1010}
1011
1012static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1013 const void* SK_RESTRICT srcRow, int width,
1014 const SkPMColor* SK_RESTRICT ctable) {
1015 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1016 while (--width >= 0) {
1017#ifdef WE_CONVERT_TO_YUV
1018 rgb2yuv_32(dst, ctable[*src++]);
1019#else
1020 uint32_t c = ctable[*src++];
1021 dst[0] = SkGetPackedR32(c);
1022 dst[1] = SkGetPackedG32(c);
1023 dst[2] = SkGetPackedB32(c);
1024#endif
1025 dst += 3;
1026 }
1027}
1028
1029static WriteScanline ChooseWriter(const SkBitmap& bm) {
1030 switch (bm.config()) {
1031 case SkBitmap::kARGB_8888_Config:
1032 return Write_32_YUV;
1033 case SkBitmap::kRGB_565_Config:
1034 return Write_16_YUV;
1035 case SkBitmap::kARGB_4444_Config:
1036 return Write_4444_YUV;
1037 case SkBitmap::kIndex8_Config:
1038 return Write_Index_YUV;
1039 default:
1040 return NULL;
1041 }
1042}
1043
1044class SkJPEGImageEncoder : public SkImageEncoder {
1045protected:
1046 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1047#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001048 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001049#endif
1050
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001051 SkAutoLockPixels alp(bm);
1052 if (NULL == bm.getPixels()) {
1053 return false;
1054 }
1055
1056 jpeg_compress_struct cinfo;
1057 skjpeg_error_mgr sk_err;
1058 skjpeg_destination_mgr sk_wstream(stream);
1059
1060 // allocate these before set call setjmp
1061 SkAutoMalloc oneRow;
1062 SkAutoLockColors ctLocker;
1063
1064 cinfo.err = jpeg_std_error(&sk_err);
1065 sk_err.error_exit = skjpeg_error_exit;
1066 if (setjmp(sk_err.fJmpBuf)) {
1067 return false;
1068 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001069
mtklein@google.com8d725b22013-07-24 16:20:05 +00001070 // Keep after setjmp or mark volatile.
1071 const WriteScanline writer = ChooseWriter(bm);
1072 if (NULL == writer) {
1073 return false;
1074 }
1075
1076 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001077 cinfo.dest = &sk_wstream;
1078 cinfo.image_width = bm.width();
1079 cinfo.image_height = bm.height();
1080 cinfo.input_components = 3;
1081#ifdef WE_CONVERT_TO_YUV
1082 cinfo.in_color_space = JCS_YCbCr;
1083#else
1084 cinfo.in_color_space = JCS_RGB;
1085#endif
1086 cinfo.input_gamma = 1;
1087
1088 jpeg_set_defaults(&cinfo);
1089 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001090#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001091 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001092#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001093
1094 jpeg_start_compress(&cinfo, TRUE);
1095
1096 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001097 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001098
1099 const SkPMColor* colors = ctLocker.lockColors(bm);
1100 const void* srcRow = bm.getPixels();
1101
1102 while (cinfo.next_scanline < cinfo.image_height) {
1103 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1104
1105 writer(oneRowP, srcRow, width, colors);
1106 row_pointer[0] = oneRowP;
1107 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1108 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1109 }
1110
1111 jpeg_finish_compress(&cinfo);
1112 jpeg_destroy_compress(&cinfo);
1113
1114 return true;
1115 }
1116};
1117
1118///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001119DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1120DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1121///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001122
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001123static bool is_jpeg(SkStream* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001124 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001125 static const size_t HEADER_SIZE = sizeof(gHeader);
1126
1127 char buffer[HEADER_SIZE];
1128 size_t len = stream->read(buffer, HEADER_SIZE);
1129
1130 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001131 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001132 }
1133 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001134 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001135 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001136 return true;
1137}
1138
1139#include "SkTRegistry.h"
1140
1141static SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) {
1142 if (is_jpeg(stream)) {
1143 return SkNEW(SkJPEGImageDecoder);
1144 }
1145 return NULL;
1146}
1147
1148static SkImageDecoder::Format get_format_jpeg(SkStream* stream) {
1149 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
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001159
1160static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory);
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001161static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_jpeg);
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001162static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efactory);