blob: bea1e98641f65725d1087576f9b191c820ec9f3f [file] [log] [blame]
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001
2/*
3 * Copyright 2007 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkImageDecoder.h"
11#include "SkImageEncoder.h"
12#include "SkJpegUtility.h"
13#include "SkColorPriv.h"
14#include "SkDither.h"
15#include "SkScaledBitmapSampler.h"
16#include "SkStream.h"
17#include "SkTemplates.h"
djsollen@google.com11399402013-03-20 17:45:27 +000018#include "SkTime.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000019#include "SkUtils.h"
djsollen@google.com11399402013-03-20 17:45:27 +000020#include "SkRect.h"
21#include "SkCanvas.h"
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000022
23#include <stdio.h>
24extern "C" {
25 #include "jpeglib.h"
26 #include "jerror.h"
27}
28
djsollen@google.com11399402013-03-20 17:45:27 +000029// Uncomment to enable the code path used by the Android framework with their
30// custom image decoders.
31//#if defined(SK_BUILD_FOR_ANDROID) && defined(SK_DEBUG)
32// #define SK_BUILD_FOR_ANDROID_FRAMEWORK
33//#endif
34
35// These enable timing code that report milliseconds for an encoding/decoding
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000036//#define TIME_ENCODE
37//#define TIME_DECODE
38
39// this enables our rgb->yuv code, which is faster than libjpeg on ARM
40// disable for the moment, as we have some glitches when width != multiple of 4
41#define WE_CONVERT_TO_YUV
42
djsollen@google.com11399402013-03-20 17:45:27 +000043// If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers
44// support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565.
45
tomhudson@google.comd33b26e2012-03-02 16:12:14 +000046//////////////////////////////////////////////////////////////////////////
47//////////////////////////////////////////////////////////////////////////
48
djsollen@google.com11399402013-03-20 17:45:27 +000049static void overwrite_mem_buffer_size(j_decompress_ptr cinfo) {
50#ifdef SK_BUILD_FOR_ANDROID
51 /* Check if the device indicates that it has a large amount of system memory
52 * if so, increase the memory allocation to 30MB instead of the default 5MB.
53 */
54#ifdef ANDROID_LARGE_MEMORY_DEVICE
55 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
56#else
57 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
58#endif
59#endif // SK_BUILD_FOR_ANDROID
60}
61
62//////////////////////////////////////////////////////////////////////////
63//////////////////////////////////////////////////////////////////////////
64
65class SkJPEGImageIndex {
66public:
67 SkJPEGImageIndex(SkStream* stream, SkImageDecoder* decoder)
68 : fSrcMgr(stream, decoder, true) {}
69
70 ~SkJPEGImageIndex() {
71#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
72 jpeg_destroy_huffman_index(&fHuffmanIndex);
73#endif
74 jpeg_finish_decompress(&fCInfo);
75 jpeg_destroy_decompress(&fCInfo);
76 }
77
78 /**
79 * Init the cinfo struct using libjpeg and apply any necessary
80 * customizations.
81 */
82 void initializeInfo() {
83 jpeg_create_decompress(&fCInfo);
84 overwrite_mem_buffer_size(&fCInfo);
85 fCInfo.src = &fSrcMgr;
86 }
87
88 jpeg_decompress_struct* cinfo() { return &fCInfo; }
89
90#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
91 huffman_index* huffmanIndex() { return &fHuffmanIndex; }
92#endif
93
94private:
95 skjpeg_source_mgr fSrcMgr;
96 jpeg_decompress_struct fCInfo;
97#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
98 huffman_index fHuffmanIndex;
99#endif
100};
101
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000102class SkJPEGImageDecoder : public SkImageDecoder {
103public:
djsollen@google.com11399402013-03-20 17:45:27 +0000104 SkJPEGImageDecoder() {
105 fImageIndex = NULL;
106 fImageWidth = 0;
107 fImageHeight = 0;
108 }
109
110 virtual ~SkJPEGImageDecoder() {
111 SkDELETE(fImageIndex);
112 }
113
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000114 virtual Format getFormat() const {
115 return kJPEG_Format;
116 }
117
118protected:
djsollen@google.com11399402013-03-20 17:45:27 +0000119#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
120 virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height) SK_OVERRIDE;
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000121 virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
djsollen@google.com11399402013-03-20 17:45:27 +0000122#endif
123 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
124
125private:
126 SkJPEGImageIndex* fImageIndex;
127 int fImageWidth;
128 int fImageHeight;
129
130 typedef SkImageDecoder INHERITED;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000131};
132
133//////////////////////////////////////////////////////////////////////////
134
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000135/* Automatically clean up after throwing an exception */
136class JPEGAutoClean {
137public:
138 JPEGAutoClean(): cinfo_ptr(NULL) {}
139 ~JPEGAutoClean() {
140 if (cinfo_ptr) {
141 jpeg_destroy_decompress(cinfo_ptr);
142 }
143 }
144 void set(jpeg_decompress_struct* info) {
145 cinfo_ptr = info;
146 }
147private:
148 jpeg_decompress_struct* cinfo_ptr;
149};
150
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000151///////////////////////////////////////////////////////////////////////////////
152
153/* If we need to better match the request, we might examine the image and
154 output dimensions, and determine if the downsampling jpeg provided is
155 not sufficient. If so, we can recompute a modified sampleSize value to
156 make up the difference.
157
158 To skip this additional scaling, just set sampleSize = 1; below.
159 */
160static int recompute_sampleSize(int sampleSize,
161 const jpeg_decompress_struct& cinfo) {
162 return sampleSize * cinfo.output_width / cinfo.image_width;
163}
164
165static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
166 /* These are initialized to 0, so if they have non-zero values, we assume
167 they are "valid" (i.e. have been computed by libjpeg)
168 */
djsollen@google.com11399402013-03-20 17:45:27 +0000169 return 0 != cinfo.output_width && 0 != cinfo.output_height;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000170}
171
djsollen@google.com11399402013-03-20 17:45:27 +0000172static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000173 for (int i = 0; i < count; i++) {
174 JSAMPLE* rowptr = (JSAMPLE*)buffer;
175 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
djsollen@google.com11399402013-03-20 17:45:27 +0000176 if (1 != row_count) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000177 return false;
178 }
179 }
180 return true;
181}
182
djsollen@google.com11399402013-03-20 17:45:27 +0000183#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
184static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
185 huffman_index *index, void* buffer, int count) {
186 for (int i = 0; i < count; i++) {
187 JSAMPLE* rowptr = (JSAMPLE*)buffer;
188 int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
189 if (1 != row_count) {
190 return false;
191 }
192 }
193 return true;
194}
195#endif
196
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000197// This guy exists just to aid in debugging, as it allows debuggers to just
198// set a break-point in one place to see all error exists.
199static bool return_false(const jpeg_decompress_struct& cinfo,
200 const SkBitmap& bm, const char msg[]) {
djsollen@google.com11399402013-03-20 17:45:27 +0000201#ifdef SK_DEBUG
scroggo@google.come8f34712013-07-01 14:44:54 +0000202 SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n", cinfo.err->msg_code,
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000203 cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
204 bm.width(), bm.height());
205#endif
206 return false; // must always return false
207}
208
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000209// Convert a scanline of CMYK samples to RGBX in place. Note that this
210// method moves the "scanline" pointer in its processing
211static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
212 // At this point we've received CMYK pixels from libjpeg. We
rmistry@google.comd6176b02012-08-23 18:14:13 +0000213 // perform a crude conversion to RGB (based on the formulae
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000214 // from easyrgb.com):
215 // CMYK -> CMY
216 // C = ( C * (1 - K) + K ) // for each CMY component
217 // CMY -> RGB
218 // R = ( 1 - C ) * 255 // for each RGB component
219 // Unfortunately we are seeing inverted CMYK so all the original terms
220 // are 1-. This yields:
221 // CMYK -> CMY
222 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
223 // The conversion from CMY->RGB remains the same
224 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
225 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
226 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
227 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
228 scanline[3] = 255;
229 }
230}
231
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000232bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
233#ifdef TIME_DECODE
djsollen@google.com11399402013-03-20 17:45:27 +0000234 SkAutoTime atm("JPEG Decode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000235#endif
236
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000237 JPEGAutoClean autoClean;
238
239 jpeg_decompress_struct cinfo;
djsollen@google.com11399402013-03-20 17:45:27 +0000240 skjpeg_error_mgr errorManager;
241 skjpeg_source_mgr srcManager(stream, this, false);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000242
djsollen@google.com11399402013-03-20 17:45:27 +0000243 cinfo.err = jpeg_std_error(&errorManager);
244 errorManager.error_exit = skjpeg_error_exit;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000245
246 // All objects need to be instantiated before this setjmp call so that
247 // they will be cleaned up properly if an error occurs.
djsollen@google.com11399402013-03-20 17:45:27 +0000248 if (setjmp(errorManager.fJmpBuf)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000249 return return_false(cinfo, *bm, "setjmp");
250 }
251
252 jpeg_create_decompress(&cinfo);
253 autoClean.set(&cinfo);
254
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000255 overwrite_mem_buffer_size(&cinfo);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000256
257 //jpeg_stdio_src(&cinfo, file);
djsollen@google.com11399402013-03-20 17:45:27 +0000258 cinfo.src = &srcManager;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000259
260 int status = jpeg_read_header(&cinfo, true);
261 if (status != JPEG_HEADER_OK) {
262 return return_false(cinfo, *bm, "read_header");
263 }
264
265 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
266 can) much faster that we, just use their num/denom api to approximate
267 the size.
268 */
269 int sampleSize = this->getSampleSize();
270
scroggo@google.com3acd3fc2013-04-12 16:28:21 +0000271#ifdef DCT_IFAST_SUPPORTED
djsollen@google.com11399402013-03-20 17:45:27 +0000272 if (this->getPreferQualityOverSpeed()) {
273 cinfo.dct_method = JDCT_ISLOW;
274 } else {
275 cinfo.dct_method = JDCT_IFAST;
276 }
scroggo@google.com3acd3fc2013-04-12 16:28:21 +0000277#else
278 cinfo.dct_method = JDCT_ISLOW;
279#endif
djsollen@google.com11399402013-03-20 17:45:27 +0000280
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000281 cinfo.scale_num = 1;
282 cinfo.scale_denom = sampleSize;
283
284 /* this gives about 30% performance improvement. In theory it may
285 reduce the visual quality, in practice I'm not seeing a difference
286 */
287 cinfo.do_fancy_upsampling = 0;
288
289 /* this gives another few percents */
290 cinfo.do_block_smoothing = 0;
291
292 /* default format is RGB */
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000293 if (cinfo.jpeg_color_space == JCS_CMYK) {
294 // libjpeg cannot convert from CMYK to RGB - here we set up
295 // so libjpeg will give us CMYK samples back and we will
296 // later manually convert them to RGB
297 cinfo.out_color_space = JCS_CMYK;
298 } else {
299 cinfo.out_color_space = JCS_RGB;
300 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000301
302 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
303 // only these make sense for jpegs
304 if (config != SkBitmap::kARGB_8888_Config &&
305 config != SkBitmap::kARGB_4444_Config &&
306 config != SkBitmap::kRGB_565_Config) {
307 config = SkBitmap::kARGB_8888_Config;
308 }
309
310#ifdef ANDROID_RGB
311 cinfo.dither_mode = JDITHER_NONE;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000312 if (SkBitmap::kARGB_8888_Config == config && JCS_CMYK != cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000313 cinfo.out_color_space = JCS_RGBA_8888;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000314 } else if (SkBitmap::kRGB_565_Config == config && JCS_CMYK != cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000315 cinfo.out_color_space = JCS_RGB_565;
316 if (this->getDitherImage()) {
317 cinfo.dither_mode = JDITHER_ORDERED;
318 }
319 }
320#endif
321
djsollen@google.com11399402013-03-20 17:45:27 +0000322 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000323 bm->setConfig(config, cinfo.image_width, cinfo.image_height);
324 bm->setIsOpaque(true);
325 return true;
326 }
327
328 /* image_width and image_height are the original dimensions, available
329 after jpeg_read_header(). To see the scaled dimensions, we have to call
330 jpeg_start_decompress(), and then read output_width and output_height.
331 */
332 if (!jpeg_start_decompress(&cinfo)) {
333 /* If we failed here, we may still have enough information to return
334 to the caller if they just wanted (subsampled bounds). If sampleSize
335 was 1, then we would have already returned. Thus we just check if
336 we're in kDecodeBounds_Mode, and that we have valid output sizes.
337
338 One reason to fail here is that we have insufficient stream data
339 to complete the setup. However, output dimensions seem to get
340 computed very early, which is why this special check can pay off.
341 */
djsollen@google.com11399402013-03-20 17:45:27 +0000342 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000343 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
344 recompute_sampleSize(sampleSize, cinfo));
345 bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
346 bm->setIsOpaque(true);
347 return true;
348 } else {
349 return return_false(cinfo, *bm, "start_decompress");
350 }
351 }
352 sampleSize = recompute_sampleSize(sampleSize, cinfo);
353
354 // should we allow the Chooser (if present) to pick a config for us???
djsollen@google.com11399402013-03-20 17:45:27 +0000355 if (!this->chooseFromOneChoice(config, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000356 return return_false(cinfo, *bm, "chooseFromOneChoice");
357 }
358
djsollen@google.com11399402013-03-20 17:45:27 +0000359 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000360 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
361 bm->setIsOpaque(true);
362 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
363 return true;
364 }
365 if (!this->allocPixelRef(bm, NULL)) {
366 return return_false(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000367 }
368
369 SkAutoLockPixels alp(*bm);
370
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000371#ifdef ANDROID_RGB
372 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
373 a significant performance boost.
374 */
375 if (sampleSize == 1 &&
rmistry@google.comd6176b02012-08-23 18:14:13 +0000376 ((config == SkBitmap::kARGB_8888_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000377 cinfo.out_color_space == JCS_RGBA_8888) ||
rmistry@google.comd6176b02012-08-23 18:14:13 +0000378 (config == SkBitmap::kRGB_565_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000379 cinfo.out_color_space == JCS_RGB_565)))
380 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000381 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000382 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000383
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000384 while (cinfo.output_scanline < cinfo.output_height) {
385 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
386 // if row_count == 0, then we didn't get a scanline, so abort.
387 // if we supported partial images, we might return true in this case
388 if (0 == row_count) {
389 return return_false(cinfo, *bm, "read_scanlines");
390 }
391 if (this->shouldCancelDecode()) {
392 return return_false(cinfo, *bm, "shouldCancelDecode");
393 }
394 rowptr += bpr;
395 }
396 jpeg_finish_decompress(&cinfo);
397 return true;
398 }
399#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000400
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000401 // check for supported formats
402 SkScaledBitmapSampler::SrcConfig sc;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000403 if (JCS_CMYK == cinfo.out_color_space) {
404 // In this case we will manually convert the CMYK values to RGB
405 sc = SkScaledBitmapSampler::kRGBX;
406 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000407 sc = SkScaledBitmapSampler::kRGB;
408#ifdef ANDROID_RGB
409 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
410 sc = SkScaledBitmapSampler::kRGBX;
411 } else if (JCS_RGB_565 == cinfo.out_color_space) {
412 sc = SkScaledBitmapSampler::kRGB_565;
413#endif
414 } else if (1 == cinfo.out_color_components &&
415 JCS_GRAYSCALE == cinfo.out_color_space) {
416 sc = SkScaledBitmapSampler::kGray;
417 } else {
418 return return_false(cinfo, *bm, "jpeg colorspace");
419 }
420
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000421 if (!sampler.begin(bm, sc, this->getDitherImage())) {
422 return return_false(cinfo, *bm, "sampler.begin");
423 }
424
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000425 // The CMYK work-around relies on 4 components per pixel here
djsollen@google.com11399402013-03-20 17:45:27 +0000426 SkAutoMalloc srcStorage(cinfo.output_width * 4);
427 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000428
429 // Possibly skip initial rows [sampler.srcY0]
430 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
431 return return_false(cinfo, *bm, "skip rows");
432 }
433
434 // now loop through scanlines until y == bm->height() - 1
435 for (int y = 0;; y++) {
436 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
437 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
438 if (0 == row_count) {
439 return return_false(cinfo, *bm, "read_scanlines");
440 }
441 if (this->shouldCancelDecode()) {
442 return return_false(cinfo, *bm, "shouldCancelDecode");
443 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000444
445 if (JCS_CMYK == cinfo.out_color_space) {
446 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
447 }
448
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000449 sampler.next(srcRow);
450 if (bm->height() - 1 == y) {
451 // we're done
452 break;
453 }
454
455 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
456 return return_false(cinfo, *bm, "skip rows");
457 }
458 }
459
460 // we formally skip the rest, so we don't get a complaint from libjpeg
461 if (!skip_src_rows(&cinfo, srcRow,
462 cinfo.output_height - cinfo.output_scanline)) {
463 return return_false(cinfo, *bm, "skip rows");
464 }
465 jpeg_finish_decompress(&cinfo);
466
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000467 return true;
468}
469
djsollen@google.com11399402013-03-20 17:45:27 +0000470#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
471bool SkJPEGImageDecoder::onBuildTileIndex(SkStream* stream, int *width, int *height) {
472
473 SkJPEGImageIndex* imageIndex = SkNEW_ARGS(SkJPEGImageIndex, (stream, this));
474 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
475 huffman_index* huffmanIndex = imageIndex->huffmanIndex();
476
477 skjpeg_error_mgr sk_err;
478 cinfo->err = jpeg_std_error(&sk_err);
479 sk_err.error_exit = skjpeg_error_exit;
480
481 // All objects need to be instantiated before this setjmp call so that
482 // they will be cleaned up properly if an error occurs.
483 if (setjmp(sk_err.fJmpBuf)) {
484 return false;
485 }
486
487 // create the cinfo used to create/build the huffmanIndex
488 imageIndex->initializeInfo();
489 cinfo->do_fancy_upsampling = 0;
490 cinfo->do_block_smoothing = 0;
491
492 int status = jpeg_read_header(cinfo, true);
493 if (JPEG_HEADER_OK != status) {
494 SkDELETE(imageIndex);
495 return false;
496 }
497
498 jpeg_create_huffman_index(cinfo, huffmanIndex);
499 cinfo->scale_num = 1;
500 cinfo->scale_denom = 1;
501 if (!jpeg_build_huffman_index(cinfo, huffmanIndex)) {
502 SkDELETE(imageIndex);
503 return false;
504 }
505
506 // destroy the cinfo used to create/build the huffman index
507 jpeg_destroy_decompress(cinfo);
508
509 // Init decoder to image decode mode
510 imageIndex->initializeInfo();
511
512 status = jpeg_read_header(cinfo, true);
513 if (JPEG_HEADER_OK != status) {
514 SkDELETE(imageIndex);
515 return false;
516 }
517
518 cinfo->out_color_space = JCS_RGBA_8888;
519 cinfo->do_fancy_upsampling = 0;
520 cinfo->do_block_smoothing = 0;
521
522 // instead of jpeg_start_decompress() we start a tiled decompress
523 jpeg_start_tile_decompress(cinfo);
524
525 cinfo->scale_num = 1;
526 *height = cinfo->output_height;
527 *width = cinfo->output_width;
528 fImageWidth = *width;
529 fImageHeight = *height;
530
531 SkDELETE(fImageIndex);
532 fImageIndex = imageIndex;
533
534 return true;
535}
536
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000537bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000538 if (NULL == fImageIndex) {
539 return false;
540 }
541 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
542
543 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
544 if (!rect.intersect(region)) {
545 // If the requested region is entirely outside the image return false
546 return false;
547 }
548
549
550 skjpeg_error_mgr errorManager;
551 cinfo->err = jpeg_std_error(&errorManager);
552 errorManager.error_exit = skjpeg_error_exit;
553 if (setjmp(errorManager.fJmpBuf)) {
554 return false;
555 }
556
557 int requestedSampleSize = this->getSampleSize();
558 cinfo->scale_denom = requestedSampleSize;
559
560 if (this->getPreferQualityOverSpeed()) {
561 cinfo->dct_method = JDCT_ISLOW;
562 } else {
563 cinfo->dct_method = JDCT_IFAST;
564 }
565
566 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
567 if (config != SkBitmap::kARGB_8888_Config &&
568 config != SkBitmap::kARGB_4444_Config &&
569 config != SkBitmap::kRGB_565_Config) {
570 config = SkBitmap::kARGB_8888_Config;
571 }
572
573 /* default format is RGB */
574 cinfo->out_color_space = JCS_RGB;
575
576#ifdef ANDROID_RGB
577 cinfo->dither_mode = JDITHER_NONE;
578 if (SkBitmap::kARGB_8888_Config == config) {
579 cinfo->out_color_space = JCS_RGBA_8888;
580 } else if (SkBitmap::kRGB_565_Config == config) {
581 cinfo->out_color_space = JCS_RGB_565;
582 if (this->getDitherImage()) {
583 cinfo->dither_mode = JDITHER_ORDERED;
584 }
585 }
586#endif
587
588 int startX = rect.fLeft;
589 int startY = rect.fTop;
590 int width = rect.width();
591 int height = rect.height();
592
593 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
594 &startX, &startY, &width, &height);
595 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
596 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
597
598 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
599
600 SkBitmap bitmap;
601 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
602 bitmap.setIsOpaque(true);
603
604 // Check ahead of time if the swap(dest, src) is possible or not.
605 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
606 // swap happening. If no, then we will use alloc to allocate pixels to
607 // prevent garbage collection.
608 int w = rect.width() / actualSampleSize;
609 int h = rect.height() / actualSampleSize;
610 bool swapOnly = (rect == region) && bm->isNull() &&
611 (w == bitmap.width()) && (h == bitmap.height()) &&
612 ((startX - rect.x()) / actualSampleSize == 0) &&
613 ((startY - rect.y()) / actualSampleSize == 0);
614 if (swapOnly) {
615 if (!this->allocPixelRef(&bitmap, NULL)) {
616 return return_false(*cinfo, bitmap, "allocPixelRef");
617 }
618 } else {
619 if (!bitmap.allocPixels()) {
620 return return_false(*cinfo, bitmap, "allocPixels");
621 }
622 }
623
624 SkAutoLockPixels alp(bitmap);
625
626#ifdef ANDROID_RGB
627 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
628 a significant performance boost.
629 */
630 if (skiaSampleSize == 1 &&
631 ((config == SkBitmap::kARGB_8888_Config &&
632 cinfo->out_color_space == JCS_RGBA_8888) ||
633 (config == SkBitmap::kRGB_565_Config &&
634 cinfo->out_color_space == JCS_RGB_565)))
635 {
636 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
637 INT32 const bpr = bitmap.rowBytes();
638 int rowTotalCount = 0;
639
640 while (rowTotalCount < height) {
641 int rowCount = jpeg_read_tile_scanline(cinfo,
642 fImageIndex->huffmanIndex(),
643 &rowptr);
644 // if row_count == 0, then we didn't get a scanline, so abort.
645 // if we supported partial images, we might return true in this case
646 if (0 == rowCount) {
647 return return_false(*cinfo, bitmap, "read_scanlines");
648 }
649 if (this->shouldCancelDecode()) {
650 return return_false(*cinfo, bitmap, "shouldCancelDecode");
651 }
652 rowTotalCount += rowCount;
653 rowptr += bpr;
654 }
655
656 if (swapOnly) {
657 bm->swap(bitmap);
658 } else {
659 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
660 region.width(), region.height(), startX, startY);
661 }
662 return true;
663 }
664#endif
665
666 // check for supported formats
667 SkScaledBitmapSampler::SrcConfig sc;
668 if (JCS_CMYK == cinfo->out_color_space) {
669 // In this case we will manually convert the CMYK values to RGB
670 sc = SkScaledBitmapSampler::kRGBX;
671 } else if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_space) {
672 sc = SkScaledBitmapSampler::kRGB;
673#ifdef ANDROID_RGB
674 } else if (JCS_RGBA_8888 == cinfo->out_color_space) {
675 sc = SkScaledBitmapSampler::kRGBX;
676 } else if (JCS_RGB_565 == cinfo->out_color_space) {
677 sc = SkScaledBitmapSampler::kRGB_565;
678#endif
679 } else if (1 == cinfo->out_color_components &&
680 JCS_GRAYSCALE == cinfo->out_color_space) {
681 sc = SkScaledBitmapSampler::kGray;
682 } else {
683 return return_false(*cinfo, *bm, "jpeg colorspace");
684 }
685
686 if (!sampler.begin(&bitmap, sc, this->getDitherImage())) {
687 return return_false(*cinfo, bitmap, "sampler.begin");
688 }
689
690 // The CMYK work-around relies on 4 components per pixel here
691 SkAutoMalloc srcStorage(width * 4);
692 uint8_t* srcRow = (uint8_t*)srcStorage.get();
693
694 // Possibly skip initial rows [sampler.srcY0]
695 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
696 return return_false(*cinfo, bitmap, "skip rows");
697 }
698
699 // now loop through scanlines until y == bitmap->height() - 1
700 for (int y = 0;; y++) {
701 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
702 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
703 if (0 == row_count) {
704 return return_false(*cinfo, bitmap, "read_scanlines");
705 }
706 if (this->shouldCancelDecode()) {
707 return return_false(*cinfo, bitmap, "shouldCancelDecode");
708 }
709
710 if (JCS_CMYK == cinfo->out_color_space) {
711 convert_CMYK_to_RGB(srcRow, width);
712 }
713
714 sampler.next(srcRow);
715 if (bitmap.height() - 1 == y) {
716 // we're done
717 break;
718 }
719
720 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
721 sampler.srcDY() - 1)) {
722 return return_false(*cinfo, bitmap, "skip rows");
723 }
724 }
725 if (swapOnly) {
726 bm->swap(bitmap);
727 } else {
728 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
729 region.width(), region.height(), startX, startY);
730 }
731 return true;
732}
733#endif
734
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000735///////////////////////////////////////////////////////////////////////////////
736
737#include "SkColorPriv.h"
738
739// taken from jcolor.c in libjpeg
740#if 0 // 16bit - precise but slow
741 #define CYR 19595 // 0.299
742 #define CYG 38470 // 0.587
743 #define CYB 7471 // 0.114
744
745 #define CUR -11059 // -0.16874
746 #define CUG -21709 // -0.33126
747 #define CUB 32768 // 0.5
748
749 #define CVR 32768 // 0.5
750 #define CVG -27439 // -0.41869
751 #define CVB -5329 // -0.08131
752
753 #define CSHIFT 16
754#else // 8bit - fast, slightly less precise
755 #define CYR 77 // 0.299
756 #define CYG 150 // 0.587
757 #define CYB 29 // 0.114
758
759 #define CUR -43 // -0.16874
760 #define CUG -85 // -0.33126
761 #define CUB 128 // 0.5
762
763 #define CVR 128 // 0.5
764 #define CVG -107 // -0.41869
765 #define CVB -21 // -0.08131
766
767 #define CSHIFT 8
768#endif
769
770static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
771 int r = SkGetPackedR32(c);
772 int g = SkGetPackedG32(c);
773 int b = SkGetPackedB32(c);
774
775 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
776 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
777 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
778
779 dst[0] = SkToU8(y);
780 dst[1] = SkToU8(u + 128);
781 dst[2] = SkToU8(v + 128);
782}
783
784static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
785 int r = SkGetPackedR4444(c);
786 int g = SkGetPackedG4444(c);
787 int b = SkGetPackedB4444(c);
788
789 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
790 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
791 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
792
793 dst[0] = SkToU8(y);
794 dst[1] = SkToU8(u + 128);
795 dst[2] = SkToU8(v + 128);
796}
797
798static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
799 int r = SkGetPackedR16(c);
800 int g = SkGetPackedG16(c);
801 int b = SkGetPackedB16(c);
802
803 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
804 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
805 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
806
807 dst[0] = SkToU8(y);
808 dst[1] = SkToU8(u + 128);
809 dst[2] = SkToU8(v + 128);
810}
811
812///////////////////////////////////////////////////////////////////////////////
813
814typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
815 const void* SK_RESTRICT src, int width,
816 const SkPMColor* SK_RESTRICT ctable);
817
818static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
819 const void* SK_RESTRICT srcRow, int width,
820 const SkPMColor*) {
821 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
822 while (--width >= 0) {
823#ifdef WE_CONVERT_TO_YUV
824 rgb2yuv_32(dst, *src++);
825#else
826 uint32_t c = *src++;
827 dst[0] = SkGetPackedR32(c);
828 dst[1] = SkGetPackedG32(c);
829 dst[2] = SkGetPackedB32(c);
830#endif
831 dst += 3;
832 }
833}
834
835static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
836 const void* SK_RESTRICT srcRow, int width,
837 const SkPMColor*) {
838 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
839 while (--width >= 0) {
840#ifdef WE_CONVERT_TO_YUV
841 rgb2yuv_4444(dst, *src++);
842#else
843 SkPMColor16 c = *src++;
844 dst[0] = SkPacked4444ToR32(c);
845 dst[1] = SkPacked4444ToG32(c);
846 dst[2] = SkPacked4444ToB32(c);
847#endif
848 dst += 3;
849 }
850}
851
852static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
853 const void* SK_RESTRICT srcRow, int width,
854 const SkPMColor*) {
855 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
856 while (--width >= 0) {
857#ifdef WE_CONVERT_TO_YUV
858 rgb2yuv_16(dst, *src++);
859#else
860 uint16_t c = *src++;
861 dst[0] = SkPacked16ToR32(c);
862 dst[1] = SkPacked16ToG32(c);
863 dst[2] = SkPacked16ToB32(c);
864#endif
865 dst += 3;
866 }
867}
868
869static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
870 const void* SK_RESTRICT srcRow, int width,
871 const SkPMColor* SK_RESTRICT ctable) {
872 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
873 while (--width >= 0) {
874#ifdef WE_CONVERT_TO_YUV
875 rgb2yuv_32(dst, ctable[*src++]);
876#else
877 uint32_t c = ctable[*src++];
878 dst[0] = SkGetPackedR32(c);
879 dst[1] = SkGetPackedG32(c);
880 dst[2] = SkGetPackedB32(c);
881#endif
882 dst += 3;
883 }
884}
885
886static WriteScanline ChooseWriter(const SkBitmap& bm) {
887 switch (bm.config()) {
888 case SkBitmap::kARGB_8888_Config:
889 return Write_32_YUV;
890 case SkBitmap::kRGB_565_Config:
891 return Write_16_YUV;
892 case SkBitmap::kARGB_4444_Config:
893 return Write_4444_YUV;
894 case SkBitmap::kIndex8_Config:
895 return Write_Index_YUV;
896 default:
897 return NULL;
898 }
899}
900
901class SkJPEGImageEncoder : public SkImageEncoder {
902protected:
903 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
904#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +0000905 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000906#endif
907
908 const WriteScanline writer = ChooseWriter(bm);
909 if (NULL == writer) {
910 return false;
911 }
912
913 SkAutoLockPixels alp(bm);
914 if (NULL == bm.getPixels()) {
915 return false;
916 }
917
918 jpeg_compress_struct cinfo;
919 skjpeg_error_mgr sk_err;
920 skjpeg_destination_mgr sk_wstream(stream);
921
922 // allocate these before set call setjmp
923 SkAutoMalloc oneRow;
924 SkAutoLockColors ctLocker;
925
926 cinfo.err = jpeg_std_error(&sk_err);
927 sk_err.error_exit = skjpeg_error_exit;
928 if (setjmp(sk_err.fJmpBuf)) {
929 return false;
930 }
931 jpeg_create_compress(&cinfo);
932
933 cinfo.dest = &sk_wstream;
934 cinfo.image_width = bm.width();
935 cinfo.image_height = bm.height();
936 cinfo.input_components = 3;
937#ifdef WE_CONVERT_TO_YUV
938 cinfo.in_color_space = JCS_YCbCr;
939#else
940 cinfo.in_color_space = JCS_RGB;
941#endif
942 cinfo.input_gamma = 1;
943
944 jpeg_set_defaults(&cinfo);
945 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +0000946#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000947 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +0000948#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000949
950 jpeg_start_compress(&cinfo, TRUE);
951
952 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000953 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000954
955 const SkPMColor* colors = ctLocker.lockColors(bm);
956 const void* srcRow = bm.getPixels();
957
958 while (cinfo.next_scanline < cinfo.image_height) {
959 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
960
961 writer(oneRowP, srcRow, width, colors);
962 row_pointer[0] = oneRowP;
963 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
964 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
965 }
966
967 jpeg_finish_compress(&cinfo);
968 jpeg_destroy_compress(&cinfo);
969
970 return true;
971 }
972};
973
974///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000975DEFINE_DECODER_CREATOR(JPEGImageDecoder);
976DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
977///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000978
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000979static bool is_jpeg(SkStream* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000980 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000981 static const size_t HEADER_SIZE = sizeof(gHeader);
982
983 char buffer[HEADER_SIZE];
984 size_t len = stream->read(buffer, HEADER_SIZE);
985
986 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000987 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000988 }
989 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000990 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000991 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000992 return true;
993}
994
995#include "SkTRegistry.h"
996
997static SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) {
998 if (is_jpeg(stream)) {
999 return SkNEW(SkJPEGImageDecoder);
1000 }
1001 return NULL;
1002}
1003
1004static SkImageDecoder::Format get_format_jpeg(SkStream* stream) {
1005 if (is_jpeg(stream)) {
1006 return SkImageDecoder::kJPEG_Format;
1007 }
1008 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001009}
1010
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001011static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001012 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1013}
1014
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001015
1016static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory);
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001017static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_jpeg);
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001018static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efactory);