blob: 5cb45a2e0a02284b4c292bafdf36cd707006c3e6 [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"
18#include "SkUtils.h"
19
20#include <stdio.h>
21extern "C" {
22 #include "jpeglib.h"
23 #include "jerror.h"
24}
25
26#ifdef SK_BUILD_FOR_ANDROID
27#include <cutils/properties.h>
28
29// Key to lookup the size of memory buffer set in system property
30static const char KEY_MEM_CAP[] = "ro.media.dec.jpeg.memcap";
31#endif
32
33// this enables timing code to report milliseconds for an encode
34//#define TIME_ENCODE
35//#define TIME_DECODE
36
37// this enables our rgb->yuv code, which is faster than libjpeg on ARM
38// disable for the moment, as we have some glitches when width != multiple of 4
39#define WE_CONVERT_TO_YUV
40
41//////////////////////////////////////////////////////////////////////////
42//////////////////////////////////////////////////////////////////////////
43
44class SkJPEGImageDecoder : public SkImageDecoder {
45public:
46 virtual Format getFormat() const {
47 return kJPEG_Format;
48 }
49
50protected:
51 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
52};
53
54//////////////////////////////////////////////////////////////////////////
55
56#include "SkTime.h"
57
58class AutoTimeMillis {
59public:
60 AutoTimeMillis(const char label[]) : fLabel(label) {
61 if (!fLabel) {
62 fLabel = "";
63 }
64 fNow = SkTime::GetMSecs();
65 }
66 ~AutoTimeMillis() {
67 SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
68 }
69private:
70 const char* fLabel;
71 SkMSec fNow;
72};
73
74/* Automatically clean up after throwing an exception */
75class JPEGAutoClean {
76public:
77 JPEGAutoClean(): cinfo_ptr(NULL) {}
78 ~JPEGAutoClean() {
79 if (cinfo_ptr) {
80 jpeg_destroy_decompress(cinfo_ptr);
81 }
82 }
83 void set(jpeg_decompress_struct* info) {
84 cinfo_ptr = info;
85 }
86private:
87 jpeg_decompress_struct* cinfo_ptr;
88};
89
90#ifdef SK_BUILD_FOR_ANDROID
91/* Check if the memory cap property is set.
92 If so, use the memory size for jpeg decode.
93*/
94static void overwrite_mem_buffer_size(j_decompress_ptr cinfo) {
95#ifdef ANDROID_LARGE_MEMORY_DEVICE
96 cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
97#else
98 cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
99#endif
100}
101#endif
102
103
104///////////////////////////////////////////////////////////////////////////////
105
106/* If we need to better match the request, we might examine the image and
107 output dimensions, and determine if the downsampling jpeg provided is
108 not sufficient. If so, we can recompute a modified sampleSize value to
109 make up the difference.
110
111 To skip this additional scaling, just set sampleSize = 1; below.
112 */
113static int recompute_sampleSize(int sampleSize,
114 const jpeg_decompress_struct& cinfo) {
115 return sampleSize * cinfo.output_width / cinfo.image_width;
116}
117
118static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
119 /* These are initialized to 0, so if they have non-zero values, we assume
120 they are "valid" (i.e. have been computed by libjpeg)
121 */
122 return cinfo.output_width != 0 && cinfo.output_height != 0;
123}
124
125static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer,
126 int count) {
127 for (int i = 0; i < count; i++) {
128 JSAMPLE* rowptr = (JSAMPLE*)buffer;
129 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
130 if (row_count != 1) {
131 return false;
132 }
133 }
134 return true;
135}
136
137// This guy exists just to aid in debugging, as it allows debuggers to just
138// set a break-point in one place to see all error exists.
139static bool return_false(const jpeg_decompress_struct& cinfo,
140 const SkBitmap& bm, const char msg[]) {
141#if 0
142 SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code,
143 cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
144 bm.width(), bm.height());
145#endif
146 return false; // must always return false
147}
148
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000149// Convert a scanline of CMYK samples to RGBX in place. Note that this
150// method moves the "scanline" pointer in its processing
151static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
152 // At this point we've received CMYK pixels from libjpeg. We
153 // perform a crude conversion to RGB (based on the formulae
154 // from easyrgb.com):
155 // CMYK -> CMY
156 // C = ( C * (1 - K) + K ) // for each CMY component
157 // CMY -> RGB
158 // R = ( 1 - C ) * 255 // for each RGB component
159 // Unfortunately we are seeing inverted CMYK so all the original terms
160 // are 1-. This yields:
161 // CMYK -> CMY
162 // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
163 // The conversion from CMY->RGB remains the same
164 for (unsigned int x = 0; x < width; ++x, scanline += 4) {
165 scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
166 scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
167 scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
168 scanline[3] = 255;
169 }
170}
171
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000172bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
173#ifdef TIME_DECODE
174 AutoTimeMillis atm("JPEG Decode");
175#endif
176
177 SkAutoMalloc srcStorage;
178 JPEGAutoClean autoClean;
179
180 jpeg_decompress_struct cinfo;
181 skjpeg_error_mgr sk_err;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000182 skjpeg_source_mgr sk_stream(stream, this, false);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000183
184 cinfo.err = jpeg_std_error(&sk_err);
185 sk_err.error_exit = skjpeg_error_exit;
186
187 // All objects need to be instantiated before this setjmp call so that
188 // they will be cleaned up properly if an error occurs.
189 if (setjmp(sk_err.fJmpBuf)) {
190 return return_false(cinfo, *bm, "setjmp");
191 }
192
193 jpeg_create_decompress(&cinfo);
194 autoClean.set(&cinfo);
195
196#ifdef SK_BUILD_FOR_ANDROID
197 overwrite_mem_buffer_size(&cinfo);
198#endif
199
200 //jpeg_stdio_src(&cinfo, file);
201 cinfo.src = &sk_stream;
202
203 int status = jpeg_read_header(&cinfo, true);
204 if (status != JPEG_HEADER_OK) {
205 return return_false(cinfo, *bm, "read_header");
206 }
207
208 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
209 can) much faster that we, just use their num/denom api to approximate
210 the size.
211 */
212 int sampleSize = this->getSampleSize();
213
214 cinfo.dct_method = JDCT_IFAST;
215 cinfo.scale_num = 1;
216 cinfo.scale_denom = sampleSize;
217
218 /* this gives about 30% performance improvement. In theory it may
219 reduce the visual quality, in practice I'm not seeing a difference
220 */
221 cinfo.do_fancy_upsampling = 0;
222
223 /* this gives another few percents */
224 cinfo.do_block_smoothing = 0;
225
226 /* default format is RGB */
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000227 if (cinfo.jpeg_color_space == JCS_CMYK) {
228 // libjpeg cannot convert from CMYK to RGB - here we set up
229 // so libjpeg will give us CMYK samples back and we will
230 // later manually convert them to RGB
231 cinfo.out_color_space = JCS_CMYK;
232 } else {
233 cinfo.out_color_space = JCS_RGB;
234 }
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000235
236 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
237 // only these make sense for jpegs
238 if (config != SkBitmap::kARGB_8888_Config &&
239 config != SkBitmap::kARGB_4444_Config &&
240 config != SkBitmap::kRGB_565_Config) {
241 config = SkBitmap::kARGB_8888_Config;
242 }
243
244#ifdef ANDROID_RGB
245 cinfo.dither_mode = JDITHER_NONE;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000246 if (SkBitmap::kARGB_8888_Config == config && JCS_CMYK != cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000247 cinfo.out_color_space = JCS_RGBA_8888;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000248 } else if (SkBitmap::kRGB_565_Config == config && JCS_CMYK != cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000249 cinfo.out_color_space = JCS_RGB_565;
250 if (this->getDitherImage()) {
251 cinfo.dither_mode = JDITHER_ORDERED;
252 }
253 }
254#endif
255
256 if (sampleSize == 1 && mode == SkImageDecoder::kDecodeBounds_Mode) {
257 bm->setConfig(config, cinfo.image_width, cinfo.image_height);
258 bm->setIsOpaque(true);
259 return true;
260 }
261
262 /* image_width and image_height are the original dimensions, available
263 after jpeg_read_header(). To see the scaled dimensions, we have to call
264 jpeg_start_decompress(), and then read output_width and output_height.
265 */
266 if (!jpeg_start_decompress(&cinfo)) {
267 /* If we failed here, we may still have enough information to return
268 to the caller if they just wanted (subsampled bounds). If sampleSize
269 was 1, then we would have already returned. Thus we just check if
270 we're in kDecodeBounds_Mode, and that we have valid output sizes.
271
272 One reason to fail here is that we have insufficient stream data
273 to complete the setup. However, output dimensions seem to get
274 computed very early, which is why this special check can pay off.
275 */
276 if (SkImageDecoder::kDecodeBounds_Mode == mode &&
277 valid_output_dimensions(cinfo)) {
278 SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
279 recompute_sampleSize(sampleSize, cinfo));
280 bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
281 bm->setIsOpaque(true);
282 return true;
283 } else {
284 return return_false(cinfo, *bm, "start_decompress");
285 }
286 }
287 sampleSize = recompute_sampleSize(sampleSize, cinfo);
288
289 // should we allow the Chooser (if present) to pick a config for us???
290 if (!this->chooseFromOneChoice(config, cinfo.output_width,
291 cinfo.output_height)) {
292 return return_false(cinfo, *bm, "chooseFromOneChoice");
293 }
294
295#ifdef ANDROID_RGB
296 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
297 a significant performance boost.
298 */
299 if (sampleSize == 1 &&
300 ((config == SkBitmap::kARGB_8888_Config &&
301 cinfo.out_color_space == JCS_RGBA_8888) ||
302 (config == SkBitmap::kRGB_565_Config &&
303 cinfo.out_color_space == JCS_RGB_565)))
304 {
305 bm->setConfig(config, cinfo.output_width, cinfo.output_height);
306 bm->setIsOpaque(true);
307 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
308 return true;
309 }
310 if (!this->allocPixelRef(bm, NULL)) {
311 return return_false(cinfo, *bm, "allocPixelRef");
312 }
313 SkAutoLockPixels alp(*bm);
314 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
315 INT32 const bpr = bm->rowBytes();
316
317 while (cinfo.output_scanline < cinfo.output_height) {
318 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
319 // if row_count == 0, then we didn't get a scanline, so abort.
320 // if we supported partial images, we might return true in this case
321 if (0 == row_count) {
322 return return_false(cinfo, *bm, "read_scanlines");
323 }
324 if (this->shouldCancelDecode()) {
325 return return_false(cinfo, *bm, "shouldCancelDecode");
326 }
327 rowptr += bpr;
328 }
329 jpeg_finish_decompress(&cinfo);
330 return true;
331 }
332#endif
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000333
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000334 // check for supported formats
335 SkScaledBitmapSampler::SrcConfig sc;
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000336 if (JCS_CMYK == cinfo.out_color_space) {
337 // In this case we will manually convert the CMYK values to RGB
338 sc = SkScaledBitmapSampler::kRGBX;
339 } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000340 sc = SkScaledBitmapSampler::kRGB;
341#ifdef ANDROID_RGB
342 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
343 sc = SkScaledBitmapSampler::kRGBX;
344 } else if (JCS_RGB_565 == cinfo.out_color_space) {
345 sc = SkScaledBitmapSampler::kRGB_565;
346#endif
347 } else if (1 == cinfo.out_color_components &&
348 JCS_GRAYSCALE == cinfo.out_color_space) {
349 sc = SkScaledBitmapSampler::kGray;
350 } else {
351 return return_false(cinfo, *bm, "jpeg colorspace");
352 }
353
354 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height,
355 sampleSize);
356
357 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000358 // jpegs are always opaque (i.e. have no per-pixel alpha)
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000359 bm->setIsOpaque(true);
360
361 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
362 return true;
363 }
364 if (!this->allocPixelRef(bm, NULL)) {
365 return return_false(cinfo, *bm, "allocPixelRef");
366 }
367
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000368 SkAutoLockPixels alp(*bm);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000369 if (!sampler.begin(bm, sc, this->getDitherImage())) {
370 return return_false(cinfo, *bm, "sampler.begin");
371 }
372
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000373 // The CMYK work-around relies on 4 components per pixel here
374 uint8_t* srcRow = (uint8_t*)srcStorage.reset(cinfo.output_width * 4);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000375
376 // Possibly skip initial rows [sampler.srcY0]
377 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
378 return return_false(cinfo, *bm, "skip rows");
379 }
380
381 // now loop through scanlines until y == bm->height() - 1
382 for (int y = 0;; y++) {
383 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
384 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
385 if (0 == row_count) {
386 return return_false(cinfo, *bm, "read_scanlines");
387 }
388 if (this->shouldCancelDecode()) {
389 return return_false(cinfo, *bm, "shouldCancelDecode");
390 }
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000391
392 if (JCS_CMYK == cinfo.out_color_space) {
393 convert_CMYK_to_RGB(srcRow, cinfo.output_width);
394 }
395
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000396 sampler.next(srcRow);
397 if (bm->height() - 1 == y) {
398 // we're done
399 break;
400 }
401
402 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
403 return return_false(cinfo, *bm, "skip rows");
404 }
405 }
406
407 // we formally skip the rest, so we don't get a complaint from libjpeg
408 if (!skip_src_rows(&cinfo, srcRow,
409 cinfo.output_height - cinfo.output_scanline)) {
410 return return_false(cinfo, *bm, "skip rows");
411 }
412 jpeg_finish_decompress(&cinfo);
413
414// SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config());
415 return true;
416}
417
418///////////////////////////////////////////////////////////////////////////////
419
420#include "SkColorPriv.h"
421
422// taken from jcolor.c in libjpeg
423#if 0 // 16bit - precise but slow
424 #define CYR 19595 // 0.299
425 #define CYG 38470 // 0.587
426 #define CYB 7471 // 0.114
427
428 #define CUR -11059 // -0.16874
429 #define CUG -21709 // -0.33126
430 #define CUB 32768 // 0.5
431
432 #define CVR 32768 // 0.5
433 #define CVG -27439 // -0.41869
434 #define CVB -5329 // -0.08131
435
436 #define CSHIFT 16
437#else // 8bit - fast, slightly less precise
438 #define CYR 77 // 0.299
439 #define CYG 150 // 0.587
440 #define CYB 29 // 0.114
441
442 #define CUR -43 // -0.16874
443 #define CUG -85 // -0.33126
444 #define CUB 128 // 0.5
445
446 #define CVR 128 // 0.5
447 #define CVG -107 // -0.41869
448 #define CVB -21 // -0.08131
449
450 #define CSHIFT 8
451#endif
452
453static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
454 int r = SkGetPackedR32(c);
455 int g = SkGetPackedG32(c);
456 int b = SkGetPackedB32(c);
457
458 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
459 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
460 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
461
462 dst[0] = SkToU8(y);
463 dst[1] = SkToU8(u + 128);
464 dst[2] = SkToU8(v + 128);
465}
466
467static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
468 int r = SkGetPackedR4444(c);
469 int g = SkGetPackedG4444(c);
470 int b = SkGetPackedB4444(c);
471
472 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
473 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
474 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
475
476 dst[0] = SkToU8(y);
477 dst[1] = SkToU8(u + 128);
478 dst[2] = SkToU8(v + 128);
479}
480
481static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
482 int r = SkGetPackedR16(c);
483 int g = SkGetPackedG16(c);
484 int b = SkGetPackedB16(c);
485
486 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
487 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
488 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
489
490 dst[0] = SkToU8(y);
491 dst[1] = SkToU8(u + 128);
492 dst[2] = SkToU8(v + 128);
493}
494
495///////////////////////////////////////////////////////////////////////////////
496
497typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
498 const void* SK_RESTRICT src, int width,
499 const SkPMColor* SK_RESTRICT ctable);
500
501static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
502 const void* SK_RESTRICT srcRow, int width,
503 const SkPMColor*) {
504 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
505 while (--width >= 0) {
506#ifdef WE_CONVERT_TO_YUV
507 rgb2yuv_32(dst, *src++);
508#else
509 uint32_t c = *src++;
510 dst[0] = SkGetPackedR32(c);
511 dst[1] = SkGetPackedG32(c);
512 dst[2] = SkGetPackedB32(c);
513#endif
514 dst += 3;
515 }
516}
517
518static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
519 const void* SK_RESTRICT srcRow, int width,
520 const SkPMColor*) {
521 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
522 while (--width >= 0) {
523#ifdef WE_CONVERT_TO_YUV
524 rgb2yuv_4444(dst, *src++);
525#else
526 SkPMColor16 c = *src++;
527 dst[0] = SkPacked4444ToR32(c);
528 dst[1] = SkPacked4444ToG32(c);
529 dst[2] = SkPacked4444ToB32(c);
530#endif
531 dst += 3;
532 }
533}
534
535static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
536 const void* SK_RESTRICT srcRow, int width,
537 const SkPMColor*) {
538 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
539 while (--width >= 0) {
540#ifdef WE_CONVERT_TO_YUV
541 rgb2yuv_16(dst, *src++);
542#else
543 uint16_t c = *src++;
544 dst[0] = SkPacked16ToR32(c);
545 dst[1] = SkPacked16ToG32(c);
546 dst[2] = SkPacked16ToB32(c);
547#endif
548 dst += 3;
549 }
550}
551
552static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
553 const void* SK_RESTRICT srcRow, int width,
554 const SkPMColor* SK_RESTRICT ctable) {
555 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
556 while (--width >= 0) {
557#ifdef WE_CONVERT_TO_YUV
558 rgb2yuv_32(dst, ctable[*src++]);
559#else
560 uint32_t c = ctable[*src++];
561 dst[0] = SkGetPackedR32(c);
562 dst[1] = SkGetPackedG32(c);
563 dst[2] = SkGetPackedB32(c);
564#endif
565 dst += 3;
566 }
567}
568
569static WriteScanline ChooseWriter(const SkBitmap& bm) {
570 switch (bm.config()) {
571 case SkBitmap::kARGB_8888_Config:
572 return Write_32_YUV;
573 case SkBitmap::kRGB_565_Config:
574 return Write_16_YUV;
575 case SkBitmap::kARGB_4444_Config:
576 return Write_4444_YUV;
577 case SkBitmap::kIndex8_Config:
578 return Write_Index_YUV;
579 default:
580 return NULL;
581 }
582}
583
584class SkJPEGImageEncoder : public SkImageEncoder {
585protected:
586 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
587#ifdef TIME_ENCODE
588 AutoTimeMillis atm("JPEG Encode");
589#endif
590
591 const WriteScanline writer = ChooseWriter(bm);
592 if (NULL == writer) {
593 return false;
594 }
595
596 SkAutoLockPixels alp(bm);
597 if (NULL == bm.getPixels()) {
598 return false;
599 }
600
601 jpeg_compress_struct cinfo;
602 skjpeg_error_mgr sk_err;
603 skjpeg_destination_mgr sk_wstream(stream);
604
605 // allocate these before set call setjmp
606 SkAutoMalloc oneRow;
607 SkAutoLockColors ctLocker;
608
609 cinfo.err = jpeg_std_error(&sk_err);
610 sk_err.error_exit = skjpeg_error_exit;
611 if (setjmp(sk_err.fJmpBuf)) {
612 return false;
613 }
614 jpeg_create_compress(&cinfo);
615
616 cinfo.dest = &sk_wstream;
617 cinfo.image_width = bm.width();
618 cinfo.image_height = bm.height();
619 cinfo.input_components = 3;
620#ifdef WE_CONVERT_TO_YUV
621 cinfo.in_color_space = JCS_YCbCr;
622#else
623 cinfo.in_color_space = JCS_RGB;
624#endif
625 cinfo.input_gamma = 1;
626
627 jpeg_set_defaults(&cinfo);
628 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
629 cinfo.dct_method = JDCT_IFAST;
630
631 jpeg_start_compress(&cinfo, TRUE);
632
633 const int width = bm.width();
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000634 uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000635
636 const SkPMColor* colors = ctLocker.lockColors(bm);
637 const void* srcRow = bm.getPixels();
638
639 while (cinfo.next_scanline < cinfo.image_height) {
640 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
641
642 writer(oneRowP, srcRow, width, colors);
643 row_pointer[0] = oneRowP;
644 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
645 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
646 }
647
648 jpeg_finish_compress(&cinfo);
649 jpeg_destroy_compress(&cinfo);
650
651 return true;
652 }
653};
654
655///////////////////////////////////////////////////////////////////////////////
656
657#include "SkTRegistry.h"
658
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000659SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000660 static const char gHeader[] = { 0xFF, 0xD8, 0xFF };
661 static const size_t HEADER_SIZE = sizeof(gHeader);
662
663 char buffer[HEADER_SIZE];
664 size_t len = stream->read(buffer, HEADER_SIZE);
665
666 if (len != HEADER_SIZE) {
667 return NULL; // can't read enough
668 }
669 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
670 return NULL;
671 }
672 return SkNEW(SkJPEGImageDecoder);
673}
674
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000675static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
tomhudson@google.comd33b26e2012-03-02 16:12:14 +0000676 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
677}
678
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000679
680static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory);
681static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efactory);
682