blob: 7652249f5eb0a0bb0f30f94bcb78b3af2fdf6bba [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
halcanary@google.comfed30372013-10-04 12:46:45 +000022#if defined(SK_DEBUG)
23#include "SkRTConf.h" // SK_CONF_DECLARE
24#endif // defined(SK_DEBUG)
25
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000026#include <stdio.h>
27extern "C" {
28 #include "jpeglib.h"
29 #include "jerror.h"
30}
31
djsollen@google.com11399402013-03-20 17:45:27 +000032// These enable timing code that report milliseconds for an encoding/decoding
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000033//#define TIME_ENCODE
34//#define TIME_DECODE
35
36// this enables our rgb->yuv code, which is faster than libjpeg on ARM
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000037#define WE_CONVERT_TO_YUV
38
djsollen@google.com11399402013-03-20 17:45:27 +000039// If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers
40// support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565.
41
halcanary@google.comfed30372013-10-04 12:46:45 +000042#if defined(SK_DEBUG)
43SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderWarnings,
44 "images.jpeg.suppressDecoderWarnings", false,
45 "Suppress most JPG warnings when calling decode functions.");
46#endif // defined(SK_DEBUG)
47
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000048//////////////////////////////////////////////////////////////////////////
49//////////////////////////////////////////////////////////////////////////
50
scroggo@google.com590a5af2013-08-07 21:09:13 +000051static void overwrite_mem_buffer_size(jpeg_decompress_struct* cinfo) {
djsollen@google.com11399402013-03-20 17:45:27 +000052#ifdef SK_BUILD_FOR_ANDROID
53 /* Check if the device indicates that it has a large amount of system memory
54 * if so, increase the memory allocation to 30MB instead of the default 5MB.
55 */
56#ifdef ANDROID_LARGE_MEMORY_DEVICE
57 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
58#else
59 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
60#endif
61#endif // SK_BUILD_FOR_ANDROID
62}
63
64//////////////////////////////////////////////////////////////////////////
65//////////////////////////////////////////////////////////////////////////
66
halcanary@google.comfed30372013-10-04 12:46:45 +000067static void do_nothing_emit_message(jpeg_common_struct*, int) {
68 /* do nothing */
69}
70
scroggo@google.com590a5af2013-08-07 21:09:13 +000071static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* src_mgr) {
72 SkASSERT(cinfo != NULL);
73 SkASSERT(src_mgr != NULL);
74 jpeg_create_decompress(cinfo);
75 overwrite_mem_buffer_size(cinfo);
76 cinfo->src = src_mgr;
halcanary@google.comfed30372013-10-04 12:46:45 +000077#if defined(SK_DEBUG)
78 /* To suppress warnings with a SK_DEBUG binary, set the
79 * environment variable "skia_images_jpeg_suppressDecoderWarnings"
80 * to "true". Inside a program that links to skia:
81 * SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); */
82 if (c_suppressJPEGImageDecoderWarnings) {
83 cinfo->err->emit_message = &do_nothing_emit_message;
84 }
85#else // Always suppress in release mode.
86 cinfo->err->emit_message = &do_nothing_emit_message;
87#endif // defined(SK_DEBUG)
scroggo@google.com590a5af2013-08-07 21:09:13 +000088}
89
scroggo@google.coma1a51542013-08-07 21:02:32 +000090#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +000091class SkJPEGImageIndex {
92public:
scroggo@google.comb5571b32013-09-25 21:34:24 +000093 SkJPEGImageIndex(SkStreamRewindable* stream, SkImageDecoder* decoder)
scroggo@google.coma1a51542013-08-07 21:02:32 +000094 : fSrcMgr(stream, decoder)
95 , fInfoInitialized(false)
96 , fHuffmanCreated(false)
97 , fDecompressStarted(false)
98 {
99 SkDEBUGCODE(fReadHeaderSucceeded = false;)
100 }
djsollen@google.com11399402013-03-20 17:45:27 +0000101
102 ~SkJPEGImageIndex() {
scroggo@google.coma1a51542013-08-07 21:02:32 +0000103 if (fHuffmanCreated) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000104 // Set to false before calling the libjpeg function, in case
105 // the libjpeg function calls longjmp. Our setjmp handler may
106 // attempt to delete this SkJPEGImageIndex, thus entering this
107 // destructor again. Setting fHuffmanCreated to false first
108 // prevents an infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000109 fHuffmanCreated = false;
110 jpeg_destroy_huffman_index(&fHuffmanIndex);
111 }
112 if (fDecompressStarted) {
scroggo@google.com590a5af2013-08-07 21:09:13 +0000113 // Like fHuffmanCreated, set to false before calling libjpeg
114 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000115 fDecompressStarted = false;
116 jpeg_finish_decompress(&fCInfo);
117 }
118 if (fInfoInitialized) {
119 this->destroyInfo();
120 }
djsollen@google.com11399402013-03-20 17:45:27 +0000121 }
122
123 /**
scroggo@google.coma1a51542013-08-07 21:02:32 +0000124 * Destroy the cinfo struct.
125 * After this call, if a huffman index was already built, it
126 * can be used after calling initializeInfoAndReadHeader
127 * again. Must not be called after startTileDecompress except
128 * in the destructor.
djsollen@google.com11399402013-03-20 17:45:27 +0000129 */
scroggo@google.coma1a51542013-08-07 21:02:32 +0000130 void destroyInfo() {
131 SkASSERT(fInfoInitialized);
132 SkASSERT(!fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000133 // Like fHuffmanCreated, set to false before calling libjpeg
134 // function to prevent potential infinite loop.
scroggo@google.coma1a51542013-08-07 21:02:32 +0000135 fInfoInitialized = false;
136 jpeg_destroy_decompress(&fCInfo);
137 SkDEBUGCODE(fReadHeaderSucceeded = false;)
138 }
139
140 /**
141 * Initialize the cinfo struct.
142 * Calls jpeg_create_decompress, makes customizations, and
143 * finally calls jpeg_read_header. Returns true if jpeg_read_header
144 * returns JPEG_HEADER_OK.
145 * If cinfo was already initialized, destroyInfo must be called to
146 * destroy the old one. Must not be called after startTileDecompress.
147 */
148 bool initializeInfoAndReadHeader() {
149 SkASSERT(!fInfoInitialized && !fDecompressStarted);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000150 initialize_info(&fCInfo, &fSrcMgr);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000151 fInfoInitialized = true;
152 const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true));
153 SkDEBUGCODE(fReadHeaderSucceeded = success;)
154 return success;
djsollen@google.com11399402013-03-20 17:45:27 +0000155 }
156
157 jpeg_decompress_struct* cinfo() { return &fCInfo; }
158
djsollen@google.com11399402013-03-20 17:45:27 +0000159 huffman_index* huffmanIndex() { return &fHuffmanIndex; }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000160
161 /**
162 * Build the index to be used for tile based decoding.
163 * Must only be called after a successful call to
164 * initializeInfoAndReadHeader and must not be called more
165 * than once.
166 */
167 bool buildHuffmanIndex() {
168 SkASSERT(fReadHeaderSucceeded);
169 SkASSERT(!fHuffmanCreated);
170 jpeg_create_huffman_index(&fCInfo, &fHuffmanIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000171 SkASSERT(1 == fCInfo.scale_num && 1 == fCInfo.scale_denom);
scroggo@google.com57a52982013-08-27 20:42:22 +0000172 fHuffmanCreated = jpeg_build_huffman_index(&fCInfo, &fHuffmanIndex);
173 return fHuffmanCreated;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000174 }
175
176 /**
177 * Start tile based decoding. Must only be called after a
178 * successful call to buildHuffmanIndex, and must only be
179 * called once.
180 */
181 bool startTileDecompress() {
182 SkASSERT(fHuffmanCreated);
183 SkASSERT(fReadHeaderSucceeded);
184 SkASSERT(!fDecompressStarted);
185 if (jpeg_start_tile_decompress(&fCInfo)) {
186 fDecompressStarted = true;
187 return true;
188 }
189 return false;
190 }
djsollen@google.com11399402013-03-20 17:45:27 +0000191
192private:
193 skjpeg_source_mgr fSrcMgr;
194 jpeg_decompress_struct fCInfo;
djsollen@google.com11399402013-03-20 17:45:27 +0000195 huffman_index fHuffmanIndex;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000196 bool fInfoInitialized;
197 bool fHuffmanCreated;
198 bool fDecompressStarted;
199 SkDEBUGCODE(bool fReadHeaderSucceeded;)
djsollen@google.com11399402013-03-20 17:45:27 +0000200};
scroggo@google.coma1a51542013-08-07 21:02:32 +0000201#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000202
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000203class SkJPEGImageDecoder : public SkImageDecoder {
204public:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000205#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000206 SkJPEGImageDecoder() {
207 fImageIndex = NULL;
208 fImageWidth = 0;
209 fImageHeight = 0;
210 }
211
212 virtual ~SkJPEGImageDecoder() {
213 SkDELETE(fImageIndex);
214 }
scroggo@google.coma1a51542013-08-07 21:02:32 +0000215#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000216
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000217 virtual Format getFormat() const {
218 return kJPEG_Format;
219 }
220
221protected:
scroggo@google.comd79277f2013-08-07 19:53:53 +0000222#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000223 virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000224 virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
djsollen@google.com11399402013-03-20 17:45:27 +0000225#endif
226 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
227
228private:
scroggo@google.coma1a51542013-08-07 21:02:32 +0000229#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000230 SkJPEGImageIndex* fImageIndex;
231 int fImageWidth;
232 int fImageHeight;
scroggo@google.coma1a51542013-08-07 21:02:32 +0000233#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000234
scroggo@google.com590a5af2013-08-07 21:09:13 +0000235 /**
236 * Determine the appropriate bitmap config and out_color_space based on
237 * both the preference of the caller and the jpeg_color_space on the
238 * jpeg_decompress_struct passed in.
239 * Must be called after jpeg_read_header.
240 */
241 SkBitmap::Config getBitmapConfig(jpeg_decompress_struct*);
242
djsollen@google.com11399402013-03-20 17:45:27 +0000243 typedef SkImageDecoder INHERITED;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000244};
245
246//////////////////////////////////////////////////////////////////////////
247
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000248/* Automatically clean up after throwing an exception */
249class JPEGAutoClean {
250public:
251 JPEGAutoClean(): cinfo_ptr(NULL) {}
252 ~JPEGAutoClean() {
253 if (cinfo_ptr) {
254 jpeg_destroy_decompress(cinfo_ptr);
255 }
256 }
257 void set(jpeg_decompress_struct* info) {
258 cinfo_ptr = info;
259 }
260private:
261 jpeg_decompress_struct* cinfo_ptr;
262};
263
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000264///////////////////////////////////////////////////////////////////////////////
265
266/* If we need to better match the request, we might examine the image and
267 output dimensions, and determine if the downsampling jpeg provided is
268 not sufficient. If so, we can recompute a modified sampleSize value to
269 make up the difference.
270
271 To skip this additional scaling, just set sampleSize = 1; below.
272 */
273static int recompute_sampleSize(int sampleSize,
274 const jpeg_decompress_struct& cinfo) {
275 return sampleSize * cinfo.output_width / cinfo.image_width;
276}
277
278static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
279 /* These are initialized to 0, so if they have non-zero values, we assume
280 they are "valid" (i.e. have been computed by libjpeg)
281 */
djsollen@google.com11399402013-03-20 17:45:27 +0000282 return 0 != cinfo.output_width && 0 != cinfo.output_height;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000283}
284
djsollen@google.com11399402013-03-20 17:45:27 +0000285static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000286 for (int i = 0; i < count; i++) {
287 JSAMPLE* rowptr = (JSAMPLE*)buffer;
288 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
djsollen@google.com11399402013-03-20 17:45:27 +0000289 if (1 != row_count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000290 return false;
291 }
292 }
293 return true;
294}
295
scroggo@google.comd79277f2013-08-07 19:53:53 +0000296#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com11399402013-03-20 17:45:27 +0000297static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
298 huffman_index *index, void* buffer, int count) {
299 for (int i = 0; i < count; i++) {
300 JSAMPLE* rowptr = (JSAMPLE*)buffer;
301 int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
302 if (1 != row_count) {
303 return false;
304 }
305 }
306 return true;
307}
308#endif
309
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000310// This guy exists just to aid in debugging, as it allows debuggers to just
311// set a break-point in one place to see all error exists.
312static bool return_false(const jpeg_decompress_struct& cinfo,
scroggo@google.com228f2b82013-09-25 21:27:31 +0000313 const SkBitmap& bm, const char caller[]) {
djsollen@google.com11399402013-03-20 17:45:27 +0000314#ifdef SK_DEBUG
scroggo@google.com228f2b82013-09-25 21:27:31 +0000315 char buffer[JMSG_LENGTH_MAX];
316 cinfo.err->format_message((const j_common_ptr)&cinfo, buffer);
scroggo@google.come8f34712013-07-01 14:44:54 +0000317 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n", cinfo.err->msg_code,
scroggo@google.com228f2b82013-09-25 21:27:31 +0000318 buffer, caller, bm.width(), bm.height());
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000319#endif
320 return false; // must always return false
321}
322
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000323// Convert a scanline of CMYK samples to RGBX in place. Note that this
324// method moves the "scanline" pointer in its processing
325static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
326 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000327 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000328 // from easyrgb.com):
329 // CMYK -> CMY
330 // C = ( C * (1 - K) + K ) // for each CMY component
331 // CMY -> RGB
332 // R = ( 1 - C ) * 255 // for each RGB component
333 // Unfortunately we are seeing inverted CMYK so all the original terms
334 // are 1-. This yields:
335 // CMYK -> CMY
336 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
337 // The conversion from CMY->RGB remains the same
338 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
339 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
340 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
341 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
342 scanline[3] = 255;
343 }
344}
345
scroggo@google.com590a5af2013-08-07 21:09:13 +0000346/**
347 * Common code for setting the error manager.
348 */
349static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) {
350 SkASSERT(cinfo != NULL);
351 SkASSERT(errorManager != NULL);
352 cinfo->err = jpeg_std_error(errorManager);
353 errorManager->error_exit = skjpeg_error_exit;
354}
355
356/**
357 * Common code for turning off upsampling and smoothing. Turning these
358 * off helps performance without showing noticable differences in the
359 * resulting bitmap.
360 */
361static void turn_off_visual_optimizations(jpeg_decompress_struct* cinfo) {
362 SkASSERT(cinfo != NULL);
363 /* this gives about 30% performance improvement. In theory it may
364 reduce the visual quality, in practice I'm not seeing a difference
365 */
366 cinfo->do_fancy_upsampling = 0;
367
368 /* this gives another few percents */
369 cinfo->do_block_smoothing = 0;
370}
371
372/**
373 * Common code for setting the dct method.
374 */
375static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) {
376 SkASSERT(cinfo != NULL);
377#ifdef DCT_IFAST_SUPPORTED
378 if (decoder.getPreferQualityOverSpeed()) {
379 cinfo->dct_method = JDCT_ISLOW;
380 } else {
381 cinfo->dct_method = JDCT_IFAST;
382 }
383#else
384 cinfo->dct_method = JDCT_ISLOW;
385#endif
386}
387
388SkBitmap::Config SkJPEGImageDecoder::getBitmapConfig(jpeg_decompress_struct* cinfo) {
389 SkASSERT(cinfo != NULL);
390
391 SrcDepth srcDepth = k32Bit_SrcDepth;
392 if (JCS_GRAYSCALE == cinfo->jpeg_color_space) {
393 srcDepth = k8BitGray_SrcDepth;
394 }
395
396 SkBitmap::Config config = this->getPrefConfig(srcDepth, /*hasAlpha*/ false);
397 switch (config) {
398 case SkBitmap::kA8_Config:
399 // Only respect A8 config if the original is grayscale,
400 // in which case we will treat the grayscale as alpha
401 // values.
402 if (cinfo->jpeg_color_space != JCS_GRAYSCALE) {
403 config = SkBitmap::kARGB_8888_Config;
404 }
405 break;
406 case SkBitmap::kARGB_8888_Config:
407 // Fall through.
408 case SkBitmap::kARGB_4444_Config:
409 // Fall through.
410 case SkBitmap::kRGB_565_Config:
411 // These are acceptable destination configs.
412 break;
413 default:
414 // Force all other configs to 8888.
415 config = SkBitmap::kARGB_8888_Config;
416 break;
417 }
418
419 switch (cinfo->jpeg_color_space) {
420 case JCS_CMYK:
421 // Fall through.
422 case JCS_YCCK:
423 // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up
424 // so libjpeg will give us CMYK samples back and we will later
425 // manually convert them to RGB
426 cinfo->out_color_space = JCS_CMYK;
427 break;
428 case JCS_GRAYSCALE:
429 if (SkBitmap::kA8_Config == config) {
430 cinfo->out_color_space = JCS_GRAYSCALE;
431 break;
432 }
433 // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB
434 // config. Fall through to set to the default.
435 default:
436 cinfo->out_color_space = JCS_RGB;
437 break;
438 }
439 return config;
440}
441
442#ifdef ANDROID_RGB
443/**
444 * Based on the config and dither mode, adjust out_color_space and
445 * dither_mode of cinfo.
446 */
447static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
448 SkBitmap::Config config,
449 const SkImageDecoder& decoder) {
450 SkASSERT(cinfo != NULL);
451 cinfo->dither_mode = JDITHER_NONE;
452 if (JCS_CMYK == cinfo->out_color_space) {
453 return;
454 }
455 switch(config) {
456 case SkBitmap::kARGB_8888_Config:
457 cinfo->out_color_space = JCS_RGBA_8888;
458 break;
459 case SkBitmap::kRGB_565_Config:
460 cinfo->out_color_space = JCS_RGB_565;
461 if (decoder.getDitherImage()) {
462 cinfo->dither_mode = JDITHER_ORDERED;
463 }
464 break;
465 default:
466 break;
467 }
468}
469#endif
470
halcanary@google.comfed30372013-10-04 12:46:45 +0000471
472/**
473 Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
474 Used when decoding fails partway through reading scanlines to fill
475 remaining lines. */
476static void fill_below_level(int y, SkBitmap* bitmap) {
477 SkRect rect = SkRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
478 SkCanvas canvas(*bitmap);
479 canvas.clipRect(rect);
480 canvas.drawColor(SK_ColorWHITE);
481}
482
483
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000484bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
485#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000486 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000487#endif
488
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000489 JPEGAutoClean autoClean;
490
491 jpeg_decompress_struct cinfo;
scroggo@google.comd4c35652013-08-01 15:03:42 +0000492 skjpeg_source_mgr srcManager(stream, this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000493
scroggo@google.com590a5af2013-08-07 21:09:13 +0000494 skjpeg_error_mgr errorManager;
495 set_error_mgr(&cinfo, &errorManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000496
497 // All objects need to be instantiated before this setjmp call so that
498 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000499 if (setjmp(errorManager.fJmpBuf)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000500 return return_false(cinfo, *bm, "setjmp");
501 }
502
scroggo@google.com590a5af2013-08-07 21:09:13 +0000503 initialize_info(&cinfo, &srcManager);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000504 autoClean.set(&cinfo);
505
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000506 int status = jpeg_read_header(&cinfo, true);
507 if (status != JPEG_HEADER_OK) {
508 return return_false(cinfo, *bm, "read_header");
509 }
510
511 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
512 can) much faster that we, just use their num/denom api to approximate
513 the size.
514 */
515 int sampleSize = this->getSampleSize();
516
scroggo@google.com590a5af2013-08-07 21:09:13 +0000517 set_dct_method(*this, &cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000518
scroggo@google.com590a5af2013-08-07 21:09:13 +0000519 SkASSERT(1 == cinfo.scale_num);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000520 cinfo.scale_denom = sampleSize;
521
scroggo@google.com590a5af2013-08-07 21:09:13 +0000522 turn_off_visual_optimizations(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000523
scroggo@google.com590a5af2013-08-07 21:09:13 +0000524 const SkBitmap::Config config = this->getBitmapConfig(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000525
526#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000527 adjust_out_color_space_and_dither(&cinfo, config, *this);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000528#endif
529
djsollen@google.com11399402013-03-20 17:45:27 +0000530 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000531 bm->setConfig(config, cinfo.image_width, cinfo.image_height);
scroggo@google.com1f6b9952013-07-18 19:37:46 +0000532 bm->setIsOpaque(config != SkBitmap::kA8_Config);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000533 return true;
534 }
535
536 /* image_width and image_height are the original dimensions, available
537 after jpeg_read_header(). To see the scaled dimensions, we have to call
538 jpeg_start_decompress(), and then read output_width and output_height.
539 */
540 if (!jpeg_start_decompress(&cinfo)) {
541 /* If we failed here, we may still have enough information to return
542 to the caller if they just wanted (subsampled bounds). If sampleSize
543 was 1, then we would have already returned. Thus we just check if
544 we're in kDecodeBounds_Mode, and that we have valid output sizes.
545
546 One reason to fail here is that we have insufficient stream data
547 to complete the setup. However, output dimensions seem to get
548 computed very early, which is why this special check can pay off.
549 */
djsollen@google.com11399402013-03-20 17:45:27 +0000550 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000551 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
552 recompute_sampleSize(sampleSize, cinfo));
553 bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
scroggo@google.com1f6b9952013-07-18 19:37:46 +0000554 bm->setIsOpaque(config != SkBitmap::kA8_Config);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000555 return true;
556 } else {
557 return return_false(cinfo, *bm, "start_decompress");
558 }
559 }
560 sampleSize = recompute_sampleSize(sampleSize, cinfo);
561
562 // should we allow the Chooser (if present) to pick a config for us???
djsollen@google.com11399402013-03-20 17:45:27 +0000563 if (!this->chooseFromOneChoice(config, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000564 return return_false(cinfo, *bm, "chooseFromOneChoice");
565 }
566
djsollen@google.com11399402013-03-20 17:45:27 +0000567 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000568 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
scroggo@google.com1f6b9952013-07-18 19:37:46 +0000569 bm->setIsOpaque(config != SkBitmap::kA8_Config);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000570 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
571 return true;
572 }
573 if (!this->allocPixelRef(bm, NULL)) {
574 return return_false(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000575 }
576
577 SkAutoLockPixels alp(*bm);
578
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000579#ifdef ANDROID_RGB
580 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
581 a significant performance boost.
582 */
583 if (sampleSize == 1 &&
rmistry@google.comd6176b02012-08-23 18:14:13 +0000584 ((config == SkBitmap::kARGB_8888_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000585 cinfo.out_color_space == JCS_RGBA_8888) ||
rmistry@google.comd6176b02012-08-23 18:14:13 +0000586 (config == SkBitmap::kRGB_565_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000587 cinfo.out_color_space == JCS_RGB_565)))
588 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000589 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000590 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000591
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000592 while (cinfo.output_scanline < cinfo.output_height) {
593 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000594 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000595 // if row_count == 0, then we didn't get a scanline,
596 // so return early. We will return a partial image.
597 fill_below_level(cinfo.output_scanline, bm);
598 cinfo.output_scanline = cinfo.output_height;
599 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000600 }
601 if (this->shouldCancelDecode()) {
602 return return_false(cinfo, *bm, "shouldCancelDecode");
603 }
604 rowptr += bpr;
605 }
606 jpeg_finish_decompress(&cinfo);
607 return true;
608 }
609#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000610
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000611 // check for supported formats
612 SkScaledBitmapSampler::SrcConfig sc;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000613 if (JCS_CMYK == cinfo.out_color_space) {
614 // In this case we will manually convert the CMYK values to RGB
615 sc = SkScaledBitmapSampler::kRGBX;
616 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000617 sc = SkScaledBitmapSampler::kRGB;
618#ifdef ANDROID_RGB
619 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
620 sc = SkScaledBitmapSampler::kRGBX;
621 } else if (JCS_RGB_565 == cinfo.out_color_space) {
622 sc = SkScaledBitmapSampler::kRGB_565;
623#endif
624 } else if (1 == cinfo.out_color_components &&
625 JCS_GRAYSCALE == cinfo.out_color_space) {
626 sc = SkScaledBitmapSampler::kGray;
627 } else {
628 return return_false(cinfo, *bm, "jpeg colorspace");
629 }
630
scroggo@google.com8d239242013-10-01 17:27:15 +0000631 if (!sampler.begin(bm, sc, *this)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000632 return return_false(cinfo, *bm, "sampler.begin");
633 }
634
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000635 // The CMYK work-around relies on 4 components per pixel here
djsollen@google.com11399402013-03-20 17:45:27 +0000636 SkAutoMalloc srcStorage(cinfo.output_width * 4);
637 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000638
639 // Possibly skip initial rows [sampler.srcY0]
640 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
641 return return_false(cinfo, *bm, "skip rows");
642 }
643
644 // now loop through scanlines until y == bm->height() - 1
645 for (int y = 0;; y++) {
646 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
647 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
648 if (0 == row_count) {
halcanary@google.comfed30372013-10-04 12:46:45 +0000649 // if row_count == 0, then we didn't get a scanline,
650 // so return early. We will return a partial image.
651 fill_below_level(y, bm);
652 cinfo.output_scanline = cinfo.output_height;
653 break; // Skip to jpeg_finish_decompress()
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000654 }
655 if (this->shouldCancelDecode()) {
656 return return_false(cinfo, *bm, "shouldCancelDecode");
657 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000658
659 if (JCS_CMYK == cinfo.out_color_space) {
660 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
661 }
662
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000663 sampler.next(srcRow);
664 if (bm->height() - 1 == y) {
665 // we're done
666 break;
667 }
668
669 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
670 return return_false(cinfo, *bm, "skip rows");
671 }
672 }
673
674 // we formally skip the rest, so we don't get a complaint from libjpeg
675 if (!skip_src_rows(&cinfo, srcRow,
676 cinfo.output_height - cinfo.output_scanline)) {
677 return return_false(cinfo, *bm, "skip rows");
678 }
679 jpeg_finish_decompress(&cinfo);
680
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000681 return true;
682}
683
scroggo@google.comd79277f2013-08-07 19:53:53 +0000684#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comb5571b32013-09-25 21:34:24 +0000685bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
djsollen@google.com11399402013-03-20 17:45:27 +0000686
scroggo@google.coma1a51542013-08-07 21:02:32 +0000687 SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));
djsollen@google.com11399402013-03-20 17:45:27 +0000688 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000689
690 skjpeg_error_mgr sk_err;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000691 set_error_mgr(cinfo, &sk_err);
djsollen@google.com11399402013-03-20 17:45:27 +0000692
693 // All objects need to be instantiated before this setjmp call so that
694 // they will be cleaned up properly if an error occurs.
695 if (setjmp(sk_err.fJmpBuf)) {
696 return false;
697 }
698
699 // create the cinfo used to create/build the huffmanIndex
scroggo@google.coma1a51542013-08-07 21:02:32 +0000700 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000701 return false;
702 }
703
scroggo@google.coma1a51542013-08-07 21:02:32 +0000704 if (!imageIndex->buildHuffmanIndex()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000705 return false;
706 }
707
708 // destroy the cinfo used to create/build the huffman index
scroggo@google.coma1a51542013-08-07 21:02:32 +0000709 imageIndex->destroyInfo();
djsollen@google.com11399402013-03-20 17:45:27 +0000710
711 // Init decoder to image decode mode
scroggo@google.coma1a51542013-08-07 21:02:32 +0000712 if (!imageIndex->initializeInfoAndReadHeader()) {
djsollen@google.com11399402013-03-20 17:45:27 +0000713 return false;
714 }
715
scroggo@google.com590a5af2013-08-07 21:09:13 +0000716 // FIXME: This sets cinfo->out_color_space, which we may change later
717 // based on the config in onDecodeSubset. This should be fine, since
718 // jpeg_init_read_tile_scanline will check out_color_space again after
719 // that change (when it calls jinit_color_deconverter).
720 (void) this->getBitmapConfig(cinfo);
721
722 turn_off_visual_optimizations(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000723
724 // instead of jpeg_start_decompress() we start a tiled decompress
scroggo@google.coma1a51542013-08-07 21:02:32 +0000725 if (!imageIndex->startTileDecompress()) {
726 return false;
727 }
djsollen@google.com11399402013-03-20 17:45:27 +0000728
scroggo@google.coma1a51542013-08-07 21:02:32 +0000729 SkASSERT(1 == cinfo->scale_num);
scroggo@google.com590a5af2013-08-07 21:09:13 +0000730 fImageWidth = cinfo->output_width;
731 fImageHeight = cinfo->output_height;
732
733 if (width) {
734 *width = fImageWidth;
735 }
736 if (height) {
737 *height = fImageHeight;
738 }
djsollen@google.com11399402013-03-20 17:45:27 +0000739
740 SkDELETE(fImageIndex);
scroggo@google.coma1a51542013-08-07 21:02:32 +0000741 fImageIndex = imageIndex.detach();
djsollen@google.com11399402013-03-20 17:45:27 +0000742
743 return true;
744}
745
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000746bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000747 if (NULL == fImageIndex) {
748 return false;
749 }
750 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
751
752 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
753 if (!rect.intersect(region)) {
754 // If the requested region is entirely outside the image return false
755 return false;
756 }
757
758
759 skjpeg_error_mgr errorManager;
scroggo@google.com590a5af2013-08-07 21:09:13 +0000760 set_error_mgr(cinfo, &errorManager);
761
djsollen@google.com11399402013-03-20 17:45:27 +0000762 if (setjmp(errorManager.fJmpBuf)) {
763 return false;
764 }
765
766 int requestedSampleSize = this->getSampleSize();
767 cinfo->scale_denom = requestedSampleSize;
768
scroggo@google.com590a5af2013-08-07 21:09:13 +0000769 set_dct_method(*this, cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000770
scroggo@google.com590a5af2013-08-07 21:09:13 +0000771 const SkBitmap::Config config = this->getBitmapConfig(cinfo);
djsollen@google.com11399402013-03-20 17:45:27 +0000772#ifdef ANDROID_RGB
scroggo@google.com590a5af2013-08-07 21:09:13 +0000773 adjust_out_color_space_and_dither(cinfo, config, *this);
djsollen@google.com11399402013-03-20 17:45:27 +0000774#endif
775
776 int startX = rect.fLeft;
777 int startY = rect.fTop;
778 int width = rect.width();
779 int height = rect.height();
780
781 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
782 &startX, &startY, &width, &height);
783 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
784 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
785
786 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
787
788 SkBitmap bitmap;
789 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
790 bitmap.setIsOpaque(true);
791
792 // Check ahead of time if the swap(dest, src) is possible or not.
793 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
794 // swap happening. If no, then we will use alloc to allocate pixels to
795 // prevent garbage collection.
796 int w = rect.width() / actualSampleSize;
797 int h = rect.height() / actualSampleSize;
798 bool swapOnly = (rect == region) && bm->isNull() &&
799 (w == bitmap.width()) && (h == bitmap.height()) &&
800 ((startX - rect.x()) / actualSampleSize == 0) &&
801 ((startY - rect.y()) / actualSampleSize == 0);
802 if (swapOnly) {
803 if (!this->allocPixelRef(&bitmap, NULL)) {
804 return return_false(*cinfo, bitmap, "allocPixelRef");
805 }
806 } else {
807 if (!bitmap.allocPixels()) {
808 return return_false(*cinfo, bitmap, "allocPixels");
809 }
810 }
811
812 SkAutoLockPixels alp(bitmap);
813
814#ifdef ANDROID_RGB
815 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
816 a significant performance boost.
817 */
818 if (skiaSampleSize == 1 &&
819 ((config == SkBitmap::kARGB_8888_Config &&
820 cinfo->out_color_space == JCS_RGBA_8888) ||
821 (config == SkBitmap::kRGB_565_Config &&
822 cinfo->out_color_space == JCS_RGB_565)))
823 {
824 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
825 INT32 const bpr = bitmap.rowBytes();
826 int rowTotalCount = 0;
827
828 while (rowTotalCount < height) {
829 int rowCount = jpeg_read_tile_scanline(cinfo,
830 fImageIndex->huffmanIndex(),
831 &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +0000832 // if rowCount == 0, then we didn't get a scanline, so abort.
833 // onDecodeSubset() relies on onBuildTileIndex(), which
834 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +0000835 if (0 == rowCount) {
836 return return_false(*cinfo, bitmap, "read_scanlines");
837 }
838 if (this->shouldCancelDecode()) {
839 return return_false(*cinfo, bitmap, "shouldCancelDecode");
840 }
841 rowTotalCount += rowCount;
842 rowptr += bpr;
843 }
844
845 if (swapOnly) {
846 bm->swap(bitmap);
847 } else {
848 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
849 region.width(), region.height(), startX, startY);
850 }
851 return true;
852 }
853#endif
854
855 // check for supported formats
856 SkScaledBitmapSampler::SrcConfig sc;
857 if (JCS_CMYK == cinfo->out_color_space) {
858 // In this case we will manually convert the CMYK values to RGB
859 sc = SkScaledBitmapSampler::kRGBX;
860 } else if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_space) {
861 sc = SkScaledBitmapSampler::kRGB;
862#ifdef ANDROID_RGB
863 } else if (JCS_RGBA_8888 == cinfo->out_color_space) {
864 sc = SkScaledBitmapSampler::kRGBX;
865 } else if (JCS_RGB_565 == cinfo->out_color_space) {
866 sc = SkScaledBitmapSampler::kRGB_565;
867#endif
868 } else if (1 == cinfo->out_color_components &&
869 JCS_GRAYSCALE == cinfo->out_color_space) {
870 sc = SkScaledBitmapSampler::kGray;
871 } else {
872 return return_false(*cinfo, *bm, "jpeg colorspace");
873 }
874
scroggo@google.com8d239242013-10-01 17:27:15 +0000875 if (!sampler.begin(&bitmap, sc, *this)) {
djsollen@google.com11399402013-03-20 17:45:27 +0000876 return return_false(*cinfo, bitmap, "sampler.begin");
877 }
878
879 // The CMYK work-around relies on 4 components per pixel here
880 SkAutoMalloc srcStorage(width * 4);
881 uint8_t* srcRow = (uint8_t*)srcStorage.get();
882
883 // Possibly skip initial rows [sampler.srcY0]
884 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
885 return return_false(*cinfo, bitmap, "skip rows");
886 }
887
888 // now loop through scanlines until y == bitmap->height() - 1
889 for (int y = 0;; y++) {
890 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
891 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
halcanary@google.comfed30372013-10-04 12:46:45 +0000892 // if row_count == 0, then we didn't get a scanline, so abort.
893 // onDecodeSubset() relies on onBuildTileIndex(), which
894 // needs a complete image to succeed.
djsollen@google.com11399402013-03-20 17:45:27 +0000895 if (0 == row_count) {
896 return return_false(*cinfo, bitmap, "read_scanlines");
897 }
898 if (this->shouldCancelDecode()) {
899 return return_false(*cinfo, bitmap, "shouldCancelDecode");
900 }
901
902 if (JCS_CMYK == cinfo->out_color_space) {
903 convert_CMYK_to_RGB(srcRow, width);
904 }
905
906 sampler.next(srcRow);
907 if (bitmap.height() - 1 == y) {
908 // we're done
909 break;
910 }
911
912 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
913 sampler.srcDY() - 1)) {
914 return return_false(*cinfo, bitmap, "skip rows");
915 }
916 }
917 if (swapOnly) {
918 bm->swap(bitmap);
919 } else {
920 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
921 region.width(), region.height(), startX, startY);
922 }
923 return true;
924}
925#endif
926
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000927///////////////////////////////////////////////////////////////////////////////
928
929#include "SkColorPriv.h"
930
931// taken from jcolor.c in libjpeg
932#if 0 // 16bit - precise but slow
933 #define CYR 19595 // 0.299
934 #define CYG 38470 // 0.587
935 #define CYB 7471 // 0.114
936
937 #define CUR -11059 // -0.16874
938 #define CUG -21709 // -0.33126
939 #define CUB 32768 // 0.5
940
941 #define CVR 32768 // 0.5
942 #define CVG -27439 // -0.41869
943 #define CVB -5329 // -0.08131
944
945 #define CSHIFT 16
946#else // 8bit - fast, slightly less precise
947 #define CYR 77 // 0.299
948 #define CYG 150 // 0.587
949 #define CYB 29 // 0.114
950
951 #define CUR -43 // -0.16874
952 #define CUG -85 // -0.33126
953 #define CUB 128 // 0.5
954
955 #define CVR 128 // 0.5
956 #define CVG -107 // -0.41869
957 #define CVB -21 // -0.08131
958
959 #define CSHIFT 8
960#endif
961
962static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
963 int r = SkGetPackedR32(c);
964 int g = SkGetPackedG32(c);
965 int b = SkGetPackedB32(c);
966
967 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
968 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
969 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
970
971 dst[0] = SkToU8(y);
972 dst[1] = SkToU8(u + 128);
973 dst[2] = SkToU8(v + 128);
974}
975
976static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
977 int r = SkGetPackedR4444(c);
978 int g = SkGetPackedG4444(c);
979 int b = SkGetPackedB4444(c);
980
981 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
982 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
983 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
984
985 dst[0] = SkToU8(y);
986 dst[1] = SkToU8(u + 128);
987 dst[2] = SkToU8(v + 128);
988}
989
990static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
991 int r = SkGetPackedR16(c);
992 int g = SkGetPackedG16(c);
993 int b = SkGetPackedB16(c);
994
995 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
996 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
997 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
998
999 dst[0] = SkToU8(y);
1000 dst[1] = SkToU8(u + 128);
1001 dst[2] = SkToU8(v + 128);
1002}
1003
1004///////////////////////////////////////////////////////////////////////////////
1005
1006typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
1007 const void* SK_RESTRICT src, int width,
1008 const SkPMColor* SK_RESTRICT ctable);
1009
1010static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
1011 const void* SK_RESTRICT srcRow, int width,
1012 const SkPMColor*) {
1013 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
1014 while (--width >= 0) {
1015#ifdef WE_CONVERT_TO_YUV
1016 rgb2yuv_32(dst, *src++);
1017#else
1018 uint32_t c = *src++;
1019 dst[0] = SkGetPackedR32(c);
1020 dst[1] = SkGetPackedG32(c);
1021 dst[2] = SkGetPackedB32(c);
1022#endif
1023 dst += 3;
1024 }
1025}
1026
1027static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
1028 const void* SK_RESTRICT srcRow, int width,
1029 const SkPMColor*) {
1030 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
1031 while (--width >= 0) {
1032#ifdef WE_CONVERT_TO_YUV
1033 rgb2yuv_4444(dst, *src++);
1034#else
1035 SkPMColor16 c = *src++;
1036 dst[0] = SkPacked4444ToR32(c);
1037 dst[1] = SkPacked4444ToG32(c);
1038 dst[2] = SkPacked4444ToB32(c);
1039#endif
1040 dst += 3;
1041 }
1042}
1043
1044static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
1045 const void* SK_RESTRICT srcRow, int width,
1046 const SkPMColor*) {
1047 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
1048 while (--width >= 0) {
1049#ifdef WE_CONVERT_TO_YUV
1050 rgb2yuv_16(dst, *src++);
1051#else
1052 uint16_t c = *src++;
1053 dst[0] = SkPacked16ToR32(c);
1054 dst[1] = SkPacked16ToG32(c);
1055 dst[2] = SkPacked16ToB32(c);
1056#endif
1057 dst += 3;
1058 }
1059}
1060
1061static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
1062 const void* SK_RESTRICT srcRow, int width,
1063 const SkPMColor* SK_RESTRICT ctable) {
1064 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
1065 while (--width >= 0) {
1066#ifdef WE_CONVERT_TO_YUV
1067 rgb2yuv_32(dst, ctable[*src++]);
1068#else
1069 uint32_t c = ctable[*src++];
1070 dst[0] = SkGetPackedR32(c);
1071 dst[1] = SkGetPackedG32(c);
1072 dst[2] = SkGetPackedB32(c);
1073#endif
1074 dst += 3;
1075 }
1076}
1077
1078static WriteScanline ChooseWriter(const SkBitmap& bm) {
1079 switch (bm.config()) {
1080 case SkBitmap::kARGB_8888_Config:
1081 return Write_32_YUV;
1082 case SkBitmap::kRGB_565_Config:
1083 return Write_16_YUV;
1084 case SkBitmap::kARGB_4444_Config:
1085 return Write_4444_YUV;
1086 case SkBitmap::kIndex8_Config:
1087 return Write_Index_YUV;
1088 default:
1089 return NULL;
1090 }
1091}
1092
1093class SkJPEGImageEncoder : public SkImageEncoder {
1094protected:
1095 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
1096#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +00001097 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001098#endif
1099
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001100 SkAutoLockPixels alp(bm);
1101 if (NULL == bm.getPixels()) {
1102 return false;
1103 }
1104
1105 jpeg_compress_struct cinfo;
1106 skjpeg_error_mgr sk_err;
1107 skjpeg_destination_mgr sk_wstream(stream);
1108
1109 // allocate these before set call setjmp
1110 SkAutoMalloc oneRow;
1111 SkAutoLockColors ctLocker;
1112
1113 cinfo.err = jpeg_std_error(&sk_err);
1114 sk_err.error_exit = skjpeg_error_exit;
1115 if (setjmp(sk_err.fJmpBuf)) {
1116 return false;
1117 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001118
mtklein@google.com8d725b22013-07-24 16:20:05 +00001119 // Keep after setjmp or mark volatile.
1120 const WriteScanline writer = ChooseWriter(bm);
1121 if (NULL == writer) {
1122 return false;
1123 }
1124
1125 jpeg_create_compress(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001126 cinfo.dest = &sk_wstream;
1127 cinfo.image_width = bm.width();
1128 cinfo.image_height = bm.height();
1129 cinfo.input_components = 3;
1130#ifdef WE_CONVERT_TO_YUV
1131 cinfo.in_color_space = JCS_YCbCr;
1132#else
1133 cinfo.in_color_space = JCS_RGB;
1134#endif
1135 cinfo.input_gamma = 1;
1136
1137 jpeg_set_defaults(&cinfo);
1138 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +00001139#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001140 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +00001141#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001142
1143 jpeg_start_compress(&cinfo, TRUE);
1144
1145 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001146 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001147
1148 const SkPMColor* colors = ctLocker.lockColors(bm);
1149 const void* srcRow = bm.getPixels();
1150
1151 while (cinfo.next_scanline < cinfo.image_height) {
1152 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
1153
1154 writer(oneRowP, srcRow, width, colors);
1155 row_pointer[0] = oneRowP;
1156 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
1157 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
1158 }
1159
1160 jpeg_finish_compress(&cinfo);
1161 jpeg_destroy_compress(&cinfo);
1162
1163 return true;
1164 }
1165};
1166
1167///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001168DEFINE_DECODER_CREATOR(JPEGImageDecoder);
1169DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
1170///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001171
scroggo@google.comb5571b32013-09-25 21:34:24 +00001172static bool is_jpeg(SkStreamRewindable* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001173 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001174 static const size_t HEADER_SIZE = sizeof(gHeader);
1175
1176 char buffer[HEADER_SIZE];
1177 size_t len = stream->read(buffer, HEADER_SIZE);
1178
1179 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001180 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001181 }
1182 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001183 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001184 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001185 return true;
1186}
1187
scroggo@google.comb5571b32013-09-25 21:34:24 +00001188
1189static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001190 if (is_jpeg(stream)) {
1191 return SkNEW(SkJPEGImageDecoder);
1192 }
1193 return NULL;
1194}
1195
scroggo@google.comb5571b32013-09-25 21:34:24 +00001196static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001197 if (is_jpeg(stream)) {
1198 return SkImageDecoder::kJPEG_Format;
1199 }
1200 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001201}
1202
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001203static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001204 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1205}
1206
mtklein@google.combd6343b2013-09-04 17:20:18 +00001207static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory);
1208static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg);
1209static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory);