blob: e4d7ace0d48295d32c98ba4805909a2204e69afc [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
scroggo@google.comf698c822013-07-18 19:34:49 +0000292 SrcDepth srcDepth = k32Bit_SrcDepth;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000293 /* default format is RGB */
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000294 if (cinfo.jpeg_color_space == JCS_CMYK) {
295 // libjpeg cannot convert from CMYK to RGB - here we set up
296 // so libjpeg will give us CMYK samples back and we will
297 // later manually convert them to RGB
298 cinfo.out_color_space = JCS_CMYK;
scroggo@google.comf698c822013-07-18 19:34:49 +0000299 } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
300 cinfo.out_color_space = JCS_GRAYSCALE;
301 srcDepth = k8BitGray_SrcDepth;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000302 } else {
303 cinfo.out_color_space = JCS_RGB;
304 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000305
scroggo@google.comf698c822013-07-18 19:34:49 +0000306 SkBitmap::Config config = this->getPrefConfig(srcDepth, false);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000307 // only these make sense for jpegs
scroggo@google.comf698c822013-07-18 19:34:49 +0000308 if (SkBitmap::kA8_Config == config) {
309 if (cinfo.jpeg_color_space != JCS_GRAYSCALE) {
310 // Converting from a non grayscale image to A8 is
311 // not currently supported.
312 config = SkBitmap::kARGB_8888_Config;
313 // Change the output from jpeg back to RGB.
314 cinfo.out_color_space = JCS_RGB;
315 }
316 } else if (config != SkBitmap::kARGB_8888_Config &&
317 config != SkBitmap::kARGB_4444_Config &&
318 config != SkBitmap::kRGB_565_Config) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000319 config = SkBitmap::kARGB_8888_Config;
320 }
321
322#ifdef ANDROID_RGB
323 cinfo.dither_mode = JDITHER_NONE;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000324 if (SkBitmap::kARGB_8888_Config == config && JCS_CMYK != cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000325 cinfo.out_color_space = JCS_RGBA_8888;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000326 } else if (SkBitmap::kRGB_565_Config == config && JCS_CMYK != cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000327 cinfo.out_color_space = JCS_RGB_565;
328 if (this->getDitherImage()) {
329 cinfo.dither_mode = JDITHER_ORDERED;
330 }
331 }
332#endif
333
djsollen@google.com11399402013-03-20 17:45:27 +0000334 if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000335 bm->setConfig(config, cinfo.image_width, cinfo.image_height);
336 bm->setIsOpaque(true);
337 return true;
338 }
339
340 /* image_width and image_height are the original dimensions, available
341 after jpeg_read_header(). To see the scaled dimensions, we have to call
342 jpeg_start_decompress(), and then read output_width and output_height.
343 */
344 if (!jpeg_start_decompress(&cinfo)) {
345 /* If we failed here, we may still have enough information to return
346 to the caller if they just wanted (subsampled bounds). If sampleSize
347 was 1, then we would have already returned. Thus we just check if
348 we're in kDecodeBounds_Mode, and that we have valid output sizes.
349
350 One reason to fail here is that we have insufficient stream data
351 to complete the setup. However, output dimensions seem to get
352 computed very early, which is why this special check can pay off.
353 */
djsollen@google.com11399402013-03-20 17:45:27 +0000354 if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000355 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
356 recompute_sampleSize(sampleSize, cinfo));
357 bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
358 bm->setIsOpaque(true);
359 return true;
360 } else {
361 return return_false(cinfo, *bm, "start_decompress");
362 }
363 }
364 sampleSize = recompute_sampleSize(sampleSize, cinfo);
365
366 // should we allow the Chooser (if present) to pick a config for us???
djsollen@google.com11399402013-03-20 17:45:27 +0000367 if (!this->chooseFromOneChoice(config, cinfo.output_width, cinfo.output_height)) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000368 return return_false(cinfo, *bm, "chooseFromOneChoice");
369 }
370
djsollen@google.com11399402013-03-20 17:45:27 +0000371 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);
scroggo@google.combc69ce92013-07-09 15:45:14 +0000372 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
373 bm->setIsOpaque(true);
374 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
375 return true;
376 }
377 if (!this->allocPixelRef(bm, NULL)) {
378 return return_false(cinfo, *bm, "allocPixelRef");
djsollen@google.com11399402013-03-20 17:45:27 +0000379 }
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 {
scroggo@google.combc69ce92013-07-09 15:45:14 +0000393 JSAMPLE* 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 }
408 jpeg_finish_decompress(&cinfo);
409 return true;
410 }
411#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000412
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000413 // check for supported formats
414 SkScaledBitmapSampler::SrcConfig sc;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000415 if (JCS_CMYK == cinfo.out_color_space) {
416 // In this case we will manually convert the CMYK values to RGB
417 sc = SkScaledBitmapSampler::kRGBX;
418 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000419 sc = SkScaledBitmapSampler::kRGB;
420#ifdef ANDROID_RGB
421 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
422 sc = SkScaledBitmapSampler::kRGBX;
423 } else if (JCS_RGB_565 == cinfo.out_color_space) {
424 sc = SkScaledBitmapSampler::kRGB_565;
425#endif
426 } else if (1 == cinfo.out_color_components &&
427 JCS_GRAYSCALE == cinfo.out_color_space) {
428 sc = SkScaledBitmapSampler::kGray;
429 } else {
430 return return_false(cinfo, *bm, "jpeg colorspace");
431 }
432
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000433 if (!sampler.begin(bm, sc, this->getDitherImage())) {
434 return return_false(cinfo, *bm, "sampler.begin");
435 }
436
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000437 // The CMYK work-around relies on 4 components per pixel here
djsollen@google.com11399402013-03-20 17:45:27 +0000438 SkAutoMalloc srcStorage(cinfo.output_width * 4);
439 uint8_t* srcRow = (uint8_t*)srcStorage.get();
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000440
441 // Possibly skip initial rows [sampler.srcY0]
442 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
443 return return_false(cinfo, *bm, "skip rows");
444 }
445
446 // now loop through scanlines until y == bm->height() - 1
447 for (int y = 0;; y++) {
448 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
449 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
450 if (0 == row_count) {
451 return return_false(cinfo, *bm, "read_scanlines");
452 }
453 if (this->shouldCancelDecode()) {
454 return return_false(cinfo, *bm, "shouldCancelDecode");
455 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000456
457 if (JCS_CMYK == cinfo.out_color_space) {
458 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
459 }
460
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000461 sampler.next(srcRow);
462 if (bm->height() - 1 == y) {
463 // we're done
464 break;
465 }
466
467 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
468 return return_false(cinfo, *bm, "skip rows");
469 }
470 }
471
472 // we formally skip the rest, so we don't get a complaint from libjpeg
473 if (!skip_src_rows(&cinfo, srcRow,
474 cinfo.output_height - cinfo.output_scanline)) {
475 return return_false(cinfo, *bm, "skip rows");
476 }
477 jpeg_finish_decompress(&cinfo);
478
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000479 return true;
480}
481
djsollen@google.com11399402013-03-20 17:45:27 +0000482#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
483bool SkJPEGImageDecoder::onBuildTileIndex(SkStream* stream, int *width, int *height) {
484
485 SkJPEGImageIndex* imageIndex = SkNEW_ARGS(SkJPEGImageIndex, (stream, this));
486 jpeg_decompress_struct* cinfo = imageIndex->cinfo();
487 huffman_index* huffmanIndex = imageIndex->huffmanIndex();
488
489 skjpeg_error_mgr sk_err;
490 cinfo->err = jpeg_std_error(&sk_err);
491 sk_err.error_exit = skjpeg_error_exit;
492
493 // All objects need to be instantiated before this setjmp call so that
494 // they will be cleaned up properly if an error occurs.
495 if (setjmp(sk_err.fJmpBuf)) {
496 return false;
497 }
498
499 // create the cinfo used to create/build the huffmanIndex
500 imageIndex->initializeInfo();
501 cinfo->do_fancy_upsampling = 0;
502 cinfo->do_block_smoothing = 0;
503
504 int status = jpeg_read_header(cinfo, true);
505 if (JPEG_HEADER_OK != status) {
506 SkDELETE(imageIndex);
507 return false;
508 }
509
510 jpeg_create_huffman_index(cinfo, huffmanIndex);
511 cinfo->scale_num = 1;
512 cinfo->scale_denom = 1;
513 if (!jpeg_build_huffman_index(cinfo, huffmanIndex)) {
514 SkDELETE(imageIndex);
515 return false;
516 }
517
518 // destroy the cinfo used to create/build the huffman index
519 jpeg_destroy_decompress(cinfo);
520
521 // Init decoder to image decode mode
522 imageIndex->initializeInfo();
523
524 status = jpeg_read_header(cinfo, true);
525 if (JPEG_HEADER_OK != status) {
526 SkDELETE(imageIndex);
527 return false;
528 }
529
530 cinfo->out_color_space = JCS_RGBA_8888;
531 cinfo->do_fancy_upsampling = 0;
532 cinfo->do_block_smoothing = 0;
533
534 // instead of jpeg_start_decompress() we start a tiled decompress
535 jpeg_start_tile_decompress(cinfo);
536
537 cinfo->scale_num = 1;
538 *height = cinfo->output_height;
539 *width = cinfo->output_width;
540 fImageWidth = *width;
541 fImageHeight = *height;
542
543 SkDELETE(fImageIndex);
544 fImageIndex = imageIndex;
545
546 return true;
547}
548
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000549bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
djsollen@google.com11399402013-03-20 17:45:27 +0000550 if (NULL == fImageIndex) {
551 return false;
552 }
553 jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
554
555 SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
556 if (!rect.intersect(region)) {
557 // If the requested region is entirely outside the image return false
558 return false;
559 }
560
561
562 skjpeg_error_mgr errorManager;
563 cinfo->err = jpeg_std_error(&errorManager);
564 errorManager.error_exit = skjpeg_error_exit;
565 if (setjmp(errorManager.fJmpBuf)) {
566 return false;
567 }
568
569 int requestedSampleSize = this->getSampleSize();
570 cinfo->scale_denom = requestedSampleSize;
571
572 if (this->getPreferQualityOverSpeed()) {
573 cinfo->dct_method = JDCT_ISLOW;
574 } else {
575 cinfo->dct_method = JDCT_IFAST;
576 }
577
578 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
579 if (config != SkBitmap::kARGB_8888_Config &&
580 config != SkBitmap::kARGB_4444_Config &&
581 config != SkBitmap::kRGB_565_Config) {
582 config = SkBitmap::kARGB_8888_Config;
583 }
584
585 /* default format is RGB */
586 cinfo->out_color_space = JCS_RGB;
587
588#ifdef ANDROID_RGB
589 cinfo->dither_mode = JDITHER_NONE;
590 if (SkBitmap::kARGB_8888_Config == config) {
591 cinfo->out_color_space = JCS_RGBA_8888;
592 } else if (SkBitmap::kRGB_565_Config == config) {
593 cinfo->out_color_space = JCS_RGB_565;
594 if (this->getDitherImage()) {
595 cinfo->dither_mode = JDITHER_ORDERED;
596 }
597 }
598#endif
599
600 int startX = rect.fLeft;
601 int startY = rect.fTop;
602 int width = rect.width();
603 int height = rect.height();
604
605 jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
606 &startX, &startY, &width, &height);
607 int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
608 int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
609
610 SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
611
612 SkBitmap bitmap;
613 bitmap.setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
614 bitmap.setIsOpaque(true);
615
616 // Check ahead of time if the swap(dest, src) is possible or not.
617 // If yes, then we will stick to AllocPixelRef since it's cheaper with the
618 // swap happening. If no, then we will use alloc to allocate pixels to
619 // prevent garbage collection.
620 int w = rect.width() / actualSampleSize;
621 int h = rect.height() / actualSampleSize;
622 bool swapOnly = (rect == region) && bm->isNull() &&
623 (w == bitmap.width()) && (h == bitmap.height()) &&
624 ((startX - rect.x()) / actualSampleSize == 0) &&
625 ((startY - rect.y()) / actualSampleSize == 0);
626 if (swapOnly) {
627 if (!this->allocPixelRef(&bitmap, NULL)) {
628 return return_false(*cinfo, bitmap, "allocPixelRef");
629 }
630 } else {
631 if (!bitmap.allocPixels()) {
632 return return_false(*cinfo, bitmap, "allocPixels");
633 }
634 }
635
636 SkAutoLockPixels alp(bitmap);
637
638#ifdef ANDROID_RGB
639 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
640 a significant performance boost.
641 */
642 if (skiaSampleSize == 1 &&
643 ((config == SkBitmap::kARGB_8888_Config &&
644 cinfo->out_color_space == JCS_RGBA_8888) ||
645 (config == SkBitmap::kRGB_565_Config &&
646 cinfo->out_color_space == JCS_RGB_565)))
647 {
648 JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
649 INT32 const bpr = bitmap.rowBytes();
650 int rowTotalCount = 0;
651
652 while (rowTotalCount < height) {
653 int rowCount = jpeg_read_tile_scanline(cinfo,
654 fImageIndex->huffmanIndex(),
655 &rowptr);
656 // if row_count == 0, then we didn't get a scanline, so abort.
657 // if we supported partial images, we might return true in this case
658 if (0 == rowCount) {
659 return return_false(*cinfo, bitmap, "read_scanlines");
660 }
661 if (this->shouldCancelDecode()) {
662 return return_false(*cinfo, bitmap, "shouldCancelDecode");
663 }
664 rowTotalCount += rowCount;
665 rowptr += bpr;
666 }
667
668 if (swapOnly) {
669 bm->swap(bitmap);
670 } else {
671 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
672 region.width(), region.height(), startX, startY);
673 }
674 return true;
675 }
676#endif
677
678 // check for supported formats
679 SkScaledBitmapSampler::SrcConfig sc;
680 if (JCS_CMYK == cinfo->out_color_space) {
681 // In this case we will manually convert the CMYK values to RGB
682 sc = SkScaledBitmapSampler::kRGBX;
683 } else if (3 == cinfo->out_color_components && JCS_RGB == cinfo->out_color_space) {
684 sc = SkScaledBitmapSampler::kRGB;
685#ifdef ANDROID_RGB
686 } else if (JCS_RGBA_8888 == cinfo->out_color_space) {
687 sc = SkScaledBitmapSampler::kRGBX;
688 } else if (JCS_RGB_565 == cinfo->out_color_space) {
689 sc = SkScaledBitmapSampler::kRGB_565;
690#endif
691 } else if (1 == cinfo->out_color_components &&
692 JCS_GRAYSCALE == cinfo->out_color_space) {
693 sc = SkScaledBitmapSampler::kGray;
694 } else {
695 return return_false(*cinfo, *bm, "jpeg colorspace");
696 }
697
698 if (!sampler.begin(&bitmap, sc, this->getDitherImage())) {
699 return return_false(*cinfo, bitmap, "sampler.begin");
700 }
701
702 // The CMYK work-around relies on 4 components per pixel here
703 SkAutoMalloc srcStorage(width * 4);
704 uint8_t* srcRow = (uint8_t*)srcStorage.get();
705
706 // Possibly skip initial rows [sampler.srcY0]
707 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
708 return return_false(*cinfo, bitmap, "skip rows");
709 }
710
711 // now loop through scanlines until y == bitmap->height() - 1
712 for (int y = 0;; y++) {
713 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
714 int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
715 if (0 == row_count) {
716 return return_false(*cinfo, bitmap, "read_scanlines");
717 }
718 if (this->shouldCancelDecode()) {
719 return return_false(*cinfo, bitmap, "shouldCancelDecode");
720 }
721
722 if (JCS_CMYK == cinfo->out_color_space) {
723 convert_CMYK_to_RGB(srcRow, width);
724 }
725
726 sampler.next(srcRow);
727 if (bitmap.height() - 1 == y) {
728 // we're done
729 break;
730 }
731
732 if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
733 sampler.srcDY() - 1)) {
734 return return_false(*cinfo, bitmap, "skip rows");
735 }
736 }
737 if (swapOnly) {
738 bm->swap(bitmap);
739 } else {
740 cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
741 region.width(), region.height(), startX, startY);
742 }
743 return true;
744}
745#endif
746
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000747///////////////////////////////////////////////////////////////////////////////
748
749#include "SkColorPriv.h"
750
751// taken from jcolor.c in libjpeg
752#if 0 // 16bit - precise but slow
753 #define CYR 19595 // 0.299
754 #define CYG 38470 // 0.587
755 #define CYB 7471 // 0.114
756
757 #define CUR -11059 // -0.16874
758 #define CUG -21709 // -0.33126
759 #define CUB 32768 // 0.5
760
761 #define CVR 32768 // 0.5
762 #define CVG -27439 // -0.41869
763 #define CVB -5329 // -0.08131
764
765 #define CSHIFT 16
766#else // 8bit - fast, slightly less precise
767 #define CYR 77 // 0.299
768 #define CYG 150 // 0.587
769 #define CYB 29 // 0.114
770
771 #define CUR -43 // -0.16874
772 #define CUG -85 // -0.33126
773 #define CUB 128 // 0.5
774
775 #define CVR 128 // 0.5
776 #define CVG -107 // -0.41869
777 #define CVB -21 // -0.08131
778
779 #define CSHIFT 8
780#endif
781
782static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
783 int r = SkGetPackedR32(c);
784 int g = SkGetPackedG32(c);
785 int b = SkGetPackedB32(c);
786
787 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
788 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
789 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
790
791 dst[0] = SkToU8(y);
792 dst[1] = SkToU8(u + 128);
793 dst[2] = SkToU8(v + 128);
794}
795
796static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
797 int r = SkGetPackedR4444(c);
798 int g = SkGetPackedG4444(c);
799 int b = SkGetPackedB4444(c);
800
801 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
802 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
803 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
804
805 dst[0] = SkToU8(y);
806 dst[1] = SkToU8(u + 128);
807 dst[2] = SkToU8(v + 128);
808}
809
810static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
811 int r = SkGetPackedR16(c);
812 int g = SkGetPackedG16(c);
813 int b = SkGetPackedB16(c);
814
815 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
816 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
817 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
818
819 dst[0] = SkToU8(y);
820 dst[1] = SkToU8(u + 128);
821 dst[2] = SkToU8(v + 128);
822}
823
824///////////////////////////////////////////////////////////////////////////////
825
826typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
827 const void* SK_RESTRICT src, int width,
828 const SkPMColor* SK_RESTRICT ctable);
829
830static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
831 const void* SK_RESTRICT srcRow, int width,
832 const SkPMColor*) {
833 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
834 while (--width >= 0) {
835#ifdef WE_CONVERT_TO_YUV
836 rgb2yuv_32(dst, *src++);
837#else
838 uint32_t c = *src++;
839 dst[0] = SkGetPackedR32(c);
840 dst[1] = SkGetPackedG32(c);
841 dst[2] = SkGetPackedB32(c);
842#endif
843 dst += 3;
844 }
845}
846
847static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
848 const void* SK_RESTRICT srcRow, int width,
849 const SkPMColor*) {
850 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
851 while (--width >= 0) {
852#ifdef WE_CONVERT_TO_YUV
853 rgb2yuv_4444(dst, *src++);
854#else
855 SkPMColor16 c = *src++;
856 dst[0] = SkPacked4444ToR32(c);
857 dst[1] = SkPacked4444ToG32(c);
858 dst[2] = SkPacked4444ToB32(c);
859#endif
860 dst += 3;
861 }
862}
863
864static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
865 const void* SK_RESTRICT srcRow, int width,
866 const SkPMColor*) {
867 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
868 while (--width >= 0) {
869#ifdef WE_CONVERT_TO_YUV
870 rgb2yuv_16(dst, *src++);
871#else
872 uint16_t c = *src++;
873 dst[0] = SkPacked16ToR32(c);
874 dst[1] = SkPacked16ToG32(c);
875 dst[2] = SkPacked16ToB32(c);
876#endif
877 dst += 3;
878 }
879}
880
881static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
882 const void* SK_RESTRICT srcRow, int width,
883 const SkPMColor* SK_RESTRICT ctable) {
884 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
885 while (--width >= 0) {
886#ifdef WE_CONVERT_TO_YUV
887 rgb2yuv_32(dst, ctable[*src++]);
888#else
889 uint32_t c = ctable[*src++];
890 dst[0] = SkGetPackedR32(c);
891 dst[1] = SkGetPackedG32(c);
892 dst[2] = SkGetPackedB32(c);
893#endif
894 dst += 3;
895 }
896}
897
898static WriteScanline ChooseWriter(const SkBitmap& bm) {
899 switch (bm.config()) {
900 case SkBitmap::kARGB_8888_Config:
901 return Write_32_YUV;
902 case SkBitmap::kRGB_565_Config:
903 return Write_16_YUV;
904 case SkBitmap::kARGB_4444_Config:
905 return Write_4444_YUV;
906 case SkBitmap::kIndex8_Config:
907 return Write_Index_YUV;
908 default:
909 return NULL;
910 }
911}
912
913class SkJPEGImageEncoder : public SkImageEncoder {
914protected:
915 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
916#ifdef TIME_ENCODE
djsollen@google.com11399402013-03-20 17:45:27 +0000917 SkAutoTime atm("JPEG Encode");
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000918#endif
919
920 const WriteScanline writer = ChooseWriter(bm);
921 if (NULL == writer) {
922 return false;
923 }
924
925 SkAutoLockPixels alp(bm);
926 if (NULL == bm.getPixels()) {
927 return false;
928 }
929
930 jpeg_compress_struct cinfo;
931 skjpeg_error_mgr sk_err;
932 skjpeg_destination_mgr sk_wstream(stream);
933
934 // allocate these before set call setjmp
935 SkAutoMalloc oneRow;
936 SkAutoLockColors ctLocker;
937
938 cinfo.err = jpeg_std_error(&sk_err);
939 sk_err.error_exit = skjpeg_error_exit;
940 if (setjmp(sk_err.fJmpBuf)) {
941 return false;
942 }
943 jpeg_create_compress(&cinfo);
944
945 cinfo.dest = &sk_wstream;
946 cinfo.image_width = bm.width();
947 cinfo.image_height = bm.height();
948 cinfo.input_components = 3;
949#ifdef WE_CONVERT_TO_YUV
950 cinfo.in_color_space = JCS_YCbCr;
951#else
952 cinfo.in_color_space = JCS_RGB;
953#endif
954 cinfo.input_gamma = 1;
955
956 jpeg_set_defaults(&cinfo);
957 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
scroggo@google.comb7decc52013-04-17 17:37:56 +0000958#ifdef DCT_IFAST_SUPPORTED
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000959 cinfo.dct_method = JDCT_IFAST;
scroggo@google.comb7decc52013-04-17 17:37:56 +0000960#endif
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000961
962 jpeg_start_compress(&cinfo, TRUE);
963
964 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000965 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000966
967 const SkPMColor* colors = ctLocker.lockColors(bm);
968 const void* srcRow = bm.getPixels();
969
970 while (cinfo.next_scanline < cinfo.image_height) {
971 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
972
973 writer(oneRowP, srcRow, width, colors);
974 row_pointer[0] = oneRowP;
975 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
976 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
977 }
978
979 jpeg_finish_compress(&cinfo);
980 jpeg_destroy_compress(&cinfo);
981
982 return true;
983 }
984};
985
986///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000987DEFINE_DECODER_CREATOR(JPEGImageDecoder);
988DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
989///////////////////////////////////////////////////////////////////////////////
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000990
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000991static bool is_jpeg(SkStream* stream) {
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000992 static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000993 static const size_t HEADER_SIZE = sizeof(gHeader);
994
995 char buffer[HEADER_SIZE];
996 size_t len = stream->read(buffer, HEADER_SIZE);
997
998 if (len != HEADER_SIZE) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000999 return false; // can't read enough
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001000 }
1001 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001002 return false;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001003 }
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001004 return true;
1005}
1006
1007#include "SkTRegistry.h"
1008
1009static SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) {
1010 if (is_jpeg(stream)) {
1011 return SkNEW(SkJPEGImageDecoder);
1012 }
1013 return NULL;
1014}
1015
1016static SkImageDecoder::Format get_format_jpeg(SkStream* stream) {
1017 if (is_jpeg(stream)) {
1018 return SkImageDecoder::kJPEG_Format;
1019 }
1020 return SkImageDecoder::kUnknown_Format;
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001021}
1022
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001023static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +00001024 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
1025}
1026
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001027
1028static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory);
scroggo@google.com39edf4c2013-04-25 17:33:51 +00001029static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_jpeg);
robertphillips@google.com8570b5c2012-03-20 17:40:58 +00001030static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efactory);