blob: 4818743605b5f62e486a11aed0fccad6d2af645e [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);
360
361 bm->lockPixels();
362 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
363 bm->unlockPixels();
364 bool reuseBitmap = (rowptr != NULL);
365
366 if (reuseBitmap) {
367 if (sampler.scaledWidth() != bm->width() ||
368 sampler.scaledHeight() != bm->height()) {
369 // Dimensions must match
370 return false;
371 } else if (SkImageDecoder::kDecodeBounds_Mode == mode) {
372 return true;
373 }
374 } else {
375 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
376 bm->setIsOpaque(true);
377 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
378 return true;
379 }
380 if (!this->allocPixelRef(bm, NULL)) {
381 return return_false(cinfo, *bm, "allocPixelRef");
382 }
383 }
384
385 SkAutoLockPixels alp(*bm);
386
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000387#ifdef ANDROID_RGB
388 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
389 a significant performance boost.
390 */
391 if (sampleSize == 1 &&
rmistry@google.comd6176b02012-08-23 18:14:13 +0000392 ((config == SkBitmap::kARGB_8888_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000393 cinfo.out_color_space == JCS_RGBA_8888) ||
rmistry@google.comd6176b02012-08-23 18:14:13 +0000394 (config == SkBitmap::kRGB_565_Config &&
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000395 cinfo.out_color_space == JCS_RGB_565)))
396 {
djsollen@google.com11399402013-03-20 17:45:27 +0000397 rowptr = (JSAMPLE*)bm->getPixels();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000398 INT32 const bpr = bm->rowBytes();
rmistry@google.comd6176b02012-08-23 18:14:13 +0000399
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000400 while (cinfo.output_scanline < cinfo.output_height) {
401 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
402 // if row_count == 0, then we didn't get a scanline, so abort.
403 // if we supported partial images, we might return true in this case
404 if (0 == row_count) {
405 return return_false(cinfo, *bm, "read_scanlines");
406 }
407 if (this->shouldCancelDecode()) {
408 return return_false(cinfo, *bm, "shouldCancelDecode");
409 }
410 rowptr += bpr;
411 }
djsollen@google.com11399402013-03-20 17:45:27 +0000412 if (reuseBitmap) {
413 bm->notifyPixelsChanged();
414 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000415 jpeg_finish_decompress(&cinfo);
416 return true;
417 }
418#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000419
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000420 // check for supported formats
421 SkScaledBitmapSampler::SrcConfig sc;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000422 if (JCS_CMYK == cinfo.out_color_space) {
423 // In this case we will manually convert the CMYK values to RGB
424 sc = SkScaledBitmapSampler::kRGBX;
425 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000426 sc = SkScaledBitmapSampler::kRGB;
427#ifdef ANDROID_RGB
428 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
429 sc = SkScaledBitmapSampler::kRGBX;
430 } else if (JCS_RGB_565 == cinfo.out_color_space) {
431 sc = SkScaledBitmapSampler::kRGB_565;
432#endif
433 } else if (1 == cinfo.out_color_components &&
434 JCS_GRAYSCALE == cinfo.out_color_space) {
435 sc = SkScaledBitmapSampler::kGray;
436 } else {
437 return return_false(cinfo, *bm, "jpeg colorspace");
438 }
439
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000440 if (!sampler.begin(bm, sc, this->getDitherImage())) {
441 return return_false(cinfo, *bm, "sampler.begin");
442 }
443
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000444 // The CMYK work-around relies on 4 components per pixel here
djsollen@google.com11399402013-03-20 17:45:27 +0000445 SkAutoMalloc srcStorage(cinfo.output_width * 4);
446 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000447
448 // Possibly skip initial rows [sampler.srcY0]
449 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
450 return return_false(cinfo, *bm, "skip rows");
451 }
452
453 // now loop through scanlines until y == bm->height() - 1
454 for (int y = 0;; y++) {
455 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
456 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
457 if (0 == row_count) {
458 return return_false(cinfo, *bm, "read_scanlines");
459 }
460 if (this->shouldCancelDecode()) {
461 return return_false(cinfo, *bm, "shouldCancelDecode");
462 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000463
464 if (JCS_CMYK == cinfo.out_color_space) {
465 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
466 }
467
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000468 sampler.next(srcRow);
469 if (bm->height() - 1 == y) {
470 // we're done
471 break;
472 }
473
474 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
475 return return_false(cinfo, *bm, "skip rows");
476 }
477 }
478
479 // we formally skip the rest, so we don't get a complaint from libjpeg
480 if (!skip_src_rows(&cinfo, srcRow,
481 cinfo.output_height - cinfo.output_scanline)) {
482 return return_false(cinfo, *bm, "skip rows");
483 }
djsollen@google.com11399402013-03-20 17:45:27 +0000484 if (reuseBitmap) {
485 bm->notifyPixelsChanged();
486 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000487 jpeg_finish_decompress(&cinfo);
488
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000489 return true;
490}
491
djsollen@google.com11399402013-03-20 17:45:27 +0000492#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
493bool SkJPEGImageDecoder::onBuildTileIndex(SkStream* stream, int *width, int *height) {
494
495 SkJPEGImageIndex* imageIndex = SkNEW_ARGS(SkJPEGImageIndex, (stream, this));
496 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
497 huffman_index* huffmanIndex = imageIndex->huffmanIndex();
498
499 skjpeg_error_mgr sk_err;
500 cinfo->err = jpeg_std_error(&sk_err);
501 sk_err.error_exit = skjpeg_error_exit;
502
503 // All objects need to be instantiated before this setjmp call so that
504 // they will be cleaned up properly if an error occurs.
505 if (setjmp(sk_err.fJmpBuf)) {
506 return false;
507 }
508
509 // create the cinfo used to create/build the huffmanIndex
510 imageIndex->initializeInfo();
511 cinfo->do_fancy_upsampling = 0;
512 cinfo->do_block_smoothing = 0;
513
514 int status = jpeg_read_header(cinfo, true);
515 if (JPEG_HEADER_OK != status) {
516 SkDELETE(imageIndex);
517 return false;
518 }
519
520 jpeg_create_huffman_index(cinfo, huffmanIndex);
521 cinfo->scale_num = 1;
522 cinfo->scale_denom = 1;
523 if (!jpeg_build_huffman_index(cinfo, huffmanIndex)) {
524 SkDELETE(imageIndex);
525 return false;
526 }
527
528 // destroy the cinfo used to create/build the huffman index
529 jpeg_destroy_decompress(cinfo);
530
531 // Init decoder to image decode mode
532 imageIndex->initializeInfo();
533
534 status = jpeg_read_header(cinfo, true);
535 if (JPEG_HEADER_OK != status) {
536 SkDELETE(imageIndex);
537 return false;
538 }
539
540 cinfo->out_color_space = JCS_RGBA_8888;
541 cinfo->do_fancy_upsampling = 0;
542 cinfo->do_block_smoothing = 0;
543
544 // instead of jpeg_start_decompress() we start a tiled decompress
545 jpeg_start_tile_decompress(cinfo);
546
547 cinfo->scale_num = 1;
548 *height = cinfo->output_height;
549 *width = cinfo->output_width;
550 fImageWidth = *width;
551 fImageHeight = *height;
552
553 SkDELETE(fImageIndex);
554 fImageIndex = imageIndex;
555
556 return true;
557}
558
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000559bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000560 if (NULL == fImageIndex) {
561 return false;
562 }
563 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
564
565 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
566 if (!rect.intersect(region)) {
567 // If the requested region is entirely outside the image return false
568 return false;
569 }
570
571
572 skjpeg_error_mgr errorManager;
573 cinfo->err = jpeg_std_error(&errorManager);
574 errorManager.error_exit = skjpeg_error_exit;
575 if (setjmp(errorManager.fJmpBuf)) {
576 return false;
577 }
578
579 int requestedSampleSize = this->getSampleSize();
580 cinfo->scale_denom = requestedSampleSize;
581
582 if (this->getPreferQualityOverSpeed()) {
583 cinfo->dct_method = JDCT_ISLOW;
584 } else {
585 cinfo->dct_method = JDCT_IFAST;
586 }
587
588 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
589 if (config != SkBitmap::kARGB_8888_Config &&
590 config != SkBitmap::kARGB_4444_Config &&
591 config != SkBitmap::kRGB_565_Config) {
592 config = SkBitmap::kARGB_8888_Config;
593 }
594
595 /* default format is RGB */
596 cinfo->out_color_space = JCS_RGB;
597
598#ifdef ANDROID_RGB
599 cinfo->dither_mode = JDITHER_NONE;
600 if (SkBitmap::kARGB_8888_Config == config) {
601 cinfo->out_color_space = JCS_RGBA_8888;
602 } else if (SkBitmap::kRGB_565_Config == config) {
603 cinfo->out_color_space = JCS_RGB_565;
604 if (this->getDitherImage()) {
605 cinfo->dither_mode = JDITHER_ORDERED;
606 }
607 }
608#endif
609
610 int startX = rect.fLeft;
611 int startY = rect.fTop;
612 int width = rect.width();
613 int height = rect.height();
614
615 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
616 &startX, &startY, &width, &height);
617 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
618 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
619
620 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
621
622 SkBitmap bitmap;
623 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
624 bitmap.setIsOpaque(true);
625
626 // Check ahead of time if the swap(dest, src) is possible or not.
627 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
628 // swap happening. If no, then we will use alloc to allocate pixels to
629 // prevent garbage collection.
630 int w = rect.width() / actualSampleSize;
631 int h = rect.height() / actualSampleSize;
632 bool swapOnly = (rect == region) && bm->isNull() &&
633 (w == bitmap.width()) && (h == bitmap.height()) &&
634 ((startX - rect.x()) / actualSampleSize == 0) &&
635 ((startY - rect.y()) / actualSampleSize == 0);
636 if (swapOnly) {
637 if (!this->allocPixelRef(&bitmap, NULL)) {
638 return return_false(*cinfo, bitmap, "allocPixelRef");
639 }
640 } else {
641 if (!bitmap.allocPixels()) {
642 return return_false(*cinfo, bitmap, "allocPixels");
643 }
644 }
645
646 SkAutoLockPixels alp(bitmap);
647
648#ifdef ANDROID_RGB
649 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
650 a significant performance boost.
651 */
652 if (skiaSampleSize == 1 &&
653 ((config == SkBitmap::kARGB_8888_Config &&
654 cinfo->out_color_space == JCS_RGBA_8888) ||
655 (config == SkBitmap::kRGB_565_Config &&
656 cinfo->out_color_space == JCS_RGB_565)))
657 {
658 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
659 INT32 const bpr = bitmap.rowBytes();
660 int rowTotalCount = 0;
661
662 while (rowTotalCount < height) {
663 int rowCount = jpeg_read_tile_scanline(cinfo,
664 fImageIndex->huffmanIndex(),
665 &rowptr);
666 // if row_count == 0, then we didn't get a scanline, so abort.
667 // if we supported partial images, we might return true in this case
668 if (0 == rowCount) {
669 return return_false(*cinfo, bitmap, "read_scanlines");
670 }
671 if (this->shouldCancelDecode()) {
672 return return_false(*cinfo, bitmap, "shouldCancelDecode");
673 }
674 rowTotalCount += rowCount;
675 rowptr += bpr;
676 }
677
678 if (swapOnly) {
679 bm->swap(bitmap);
680 } else {
681 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
682 region.width(), region.height(), startX, startY);
683 }
684 return true;
685 }
686#endif
687
688 // check for supported formats
689 SkScaledBitmapSampler::SrcConfig sc;
690 if (JCS_CMYK == cinfo->out_color_space) {
691 // In this case we will manually convert the CMYK values to RGB
692 sc = SkScaledBitmapSampler::kRGBX;
693 } else if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_space) {
694 sc = SkScaledBitmapSampler::kRGB;
695#ifdef ANDROID_RGB
696 } else if (JCS_RGBA_8888 == cinfo->out_color_space) {
697 sc = SkScaledBitmapSampler::kRGBX;
698 } else if (JCS_RGB_565 == cinfo->out_color_space) {
699 sc = SkScaledBitmapSampler::kRGB_565;
700#endif
701 } else if (1 == cinfo->out_color_components &&
702 JCS_GRAYSCALE == cinfo->out_color_space) {
703 sc = SkScaledBitmapSampler::kGray;
704 } else {
705 return return_false(*cinfo, *bm, "jpeg colorspace");
706 }
707
708 if (!sampler.begin(&bitmap, sc, this->getDitherImage())) {
709 return return_false(*cinfo, bitmap, "sampler.begin");
710 }
711
712 // The CMYK work-around relies on 4 components per pixel here
713 SkAutoMalloc srcStorage(width * 4);
714 uint8_t* srcRow = (uint8_t*)srcStorage.get();
715
716 // Possibly skip initial rows [sampler.srcY0]
717 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
718 return return_false(*cinfo, bitmap, "skip rows");
719 }
720
721 // now loop through scanlines until y == bitmap->height() - 1
722 for (int y = 0;; y++) {
723 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
724 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
725 if (0 == row_count) {
726 return return_false(*cinfo, bitmap, "read_scanlines");
727 }
728 if (this->shouldCancelDecode()) {
729 return return_false(*cinfo, bitmap, "shouldCancelDecode");
730 }
731
732 if (JCS_CMYK == cinfo->out_color_space) {
733 convert_CMYK_to_RGB(srcRow, width);
734 }
735
736 sampler.next(srcRow);
737 if (bitmap.height() - 1 == y) {
738 // we're done
739 break;
740 }
741
742 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
743 sampler.srcDY() - 1)) {
744 return return_false(*cinfo, bitmap, "skip rows");
745 }
746 }
747 if (swapOnly) {
748 bm->swap(bitmap);
749 } else {
750 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
751 region.width(), region.height(), startX, startY);
752 }
753 return true;
754}
755#endif
756
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000757///////////////////////////////////////////////////////////////////////////////
758
759#include "SkColorPriv.h"
760
761// taken from jcolor.c in libjpeg
762#if 0 // 16bit - precise but slow
763 #define CYR 19595 // 0.299
764 #define CYG 38470 // 0.587
765 #define CYB 7471 // 0.114
766
767 #define CUR -11059 // -0.16874
768 #define CUG -21709 // -0.33126
769 #define CUB 32768 // 0.5
770
771 #define CVR 32768 // 0.5
772 #define CVG -27439 // -0.41869
773 #define CVB -5329 // -0.08131
774
775 #define CSHIFT 16
776#else // 8bit - fast, slightly less precise
777 #define CYR 77 // 0.299
778 #define CYG 150 // 0.587
779 #define CYB 29 // 0.114
780
781 #define CUR -43 // -0.16874
782 #define CUG -85 // -0.33126
783 #define CUB 128 // 0.5
784
785 #define CVR 128 // 0.5
786 #define CVG -107 // -0.41869
787 #define CVB -21 // -0.08131
788
789 #define CSHIFT 8
790#endif
791
792static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
793 int r = SkGetPackedR32(c);
794 int g = SkGetPackedG32(c);
795 int b = SkGetPackedB32(c);
796
797 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
798 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
799 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
800
801 dst[0] = SkToU8(y);
802 dst[1] = SkToU8(u + 128);
803 dst[2] = SkToU8(v + 128);
804}
805
806static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
807 int r = SkGetPackedR4444(c);
808 int g = SkGetPackedG4444(c);
809 int b = SkGetPackedB4444(c);
810
811 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
812 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
813 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
814
815 dst[0] = SkToU8(y);
816 dst[1] = SkToU8(u + 128);
817 dst[2] = SkToU8(v + 128);
818}
819
820static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
821 int r = SkGetPackedR16(c);
822 int g = SkGetPackedG16(c);
823 int b = SkGetPackedB16(c);
824
825 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
826 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
827 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
828
829 dst[0] = SkToU8(y);
830 dst[1] = SkToU8(u + 128);
831 dst[2] = SkToU8(v + 128);
832}
833
834///////////////////////////////////////////////////////////////////////////////
835
836typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
837 const void* SK_RESTRICT src, int width,
838 const SkPMColor* SK_RESTRICT ctable);
839
840static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
841 const void* SK_RESTRICT srcRow, int width,
842 const SkPMColor*) {
843 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
844 while (--width >= 0) {
845#ifdef WE_CONVERT_TO_YUV
846 rgb2yuv_32(dst, *src++);
847#else
848 uint32_t c = *src++;
849 dst[0] = SkGetPackedR32(c);
850 dst[1] = SkGetPackedG32(c);
851 dst[2] = SkGetPackedB32(c);
852#endif
853 dst += 3;
854 }
855}
856
857static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
858 const void* SK_RESTRICT srcRow, int width,
859 const SkPMColor*) {
860 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
861 while (--width >= 0) {
862#ifdef WE_CONVERT_TO_YUV
863 rgb2yuv_4444(dst, *src++);
864#else
865 SkPMColor16 c = *src++;
866 dst[0] = SkPacked4444ToR32(c);
867 dst[1] = SkPacked4444ToG32(c);
868 dst[2] = SkPacked4444ToB32(c);
869#endif
870 dst += 3;
871 }
872}
873
874static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
875 const void* SK_RESTRICT srcRow, int width,
876 const SkPMColor*) {
877 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
878 while (--width >= 0) {
879#ifdef WE_CONVERT_TO_YUV
880 rgb2yuv_16(dst, *src++);
881#else
882 uint16_t c = *src++;
883 dst[0] = SkPacked16ToR32(c);
884 dst[1] = SkPacked16ToG32(c);
885 dst[2] = SkPacked16ToB32(c);
886#endif
887 dst += 3;
888 }
889}
890
891static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
892 const void* SK_RESTRICT srcRow, int width,
893 const SkPMColor* SK_RESTRICT ctable) {
894 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
895 while (--width >= 0) {
896#ifdef WE_CONVERT_TO_YUV
897 rgb2yuv_32(dst, ctable[*src++]);
898#else
899 uint32_t c = ctable[*src++];
900 dst[0] = SkGetPackedR32(c);
901 dst[1] = SkGetPackedG32(c);
902 dst[2] = SkGetPackedB32(c);
903#endif
904 dst += 3;
905 }
906}
907
908static WriteScanline ChooseWriter(const SkBitmap& bm) {
909 switch (bm.config()) {
910 case SkBitmap::kARGB_8888_Config:
911 return Write_32_YUV;
912 case SkBitmap::kRGB_565_Config:
913 return Write_16_YUV;
914 case SkBitmap::kARGB_4444_Config:
915 return Write_4444_YUV;
916 case SkBitmap::kIndex8_Config:
917 return Write_Index_YUV;
918 default:
919 return NULL;
920 }
921}
922
923class SkJPEGImageEncoder : public SkImageEncoder {
924protected:
925 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
926#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +0000927 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000928#endif
929
930 const WriteScanline writer = ChooseWriter(bm);
931 if (NULL == writer) {
932 return false;
933 }
934
935 SkAutoLockPixels alp(bm);
936 if (NULL == bm.getPixels()) {
937 return false;
938 }
939
940 jpeg_compress_struct cinfo;
941 skjpeg_error_mgr sk_err;
942 skjpeg_destination_mgr sk_wstream(stream);
943
944 // allocate these before set call setjmp
945 SkAutoMalloc oneRow;
946 SkAutoLockColors ctLocker;
947
948 cinfo.err = jpeg_std_error(&sk_err);
949 sk_err.error_exit = skjpeg_error_exit;
950 if (setjmp(sk_err.fJmpBuf)) {
951 return false;
952 }
953 jpeg_create_compress(&cinfo);
954
955 cinfo.dest = &sk_wstream;
956 cinfo.image_width = bm.width();
957 cinfo.image_height = bm.height();
958 cinfo.input_components = 3;
959#ifdef WE_CONVERT_TO_YUV
960 cinfo.in_color_space = JCS_YCbCr;
961#else
962 cinfo.in_color_space = JCS_RGB;
963#endif
964 cinfo.input_gamma = 1;
965
966 jpeg_set_defaults(&cinfo);
967 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +0000968#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000969 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +0000970#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000971
972 jpeg_start_compress(&cinfo, TRUE);
973
974 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000975 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000976
977 const SkPMColor* colors = ctLocker.lockColors(bm);
978 const void* srcRow = bm.getPixels();
979
980 while (cinfo.next_scanline < cinfo.image_height) {
981 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
982
983 writer(oneRowP, srcRow, width, colors);
984 row_pointer[0] = oneRowP;
985 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
986 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
987 }
988
989 jpeg_finish_compress(&cinfo);
990 jpeg_destroy_compress(&cinfo);
991
992 return true;
993 }
994};
995
996///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000997DEFINE_DECODER_CREATOR(JPEGImageDecoder);
998DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
999///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001000
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001001static bool is_jpeg(SkStream* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +00001002 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001003 static const size_t HEADER_SIZE = sizeof(gHeader);
1004
1005 char buffer[HEADER_SIZE];
1006 size_t len = stream->read(buffer, HEADER_SIZE);
1007
1008 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001009 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001010 }
1011 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001012 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001013 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001014 return true;
1015}
1016
1017#include "SkTRegistry.h"
1018
1019static SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) {
1020 if (is_jpeg(stream)) {
1021 return SkNEW(SkJPEGImageDecoder);
1022 }
1023 return NULL;
1024}
1025
1026static SkImageDecoder::Format get_format_jpeg(SkStream* stream) {
1027 if (is_jpeg(stream)) {
1028 return SkImageDecoder::kJPEG_Format;
1029 }
1030 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001031}
1032
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001033static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001034 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1035}
1036
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001037
1038static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory);
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001039static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_jpeg);
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001040static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efactory);