blob: 4f32aa918614c8e72afd9cd4a7f59e5335ac3f62 [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;
121 virtual bool onDecodeRegion(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
122#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
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000202 SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code,
203 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
djsollen@google.com11399402013-03-20 17:45:27 +0000271 if (this->getPreferQualityOverSpeed()) {
272 cinfo.dct_method = JDCT_ISLOW;
273 } else {
274 cinfo.dct_method = JDCT_IFAST;
275 }
276
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000277 cinfo.scale_num = 1;
278 cinfo.scale_denom = sampleSize;
279
280 /* this gives about 30% performance improvement. In theory it may
281 reduce the visual quality, in practice I'm not seeing a difference
282 */
283 cinfo.do_fancy_upsampling = 0;
284
285 /* this gives another few percents */
286 cinfo.do_block_smoothing = 0;
287
288 /* default format is RGB */
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000289 if (cinfo.jpeg_color_space == JCS_CMYK) {
290 // libjpeg cannot convert from CMYK to RGB - here we set up
291 // so libjpeg will give us CMYK samples back and we will
292 // later manually convert them to RGB
293 cinfo.out_color_space = JCS_CMYK;
294 } else {
295 cinfo.out_color_space = JCS_RGB;
296 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000297
298 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
299 // only these make sense for jpegs
300 if (config != SkBitmap::kARGB_8888_Config &&
301 config != SkBitmap::kARGB_4444_Config &&
302 config != SkBitmap::kRGB_565_Config) {
303 config = SkBitmap::kARGB_8888_Config;
304 }
305
306#ifdef ANDROID_RGB
307 cinfo.dither_mode = JDITHER_NONE;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000308 if (SkBitmap::kARGB_8888_Config == config && JCS_CMYK != cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000309 cinfo.out_color_space = JCS_RGBA_8888;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000310 } else if (SkBitmap::kRGB_565_Config == config && JCS_CMYK != cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000311 cinfo.out_color_space = JCS_RGB_565;
312 if (this->getDitherImage()) {
313 cinfo.dither_mode = JDITHER_ORDERED;
314 }
315 }
316#endif
317
djsollen@google.com11399402013-03-20 17:45:27 +0000318 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000319 bm->setConfig(config, cinfo.image_width, cinfo.image_height);
320 bm->setIsOpaque(true);
321 return true;
322 }
323
324 /* image_width and image_height are the original dimensions, available
325 after jpeg_read_header(). To see the scaled dimensions, we have to call
326 jpeg_start_decompress(), and then read output_width and output_height.
327 */
328 if (!jpeg_start_decompress(&cinfo)) {
329 /* If we failed here, we may still have enough information to return
330 to the caller if they just wanted (subsampled bounds). If sampleSize
331 was 1, then we would have already returned. Thus we just check if
332 we're in kDecodeBounds_Mode, and that we have valid output sizes.
333
334 One reason to fail here is that we have insufficient stream data
335 to complete the setup. However, output dimensions seem to get
336 computed very early, which is why this special check can pay off.
337 */
djsollen@google.com11399402013-03-20 17:45:27 +0000338 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000339 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
340 recompute_sampleSize(sampleSize, cinfo));
341 bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
342 bm->setIsOpaque(true);
343 return true;
344 } else {
345 return return_false(cinfo, *bm, "start_decompress");
346 }
347 }
348 sampleSize = recompute_sampleSize(sampleSize, cinfo);
349
350 // should we allow the Chooser (if present) to pick a config for us???
djsollen@google.com11399402013-03-20 17:45:27 +0000351 if (!this->chooseFromOneChoice(config, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000352 return return_false(cinfo, *bm, "chooseFromOneChoice");
353 }
354
djsollen@google.com11399402013-03-20 17:45:27 +0000355 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
356
357 bm->lockPixels();
358 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
359 bm->unlockPixels();
360 bool reuseBitmap = (rowptr != NULL);
361
362 if (reuseBitmap) {
363 if (sampler.scaledWidth() != bm->width() ||
364 sampler.scaledHeight() != bm->height()) {
365 // Dimensions must match
366 return false;
367 } else if (SkImageDecoder::kDecodeBounds_Mode == mode) {
368 return true;
369 }
370 } else {
371 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
372 bm->setIsOpaque(true);
373 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
374 return true;
375 }
376 if (!this->allocPixelRef(bm, NULL)) {
377 return return_false(cinfo, *bm, "allocPixelRef");
378 }
379 }
380
381 SkAutoLockPixels alp(*bm);
382
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000383#ifdef ANDROID_RGB
384 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
385 a significant performance boost.
386 */
387 if (sampleSize == 1 &&
rmistry@google.comd6176b02012-08-23 18:14:13 +0000388 ((config == SkBitmap::kARGB_8888_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000389 cinfo.out_color_space == JCS_RGBA_8888) ||
rmistry@google.comd6176b02012-08-23 18:14:13 +0000390 (config == SkBitmap::kRGB_565_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000391 cinfo.out_color_space == JCS_RGB_565)))
392 {
djsollen@google.com11399402013-03-20 17:45:27 +0000393 rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000394 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000395
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000396 while (cinfo.output_scanline < cinfo.output_height) {
397 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
398 // if row_count == 0, then we didn't get a scanline, so abort.
399 // if we supported partial images, we might return true in this case
400 if (0 == row_count) {
401 return return_false(cinfo, *bm, "read_scanlines");
402 }
403 if (this->shouldCancelDecode()) {
404 return return_false(cinfo, *bm, "shouldCancelDecode");
405 }
406 rowptr += bpr;
407 }
djsollen@google.com11399402013-03-20 17:45:27 +0000408 if (reuseBitmap) {
409 bm->notifyPixelsChanged();
410 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000411 jpeg_finish_decompress(&cinfo);
412 return true;
413 }
414#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000415
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000416 // check for supported formats
417 SkScaledBitmapSampler::SrcConfig sc;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000418 if (JCS_CMYK == cinfo.out_color_space) {
419 // In this case we will manually convert the CMYK values to RGB
420 sc = SkScaledBitmapSampler::kRGBX;
421 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000422 sc = SkScaledBitmapSampler::kRGB;
423#ifdef ANDROID_RGB
424 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
425 sc = SkScaledBitmapSampler::kRGBX;
426 } else if (JCS_RGB_565 == cinfo.out_color_space) {
427 sc = SkScaledBitmapSampler::kRGB_565;
428#endif
429 } else if (1 == cinfo.out_color_components &&
430 JCS_GRAYSCALE == cinfo.out_color_space) {
431 sc = SkScaledBitmapSampler::kGray;
432 } else {
433 return return_false(cinfo, *bm, "jpeg colorspace");
434 }
435
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000436 if (!sampler.begin(bm, sc, this->getDitherImage())) {
437 return return_false(cinfo, *bm, "sampler.begin");
438 }
439
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000440 // The CMYK work-around relies on 4 components per pixel here
djsollen@google.com11399402013-03-20 17:45:27 +0000441 SkAutoMalloc srcStorage(cinfo.output_width * 4);
442 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000443
444 // Possibly skip initial rows [sampler.srcY0]
445 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
446 return return_false(cinfo, *bm, "skip rows");
447 }
448
449 // now loop through scanlines until y == bm->height() - 1
450 for (int y = 0;; y++) {
451 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
452 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
453 if (0 == row_count) {
454 return return_false(cinfo, *bm, "read_scanlines");
455 }
456 if (this->shouldCancelDecode()) {
457 return return_false(cinfo, *bm, "shouldCancelDecode");
458 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000459
460 if (JCS_CMYK == cinfo.out_color_space) {
461 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
462 }
463
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000464 sampler.next(srcRow);
465 if (bm->height() - 1 == y) {
466 // we're done
467 break;
468 }
469
470 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
471 return return_false(cinfo, *bm, "skip rows");
472 }
473 }
474
475 // we formally skip the rest, so we don't get a complaint from libjpeg
476 if (!skip_src_rows(&cinfo, srcRow,
477 cinfo.output_height - cinfo.output_scanline)) {
478 return return_false(cinfo, *bm, "skip rows");
479 }
djsollen@google.com11399402013-03-20 17:45:27 +0000480 if (reuseBitmap) {
481 bm->notifyPixelsChanged();
482 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000483 jpeg_finish_decompress(&cinfo);
484
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000485 return true;
486}
487
djsollen@google.com11399402013-03-20 17:45:27 +0000488#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
489bool SkJPEGImageDecoder::onBuildTileIndex(SkStream* stream, int *width, int *height) {
490
491 SkJPEGImageIndex* imageIndex = SkNEW_ARGS(SkJPEGImageIndex, (stream, this));
492 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
493 huffman_index* huffmanIndex = imageIndex->huffmanIndex();
494
495 skjpeg_error_mgr sk_err;
496 cinfo->err = jpeg_std_error(&sk_err);
497 sk_err.error_exit = skjpeg_error_exit;
498
499 // All objects need to be instantiated before this setjmp call so that
500 // they will be cleaned up properly if an error occurs.
501 if (setjmp(sk_err.fJmpBuf)) {
502 return false;
503 }
504
505 // create the cinfo used to create/build the huffmanIndex
506 imageIndex->initializeInfo();
507 cinfo->do_fancy_upsampling = 0;
508 cinfo->do_block_smoothing = 0;
509
510 int status = jpeg_read_header(cinfo, true);
511 if (JPEG_HEADER_OK != status) {
512 SkDELETE(imageIndex);
513 return false;
514 }
515
516 jpeg_create_huffman_index(cinfo, huffmanIndex);
517 cinfo->scale_num = 1;
518 cinfo->scale_denom = 1;
519 if (!jpeg_build_huffman_index(cinfo, huffmanIndex)) {
520 SkDELETE(imageIndex);
521 return false;
522 }
523
524 // destroy the cinfo used to create/build the huffman index
525 jpeg_destroy_decompress(cinfo);
526
527 // Init decoder to image decode mode
528 imageIndex->initializeInfo();
529
530 status = jpeg_read_header(cinfo, true);
531 if (JPEG_HEADER_OK != status) {
532 SkDELETE(imageIndex);
533 return false;
534 }
535
536 cinfo->out_color_space = JCS_RGBA_8888;
537 cinfo->do_fancy_upsampling = 0;
538 cinfo->do_block_smoothing = 0;
539
540 // instead of jpeg_start_decompress() we start a tiled decompress
541 jpeg_start_tile_decompress(cinfo);
542
543 cinfo->scale_num = 1;
544 *height = cinfo->output_height;
545 *width = cinfo->output_width;
546 fImageWidth = *width;
547 fImageHeight = *height;
548
549 SkDELETE(fImageIndex);
550 fImageIndex = imageIndex;
551
552 return true;
553}
554
555bool SkJPEGImageDecoder::onDecodeRegion(SkBitmap* bm, const SkIRect& region) {
556 if (NULL == fImageIndex) {
557 return false;
558 }
559 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
560
561 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
562 if (!rect.intersect(region)) {
563 // If the requested region is entirely outside the image return false
564 return false;
565 }
566
567
568 skjpeg_error_mgr errorManager;
569 cinfo->err = jpeg_std_error(&errorManager);
570 errorManager.error_exit = skjpeg_error_exit;
571 if (setjmp(errorManager.fJmpBuf)) {
572 return false;
573 }
574
575 int requestedSampleSize = this->getSampleSize();
576 cinfo->scale_denom = requestedSampleSize;
577
578 if (this->getPreferQualityOverSpeed()) {
579 cinfo->dct_method = JDCT_ISLOW;
580 } else {
581 cinfo->dct_method = JDCT_IFAST;
582 }
583
584 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
585 if (config != SkBitmap::kARGB_8888_Config &&
586 config != SkBitmap::kARGB_4444_Config &&
587 config != SkBitmap::kRGB_565_Config) {
588 config = SkBitmap::kARGB_8888_Config;
589 }
590
591 /* default format is RGB */
592 cinfo->out_color_space = JCS_RGB;
593
594#ifdef ANDROID_RGB
595 cinfo->dither_mode = JDITHER_NONE;
596 if (SkBitmap::kARGB_8888_Config == config) {
597 cinfo->out_color_space = JCS_RGBA_8888;
598 } else if (SkBitmap::kRGB_565_Config == config) {
599 cinfo->out_color_space = JCS_RGB_565;
600 if (this->getDitherImage()) {
601 cinfo->dither_mode = JDITHER_ORDERED;
602 }
603 }
604#endif
605
606 int startX = rect.fLeft;
607 int startY = rect.fTop;
608 int width = rect.width();
609 int height = rect.height();
610
611 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
612 &startX, &startY, &width, &height);
613 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
614 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
615
616 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
617
618 SkBitmap bitmap;
619 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
620 bitmap.setIsOpaque(true);
621
622 // Check ahead of time if the swap(dest, src) is possible or not.
623 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
624 // swap happening. If no, then we will use alloc to allocate pixels to
625 // prevent garbage collection.
626 int w = rect.width() / actualSampleSize;
627 int h = rect.height() / actualSampleSize;
628 bool swapOnly = (rect == region) && bm->isNull() &&
629 (w == bitmap.width()) && (h == bitmap.height()) &&
630 ((startX - rect.x()) / actualSampleSize == 0) &&
631 ((startY - rect.y()) / actualSampleSize == 0);
632 if (swapOnly) {
633 if (!this->allocPixelRef(&bitmap, NULL)) {
634 return return_false(*cinfo, bitmap, "allocPixelRef");
635 }
636 } else {
637 if (!bitmap.allocPixels()) {
638 return return_false(*cinfo, bitmap, "allocPixels");
639 }
640 }
641
642 SkAutoLockPixels alp(bitmap);
643
644#ifdef ANDROID_RGB
645 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
646 a significant performance boost.
647 */
648 if (skiaSampleSize == 1 &&
649 ((config == SkBitmap::kARGB_8888_Config &&
650 cinfo->out_color_space == JCS_RGBA_8888) ||
651 (config == SkBitmap::kRGB_565_Config &&
652 cinfo->out_color_space == JCS_RGB_565)))
653 {
654 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
655 INT32 const bpr = bitmap.rowBytes();
656 int rowTotalCount = 0;
657
658 while (rowTotalCount < height) {
659 int rowCount = jpeg_read_tile_scanline(cinfo,
660 fImageIndex->huffmanIndex(),
661 &rowptr);
662 // if row_count == 0, then we didn't get a scanline, so abort.
663 // if we supported partial images, we might return true in this case
664 if (0 == rowCount) {
665 return return_false(*cinfo, bitmap, "read_scanlines");
666 }
667 if (this->shouldCancelDecode()) {
668 return return_false(*cinfo, bitmap, "shouldCancelDecode");
669 }
670 rowTotalCount += rowCount;
671 rowptr += bpr;
672 }
673
674 if (swapOnly) {
675 bm->swap(bitmap);
676 } else {
677 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
678 region.width(), region.height(), startX, startY);
679 }
680 return true;
681 }
682#endif
683
684 // check for supported formats
685 SkScaledBitmapSampler::SrcConfig sc;
686 if (JCS_CMYK == cinfo->out_color_space) {
687 // In this case we will manually convert the CMYK values to RGB
688 sc = SkScaledBitmapSampler::kRGBX;
689 } else if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_space) {
690 sc = SkScaledBitmapSampler::kRGB;
691#ifdef ANDROID_RGB
692 } else if (JCS_RGBA_8888 == cinfo->out_color_space) {
693 sc = SkScaledBitmapSampler::kRGBX;
694 } else if (JCS_RGB_565 == cinfo->out_color_space) {
695 sc = SkScaledBitmapSampler::kRGB_565;
696#endif
697 } else if (1 == cinfo->out_color_components &&
698 JCS_GRAYSCALE == cinfo->out_color_space) {
699 sc = SkScaledBitmapSampler::kGray;
700 } else {
701 return return_false(*cinfo, *bm, "jpeg colorspace");
702 }
703
704 if (!sampler.begin(&bitmap, sc, this->getDitherImage())) {
705 return return_false(*cinfo, bitmap, "sampler.begin");
706 }
707
708 // The CMYK work-around relies on 4 components per pixel here
709 SkAutoMalloc srcStorage(width * 4);
710 uint8_t* srcRow = (uint8_t*)srcStorage.get();
711
712 // Possibly skip initial rows [sampler.srcY0]
713 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
714 return return_false(*cinfo, bitmap, "skip rows");
715 }
716
717 // now loop through scanlines until y == bitmap->height() - 1
718 for (int y = 0;; y++) {
719 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
720 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
721 if (0 == row_count) {
722 return return_false(*cinfo, bitmap, "read_scanlines");
723 }
724 if (this->shouldCancelDecode()) {
725 return return_false(*cinfo, bitmap, "shouldCancelDecode");
726 }
727
728 if (JCS_CMYK == cinfo->out_color_space) {
729 convert_CMYK_to_RGB(srcRow, width);
730 }
731
732 sampler.next(srcRow);
733 if (bitmap.height() - 1 == y) {
734 // we're done
735 break;
736 }
737
738 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
739 sampler.srcDY() - 1)) {
740 return return_false(*cinfo, bitmap, "skip rows");
741 }
742 }
743 if (swapOnly) {
744 bm->swap(bitmap);
745 } else {
746 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
747 region.width(), region.height(), startX, startY);
748 }
749 return true;
750}
751#endif
752
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000753///////////////////////////////////////////////////////////////////////////////
754
755#include "SkColorPriv.h"
756
757// taken from jcolor.c in libjpeg
758#if 0 // 16bit - precise but slow
759 #define CYR 19595 // 0.299
760 #define CYG 38470 // 0.587
761 #define CYB 7471 // 0.114
762
763 #define CUR -11059 // -0.16874
764 #define CUG -21709 // -0.33126
765 #define CUB 32768 // 0.5
766
767 #define CVR 32768 // 0.5
768 #define CVG -27439 // -0.41869
769 #define CVB -5329 // -0.08131
770
771 #define CSHIFT 16
772#else // 8bit - fast, slightly less precise
773 #define CYR 77 // 0.299
774 #define CYG 150 // 0.587
775 #define CYB 29 // 0.114
776
777 #define CUR -43 // -0.16874
778 #define CUG -85 // -0.33126
779 #define CUB 128 // 0.5
780
781 #define CVR 128 // 0.5
782 #define CVG -107 // -0.41869
783 #define CVB -21 // -0.08131
784
785 #define CSHIFT 8
786#endif
787
788static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
789 int r = SkGetPackedR32(c);
790 int g = SkGetPackedG32(c);
791 int b = SkGetPackedB32(c);
792
793 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
794 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
795 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
796
797 dst[0] = SkToU8(y);
798 dst[1] = SkToU8(u + 128);
799 dst[2] = SkToU8(v + 128);
800}
801
802static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
803 int r = SkGetPackedR4444(c);
804 int g = SkGetPackedG4444(c);
805 int b = SkGetPackedB4444(c);
806
807 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
808 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
809 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
810
811 dst[0] = SkToU8(y);
812 dst[1] = SkToU8(u + 128);
813 dst[2] = SkToU8(v + 128);
814}
815
816static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
817 int r = SkGetPackedR16(c);
818 int g = SkGetPackedG16(c);
819 int b = SkGetPackedB16(c);
820
821 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
822 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
823 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
824
825 dst[0] = SkToU8(y);
826 dst[1] = SkToU8(u + 128);
827 dst[2] = SkToU8(v + 128);
828}
829
830///////////////////////////////////////////////////////////////////////////////
831
832typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
833 const void* SK_RESTRICT src, int width,
834 const SkPMColor* SK_RESTRICT ctable);
835
836static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
837 const void* SK_RESTRICT srcRow, int width,
838 const SkPMColor*) {
839 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
840 while (--width >= 0) {
841#ifdef WE_CONVERT_TO_YUV
842 rgb2yuv_32(dst, *src++);
843#else
844 uint32_t c = *src++;
845 dst[0] = SkGetPackedR32(c);
846 dst[1] = SkGetPackedG32(c);
847 dst[2] = SkGetPackedB32(c);
848#endif
849 dst += 3;
850 }
851}
852
853static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
854 const void* SK_RESTRICT srcRow, int width,
855 const SkPMColor*) {
856 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
857 while (--width >= 0) {
858#ifdef WE_CONVERT_TO_YUV
859 rgb2yuv_4444(dst, *src++);
860#else
861 SkPMColor16 c = *src++;
862 dst[0] = SkPacked4444ToR32(c);
863 dst[1] = SkPacked4444ToG32(c);
864 dst[2] = SkPacked4444ToB32(c);
865#endif
866 dst += 3;
867 }
868}
869
870static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
871 const void* SK_RESTRICT srcRow, int width,
872 const SkPMColor*) {
873 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
874 while (--width >= 0) {
875#ifdef WE_CONVERT_TO_YUV
876 rgb2yuv_16(dst, *src++);
877#else
878 uint16_t c = *src++;
879 dst[0] = SkPacked16ToR32(c);
880 dst[1] = SkPacked16ToG32(c);
881 dst[2] = SkPacked16ToB32(c);
882#endif
883 dst += 3;
884 }
885}
886
887static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
888 const void* SK_RESTRICT srcRow, int width,
889 const SkPMColor* SK_RESTRICT ctable) {
890 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
891 while (--width >= 0) {
892#ifdef WE_CONVERT_TO_YUV
893 rgb2yuv_32(dst, ctable[*src++]);
894#else
895 uint32_t c = ctable[*src++];
896 dst[0] = SkGetPackedR32(c);
897 dst[1] = SkGetPackedG32(c);
898 dst[2] = SkGetPackedB32(c);
899#endif
900 dst += 3;
901 }
902}
903
904static WriteScanline ChooseWriter(const SkBitmap& bm) {
905 switch (bm.config()) {
906 case SkBitmap::kARGB_8888_Config:
907 return Write_32_YUV;
908 case SkBitmap::kRGB_565_Config:
909 return Write_16_YUV;
910 case SkBitmap::kARGB_4444_Config:
911 return Write_4444_YUV;
912 case SkBitmap::kIndex8_Config:
913 return Write_Index_YUV;
914 default:
915 return NULL;
916 }
917}
918
919class SkJPEGImageEncoder : public SkImageEncoder {
920protected:
921 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
922#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +0000923 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000924#endif
925
926 const WriteScanline writer = ChooseWriter(bm);
927 if (NULL == writer) {
928 return false;
929 }
930
931 SkAutoLockPixels alp(bm);
932 if (NULL == bm.getPixels()) {
933 return false;
934 }
935
936 jpeg_compress_struct cinfo;
937 skjpeg_error_mgr sk_err;
938 skjpeg_destination_mgr sk_wstream(stream);
939
940 // allocate these before set call setjmp
941 SkAutoMalloc oneRow;
942 SkAutoLockColors ctLocker;
943
944 cinfo.err = jpeg_std_error(&sk_err);
945 sk_err.error_exit = skjpeg_error_exit;
946 if (setjmp(sk_err.fJmpBuf)) {
947 return false;
948 }
949 jpeg_create_compress(&cinfo);
950
951 cinfo.dest = &sk_wstream;
952 cinfo.image_width = bm.width();
953 cinfo.image_height = bm.height();
954 cinfo.input_components = 3;
955#ifdef WE_CONVERT_TO_YUV
956 cinfo.in_color_space = JCS_YCbCr;
957#else
958 cinfo.in_color_space = JCS_RGB;
959#endif
960 cinfo.input_gamma = 1;
961
962 jpeg_set_defaults(&cinfo);
963 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
964 cinfo.dct_method = JDCT_IFAST;
965
966 jpeg_start_compress(&cinfo, TRUE);
967
968 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000969 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000970
971 const SkPMColor* colors = ctLocker.lockColors(bm);
972 const void* srcRow = bm.getPixels();
973
974 while (cinfo.next_scanline < cinfo.image_height) {
975 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
976
977 writer(oneRowP, srcRow, width, colors);
978 row_pointer[0] = oneRowP;
979 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
980 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
981 }
982
983 jpeg_finish_compress(&cinfo);
984 jpeg_destroy_compress(&cinfo);
985
986 return true;
987 }
988};
989
990///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000991DEFINE_DECODER_CREATOR(JPEGImageDecoder);
992DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
993///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000994
995#include "SkTRegistry.h"
996
caryclark@google.com2a2cc202012-06-06 12:04:36 +0000997static SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000998 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000999 static const size_t HEADER_SIZE = sizeof(gHeader);
1000
1001 char buffer[HEADER_SIZE];
1002 size_t len = stream->read(buffer, HEADER_SIZE);
1003
1004 if (len != HEADER_SIZE) {
1005 return NULL; // can't read enough
1006 }
1007 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
1008 return NULL;
1009 }
1010 return SkNEW(SkJPEGImageDecoder);
1011}
1012
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001013static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001014 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1015}
1016
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001017
1018static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory);
1019static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efactory);