blob: cc0f8a9d7cf1e57f5ae7e081571a1c1a2a9e621c [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
2 * Copyright 2007, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "SkImageDecoder.h"
reed@android.comb08eb2b2009-01-06 20:16:26 +000018#include "SkImageEncoder.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkColorPriv.h"
20#include "SkDither.h"
21#include "SkScaledBitmapSampler.h"
22#include "SkStream.h"
23#include "SkTemplates.h"
24#include "SkUtils.h"
25
26#include <stdio.h>
27extern "C" {
28 #include "jpeglib.h"
29 #include "jerror.h"
30}
31
32// this enables timing code to report milliseconds for an encode
33//#define TIME_ENCODE
34//#define TIME_DECODE
35
36// this enables our rgb->yuv code, which is faster than libjpeg on ARM
37// disable for the moment, as we have some glitches when width != multiple of 4
38#define WE_CONVERT_TO_YUV
39
40//////////////////////////////////////////////////////////////////////////
41//////////////////////////////////////////////////////////////////////////
42
43class SkJPEGImageDecoder : public SkImageDecoder {
44public:
45 virtual Format getFormat() const {
46 return kJPEG_Format;
47 }
48
49protected:
50 virtual bool onDecode(SkStream* stream, SkBitmap* bm,
51 SkBitmap::Config pref, Mode);
52};
53
reed@android.com8a1c16f2008-12-17 15:59:43 +000054//////////////////////////////////////////////////////////////////////////
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/* our source struct for directing jpeg to our stream object
75*/
76struct sk_source_mgr : jpeg_source_mgr {
77 sk_source_mgr(SkStream* stream, SkImageDecoder* decoder);
78
79 SkStream* fStream;
80 const void* fMemoryBase;
81 size_t fMemoryBaseSize;
82 SkImageDecoder* fDecoder;
83 enum {
84 kBufferSize = 1024
85 };
86 char fBuffer[kBufferSize];
87};
88
89/* Automatically clean up after throwing an exception */
90class JPEGAutoClean {
91public:
92 JPEGAutoClean(): cinfo_ptr(NULL) {}
93 ~JPEGAutoClean() {
94 if (cinfo_ptr) {
95 jpeg_destroy_decompress(cinfo_ptr);
96 }
97 }
98 void set(jpeg_decompress_struct* info) {
99 cinfo_ptr = info;
100 }
101private:
102 jpeg_decompress_struct* cinfo_ptr;
103};
104
105static void sk_init_source(j_decompress_ptr cinfo) {
106 sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
107 src->next_input_byte = (const JOCTET*)src->fBuffer;
108 src->bytes_in_buffer = 0;
109}
110
111static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) {
112 sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
113 if (src->fDecoder != NULL && src->fDecoder->shouldCancelDecode()) {
114 return FALSE;
115 }
116 size_t bytes = src->fStream->read(src->fBuffer, sk_source_mgr::kBufferSize);
117 // note that JPEG is happy with less than the full read,
118 // as long as the result is non-zero
119 if (bytes == 0) {
120 return FALSE;
121 }
122
123 src->next_input_byte = (const JOCTET*)src->fBuffer;
124 src->bytes_in_buffer = bytes;
125 return TRUE;
126}
127
128static void sk_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
129 SkASSERT(num_bytes > 0);
130
131 sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
132
133 long bytesToSkip = num_bytes - src->bytes_in_buffer;
134
135 // check if the skip amount exceeds the current buffer
136 if (bytesToSkip > 0) {
137 size_t bytes = src->fStream->skip(bytesToSkip);
138 if (bytes != (size_t)bytesToSkip) {
139// SkDebugf("xxxxxxxxxxxxxx failure to skip request %d actual %d\n", bytesToSkip, bytes);
140 cinfo->err->error_exit((j_common_ptr)cinfo);
141 }
142 src->next_input_byte = (const JOCTET*)src->fBuffer;
143 src->bytes_in_buffer = 0;
144 } else {
145 src->next_input_byte += num_bytes;
146 src->bytes_in_buffer -= num_bytes;
147 }
148}
149
150static boolean sk_resync_to_restart(j_decompress_ptr cinfo, int desired) {
151 sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
152
153 // what is the desired param for???
154
155 if (!src->fStream->rewind()) {
156 SkDebugf("xxxxxxxxxxxxxx failure to rewind\n");
157 cinfo->err->error_exit((j_common_ptr)cinfo);
158 return FALSE;
159 }
160 src->next_input_byte = (const JOCTET*)src->fBuffer;
161 src->bytes_in_buffer = 0;
162 return TRUE;
163}
164
165static void sk_term_source(j_decompress_ptr /*cinfo*/) {}
166
167///////////////////////////////////////////////////////////////////////////////
168
169static void skmem_init_source(j_decompress_ptr cinfo) {
170 sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
171 src->next_input_byte = (const JOCTET*)src->fMemoryBase;
172 src->bytes_in_buffer = src->fMemoryBaseSize;
173}
174
175static boolean skmem_fill_input_buffer(j_decompress_ptr cinfo) {
176 SkDebugf("xxxxxxxxxxxxxx skmem_fill_input_buffer called\n");
177 return FALSE;
178}
179
180static void skmem_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
181 sk_source_mgr* src = (sk_source_mgr*)cinfo->src;
182// SkDebugf("xxxxxxxxxxxxxx skmem_skip_input_data called %d\n", num_bytes);
183 src->next_input_byte = (const JOCTET*)((const char*)src->next_input_byte + num_bytes);
184 src->bytes_in_buffer -= num_bytes;
185}
186
187static boolean skmem_resync_to_restart(j_decompress_ptr cinfo, int desired) {
188 SkDebugf("xxxxxxxxxxxxxx skmem_resync_to_restart called\n");
189 return TRUE;
190}
191
192static void skmem_term_source(j_decompress_ptr /*cinfo*/) {}
193
194///////////////////////////////////////////////////////////////////////////////
195
196sk_source_mgr::sk_source_mgr(SkStream* stream, SkImageDecoder* decoder) : fStream(stream) {
197 fDecoder = decoder;
198 const void* baseAddr = stream->getMemoryBase();
199 if (baseAddr && false) {
200 fMemoryBase = baseAddr;
201 fMemoryBaseSize = stream->getLength();
202
203 init_source = skmem_init_source;
204 fill_input_buffer = skmem_fill_input_buffer;
205 skip_input_data = skmem_skip_input_data;
206 resync_to_restart = skmem_resync_to_restart;
207 term_source = skmem_term_source;
208 } else {
209 fMemoryBase = NULL;
210 fMemoryBaseSize = 0;
211
212 init_source = sk_init_source;
213 fill_input_buffer = sk_fill_input_buffer;
214 skip_input_data = sk_skip_input_data;
215 resync_to_restart = sk_resync_to_restart;
216 term_source = sk_term_source;
217 }
218// SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize);
219}
220
221#include <setjmp.h>
222
223struct sk_error_mgr : jpeg_error_mgr {
224 jmp_buf fJmpBuf;
225};
226
227static void sk_error_exit(j_common_ptr cinfo) {
228 sk_error_mgr* error = (sk_error_mgr*)cinfo->err;
229
230 (*error->output_message) (cinfo);
231
232 /* Let the memory manager delete any temp files before we die */
233 jpeg_destroy(cinfo);
234
235 longjmp(error->fJmpBuf, -1);
236}
237
238///////////////////////////////////////////////////////////////////////////////
239
240static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer,
241 int count) {
242 for (int i = 0; i < count; i++) {
243 JSAMPLE* rowptr = (JSAMPLE*)buffer;
244 int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
245 if (row_count != 1) {
246 return false;
247 }
248 }
249 return true;
250}
251
252// This guy exists just to aid in debugging, as it allows debuggers to just
253// set a break-point in one place to see all error exists.
254static bool return_false(const jpeg_decompress_struct& cinfo,
255 const SkBitmap& bm, const char msg[]) {
256#if 0
257 SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code,
258 cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
259 bm.width(), bm.height());
260#endif
261 return false; // must always return false
262}
263
264bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
265 SkBitmap::Config prefConfig, Mode mode) {
266#ifdef TIME_DECODE
267 AutoTimeMillis atm("JPEG Decode");
268#endif
269
270 SkAutoMalloc srcStorage;
271 JPEGAutoClean autoClean;
272
273 jpeg_decompress_struct cinfo;
274 sk_error_mgr sk_err;
275 sk_source_mgr sk_stream(stream, this);
276
277 cinfo.err = jpeg_std_error(&sk_err);
278 sk_err.error_exit = sk_error_exit;
279
280 // All objects need to be instantiated before this setjmp call so that
281 // they will be cleaned up properly if an error occurs.
282 if (setjmp(sk_err.fJmpBuf)) {
283 return return_false(cinfo, *bm, "setjmp");
284 }
285
286 jpeg_create_decompress(&cinfo);
287 autoClean.set(&cinfo);
288
289 //jpeg_stdio_src(&cinfo, file);
290 cinfo.src = &sk_stream;
291
292 int status = jpeg_read_header(&cinfo, true);
293 if (status != JPEG_HEADER_OK) {
294 return return_false(cinfo, *bm, "read_header");
295 }
296
297 /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it
298 can) much faster that we, just use their num/denom api to approximate
299 the size.
300 */
301 int sampleSize = this->getSampleSize();
302
303 cinfo.dct_method = JDCT_IFAST;
304 cinfo.scale_num = 1;
305 cinfo.scale_denom = sampleSize;
306
307 /* this gives about 30% performance improvement. In theory it may
308 reduce the visual quality, in practice I'm not seeing a difference
309 */
310 cinfo.do_fancy_upsampling = 0;
311
312 /* this gives another few percents */
313 cinfo.do_block_smoothing = 0;
314
315 /* default format is RGB */
316 cinfo.out_color_space = JCS_RGB;
317
318 SkBitmap::Config config = prefConfig;
319 // if no user preference, see what the device recommends
320 if (config == SkBitmap::kNo_Config)
321 config = SkImageDecoder::GetDeviceConfig();
322
323 // only these make sense for jpegs
324 if (config != SkBitmap::kARGB_8888_Config &&
325 config != SkBitmap::kARGB_4444_Config &&
326 config != SkBitmap::kRGB_565_Config) {
327 config = SkBitmap::kARGB_8888_Config;
328 }
329
330#ifdef ANDROID_RGB
331 cinfo.dither_mode = JDITHER_NONE;
332 if (config == SkBitmap::kARGB_8888_Config) {
333 cinfo.out_color_space = JCS_RGBA_8888;
334 } else if (config == SkBitmap::kRGB_565_Config) {
335 if (sampleSize == 1) {
336 // SkScaledBitmapSampler can't handle RGB_565 yet,
337 // so don't even try.
338 cinfo.out_color_space = JCS_RGB_565;
339 if (this->getDitherImage()) {
340 cinfo.dither_mode = JDITHER_ORDERED;
341 }
342 }
343 }
344#endif
345
reed@android.com9ce22572009-07-16 14:50:54 +0000346 if (sampleSize == 1 && mode == SkImageDecoder::kDecodeBounds_Mode) {
347 bm->setConfig(config, cinfo.image_width, cinfo.image_height);
348 bm->setIsOpaque(true);
349 return true;
350 }
351
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352 /* image_width and image_height are the original dimensions, available
353 after jpeg_read_header(). To see the scaled dimensions, we have to call
354 jpeg_start_decompress(), and then read output_width and output_height.
355 */
356 if (!jpeg_start_decompress(&cinfo)) {
357 return return_false(cinfo, *bm, "start_decompress");
358 }
359
360 /* If we need to better match the request, we might examine the image and
361 output dimensions, and determine if the downsampling jpeg provided is
362 not sufficient. If so, we can recompute a modified sampleSize value to
363 make up the difference.
364
365 To skip this additional scaling, just set sampleSize = 1; below.
366 */
367 sampleSize = sampleSize * cinfo.output_width / cinfo.image_width;
368
369
370 // should we allow the Chooser (if present) to pick a config for us???
371 if (!this->chooseFromOneChoice(config, cinfo.output_width,
372 cinfo.output_height)) {
373 return return_false(cinfo, *bm, "chooseFromOneChoice");
374 }
375
376#ifdef ANDROID_RGB
377 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
378 a significant performance boost.
379 */
380 if (sampleSize == 1 &&
381 ((config == SkBitmap::kARGB_8888_Config &&
382 cinfo.out_color_space == JCS_RGBA_8888) ||
383 (config == SkBitmap::kRGB_565_Config &&
384 cinfo.out_color_space == JCS_RGB_565)))
385 {
386 bm->setConfig(config, cinfo.output_width, cinfo.output_height);
387 bm->setIsOpaque(true);
388 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
389 return true;
390 }
391 if (!this->allocPixelRef(bm, NULL)) {
392 return return_false(cinfo, *bm, "allocPixelRef");
393 }
394 SkAutoLockPixels alp(*bm);
395 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
396 INT32 const bpr = bm->rowBytes();
397
398 while (cinfo.output_scanline < cinfo.output_height) {
399 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
400 // if row_count == 0, then we didn't get a scanline, so abort.
401 // if we supported partial images, we might return true in this case
402 if (0 == row_count) {
403 return return_false(cinfo, *bm, "read_scanlines");
404 }
405 if (this->shouldCancelDecode()) {
406 return return_false(cinfo, *bm, "shouldCancelDecode");
407 }
408 rowptr += bpr;
409 }
410 jpeg_finish_decompress(&cinfo);
411 return true;
412 }
413#endif
414
415 // check for supported formats
416 SkScaledBitmapSampler::SrcConfig sc;
417 if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
418 sc = SkScaledBitmapSampler::kRGB;
419#ifdef ANDROID_RGB
420 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
421 sc = SkScaledBitmapSampler::kRGBX;
422 //} else if (JCS_RGB_565 == cinfo.out_color_space) {
423 // sc = SkScaledBitmapSampler::kRGB_565;
424#endif
425 } else if (1 == cinfo.out_color_components &&
426 JCS_GRAYSCALE == cinfo.out_color_space) {
427 sc = SkScaledBitmapSampler::kGray;
428 } else {
429 return return_false(cinfo, *bm, "jpeg colorspace");
430 }
431
432 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height,
433 sampleSize);
434
435 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
436 // jpegs are always opauqe (i.e. have no per-pixel alpha)
437 bm->setIsOpaque(true);
438
439 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
440 return true;
441 }
442 if (!this->allocPixelRef(bm, NULL)) {
443 return return_false(cinfo, *bm, "allocPixelRef");
444 }
445
446 SkAutoLockPixels alp(*bm);
447 if (!sampler.begin(bm, sc, this->getDitherImage())) {
448 return return_false(cinfo, *bm, "sampler.begin");
449 }
450
451 uint8_t* srcRow = (uint8_t*)srcStorage.alloc(cinfo.output_width * 4);
452
453 // Possibly skip initial rows [sampler.srcY0]
454 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
455 return return_false(cinfo, *bm, "skip rows");
456 }
457
458 // now loop through scanlines until y == bm->height() - 1
459 for (int y = 0;; y++) {
460 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
461 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
462 if (0 == row_count) {
463 return return_false(cinfo, *bm, "read_scanlines");
464 }
465 if (this->shouldCancelDecode()) {
466 return return_false(cinfo, *bm, "shouldCancelDecode");
467 }
468
469 sampler.next(srcRow);
470 if (bm->height() - 1 == y) {
471 // we're done
472 break;
473 }
474
475 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
476 return return_false(cinfo, *bm, "skip rows");
477 }
478 }
479
480 // we formally skip the rest, so we don't get a complaint from libjpeg
481 if (!skip_src_rows(&cinfo, srcRow,
482 cinfo.output_height - cinfo.output_scanline)) {
483 return return_false(cinfo, *bm, "skip rows");
484 }
485 jpeg_finish_decompress(&cinfo);
486
487// SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config());
488 return true;
489}
490
491///////////////////////////////////////////////////////////////////////////////
492
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493#include "SkColorPriv.h"
494
495// taken from jcolor.c in libjpeg
496#if 0 // 16bit - precise but slow
497 #define CYR 19595 // 0.299
498 #define CYG 38470 // 0.587
499 #define CYB 7471 // 0.114
500
501 #define CUR -11059 // -0.16874
502 #define CUG -21709 // -0.33126
503 #define CUB 32768 // 0.5
504
505 #define CVR 32768 // 0.5
506 #define CVG -27439 // -0.41869
507 #define CVB -5329 // -0.08131
508
509 #define CSHIFT 16
510#else // 8bit - fast, slightly less precise
511 #define CYR 77 // 0.299
512 #define CYG 150 // 0.587
513 #define CYB 29 // 0.114
514
515 #define CUR -43 // -0.16874
516 #define CUG -85 // -0.33126
517 #define CUB 128 // 0.5
518
519 #define CVR 128 // 0.5
520 #define CVG -107 // -0.41869
521 #define CVB -21 // -0.08131
522
523 #define CSHIFT 8
524#endif
525
526static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
527 int r = SkGetPackedR32(c);
528 int g = SkGetPackedG32(c);
529 int b = SkGetPackedB32(c);
530
531 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
532 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
533 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
534
535 dst[0] = SkToU8(y);
536 dst[1] = SkToU8(u + 128);
537 dst[2] = SkToU8(v + 128);
538}
539
540static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
541 int r = SkGetPackedR4444(c);
542 int g = SkGetPackedG4444(c);
543 int b = SkGetPackedB4444(c);
544
545 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
546 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
547 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
548
549 dst[0] = SkToU8(y);
550 dst[1] = SkToU8(u + 128);
551 dst[2] = SkToU8(v + 128);
552}
553
554static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
555 int r = SkGetPackedR16(c);
556 int g = SkGetPackedG16(c);
557 int b = SkGetPackedB16(c);
558
559 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
560 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
561 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
562
563 dst[0] = SkToU8(y);
564 dst[1] = SkToU8(u + 128);
565 dst[2] = SkToU8(v + 128);
566}
567
568///////////////////////////////////////////////////////////////////////////////
569
570typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
571 const void* SK_RESTRICT src, int width,
572 const SkPMColor* SK_RESTRICT ctable);
573
574static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
575 const void* SK_RESTRICT srcRow, int width,
576 const SkPMColor*) {
577 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
578 while (--width >= 0) {
579#ifdef WE_CONVERT_TO_YUV
580 rgb2yuv_32(dst, *src++);
581#else
582 uint32_t c = *src++;
583 dst[0] = SkGetPackedR32(c);
584 dst[1] = SkGetPackedG32(c);
585 dst[2] = SkGetPackedB32(c);
586#endif
587 dst += 3;
588 }
589}
590
591static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
592 const void* SK_RESTRICT srcRow, int width,
593 const SkPMColor*) {
594 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
595 while (--width >= 0) {
596#ifdef WE_CONVERT_TO_YUV
597 rgb2yuv_4444(dst, *src++);
598#else
599 SkPMColor16 c = *src++;
600 dst[0] = SkPacked4444ToR32(c);
601 dst[1] = SkPacked4444ToG32(c);
602 dst[2] = SkPacked4444ToB32(c);
603#endif
604 dst += 3;
605 }
606}
607
608static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
609 const void* SK_RESTRICT srcRow, int width,
610 const SkPMColor*) {
611 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
612 while (--width >= 0) {
613#ifdef WE_CONVERT_TO_YUV
614 rgb2yuv_16(dst, *src++);
615#else
616 uint16_t c = *src++;
617 dst[0] = SkPacked16ToR32(c);
618 dst[1] = SkPacked16ToG32(c);
619 dst[2] = SkPacked16ToB32(c);
620#endif
621 dst += 3;
622 }
623}
624
625static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
626 const void* SK_RESTRICT srcRow, int width,
627 const SkPMColor* SK_RESTRICT ctable) {
628 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
629 while (--width >= 0) {
630#ifdef WE_CONVERT_TO_YUV
631 rgb2yuv_32(dst, ctable[*src++]);
632#else
633 uint32_t c = ctable[*src++];
634 dst[0] = SkGetPackedR32(c);
635 dst[1] = SkGetPackedG32(c);
636 dst[2] = SkGetPackedB32(c);
637#endif
638 dst += 3;
639 }
640}
641
642static WriteScanline ChooseWriter(const SkBitmap& bm) {
643 switch (bm.config()) {
644 case SkBitmap::kARGB_8888_Config:
645 return Write_32_YUV;
646 case SkBitmap::kRGB_565_Config:
647 return Write_16_YUV;
648 case SkBitmap::kARGB_4444_Config:
649 return Write_4444_YUV;
650 case SkBitmap::kIndex8_Config:
651 return Write_Index_YUV;
652 default:
653 return NULL;
654 }
655}
656
657struct sk_destination_mgr : jpeg_destination_mgr {
658 sk_destination_mgr(SkWStream* stream);
659
660 SkWStream* fStream;
661
662 enum {
663 kBufferSize = 1024
664 };
665 uint8_t fBuffer[kBufferSize];
666};
667
668static void sk_init_destination(j_compress_ptr cinfo) {
669 sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
670
671 dest->next_output_byte = dest->fBuffer;
672 dest->free_in_buffer = sk_destination_mgr::kBufferSize;
673}
674
675static boolean sk_empty_output_buffer(j_compress_ptr cinfo) {
676 sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
677
678// if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize - dest->free_in_buffer))
679 if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize)) {
680 ERREXIT(cinfo, JERR_FILE_WRITE);
681 return false;
682 }
683
684 dest->next_output_byte = dest->fBuffer;
685 dest->free_in_buffer = sk_destination_mgr::kBufferSize;
686 return TRUE;
687}
688
689static void sk_term_destination (j_compress_ptr cinfo) {
690 sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
691
692 size_t size = sk_destination_mgr::kBufferSize - dest->free_in_buffer;
693 if (size > 0) {
694 if (!dest->fStream->write(dest->fBuffer, size)) {
695 ERREXIT(cinfo, JERR_FILE_WRITE);
696 return;
697 }
698 }
699 dest->fStream->flush();
700}
701
702sk_destination_mgr::sk_destination_mgr(SkWStream* stream)
703 : fStream(stream) {
704 this->init_destination = sk_init_destination;
705 this->empty_output_buffer = sk_empty_output_buffer;
706 this->term_destination = sk_term_destination;
707}
708
709class SkJPEGImageEncoder : public SkImageEncoder {
710protected:
711 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
712#ifdef TIME_ENCODE
713 AutoTimeMillis atm("JPEG Encode");
714#endif
715
716 const WriteScanline writer = ChooseWriter(bm);
717 if (NULL == writer) {
718 return false;
719 }
720
721 SkAutoLockPixels alp(bm);
722 if (NULL == bm.getPixels()) {
723 return false;
724 }
725
726 jpeg_compress_struct cinfo;
727 sk_error_mgr sk_err;
728 sk_destination_mgr sk_wstream(stream);
729
730 // allocate these before set call setjmp
731 SkAutoMalloc oneRow;
732 SkAutoLockColors ctLocker;
733
734 cinfo.err = jpeg_std_error(&sk_err);
735 sk_err.error_exit = sk_error_exit;
736 if (setjmp(sk_err.fJmpBuf)) {
737 return false;
738 }
739 jpeg_create_compress(&cinfo);
740
741 cinfo.dest = &sk_wstream;
742 cinfo.image_width = bm.width();
743 cinfo.image_height = bm.height();
744 cinfo.input_components = 3;
745#ifdef WE_CONVERT_TO_YUV
746 cinfo.in_color_space = JCS_YCbCr;
747#else
748 cinfo.in_color_space = JCS_RGB;
749#endif
750 cinfo.input_gamma = 1;
751
752 jpeg_set_defaults(&cinfo);
753 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
754 cinfo.dct_method = JDCT_IFAST;
755
756 jpeg_start_compress(&cinfo, TRUE);
757
758 const int width = bm.width();
759 uint8_t* oneRowP = (uint8_t*)oneRow.alloc(width * 3);
760
761 const SkPMColor* colors = ctLocker.lockColors(bm);
762 const void* srcRow = bm.getPixels();
763
764 while (cinfo.next_scanline < cinfo.image_height) {
765 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
766
767 writer(oneRowP, srcRow, width, colors);
768 row_pointer[0] = oneRowP;
769 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
770 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
771 }
772
773 jpeg_finish_compress(&cinfo);
774 jpeg_destroy_compress(&cinfo);
775
776 return true;
777 }
778};
779
reed@android.com00bf85a2009-01-22 13:04:56 +0000780///////////////////////////////////////////////////////////////////////////////
781
782#include "SkTRegistry.h"
783
784static SkImageDecoder* DFactory(SkStream* stream) {
785 static const char gHeader[] = { 0xFF, 0xD8, 0xFF };
786 static const size_t HEADER_SIZE = sizeof(gHeader);
787
788 char buffer[HEADER_SIZE];
789 size_t len = stream->read(buffer, HEADER_SIZE);
790
791 if (len != HEADER_SIZE) {
792 return NULL; // can't read enough
793 }
794 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
795 return NULL;
796 }
797 return SkNEW(SkJPEGImageDecoder);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798}
799
reed@android.com00bf85a2009-01-22 13:04:56 +0000800static SkImageEncoder* EFactory(SkImageEncoder::Type t) {
801 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802}
803
reed@android.com00bf85a2009-01-22 13:04:56 +0000804static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);
805static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
806