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