blob: 018c96c353ae9b242a579c2d9f50a8cbc7f3663b [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
346 /* image_width and image_height are the original dimensions, available
347 after jpeg_read_header(). To see the scaled dimensions, we have to call
348 jpeg_start_decompress(), and then read output_width and output_height.
349 */
350 if (!jpeg_start_decompress(&cinfo)) {
351 return return_false(cinfo, *bm, "start_decompress");
352 }
353
354 /* If we need to better match the request, we might examine the image and
355 output dimensions, and determine if the downsampling jpeg provided is
356 not sufficient. If so, we can recompute a modified sampleSize value to
357 make up the difference.
358
359 To skip this additional scaling, just set sampleSize = 1; below.
360 */
361 sampleSize = sampleSize * cinfo.output_width / cinfo.image_width;
362
363
364 // should we allow the Chooser (if present) to pick a config for us???
365 if (!this->chooseFromOneChoice(config, cinfo.output_width,
366 cinfo.output_height)) {
367 return return_false(cinfo, *bm, "chooseFromOneChoice");
368 }
369
370#ifdef ANDROID_RGB
371 /* short-circuit the SkScaledBitmapSampler when possible, as this gives
372 a significant performance boost.
373 */
374 if (sampleSize == 1 &&
375 ((config == SkBitmap::kARGB_8888_Config &&
376 cinfo.out_color_space == JCS_RGBA_8888) ||
377 (config == SkBitmap::kRGB_565_Config &&
378 cinfo.out_color_space == JCS_RGB_565)))
379 {
380 bm->setConfig(config, cinfo.output_width, cinfo.output_height);
381 bm->setIsOpaque(true);
382 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
383 return true;
384 }
385 if (!this->allocPixelRef(bm, NULL)) {
386 return return_false(cinfo, *bm, "allocPixelRef");
387 }
388 SkAutoLockPixels alp(*bm);
389 JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
390 INT32 const bpr = bm->rowBytes();
391
392 while (cinfo.output_scanline < cinfo.output_height) {
393 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
394 // if row_count == 0, then we didn't get a scanline, so abort.
395 // if we supported partial images, we might return true in this case
396 if (0 == row_count) {
397 return return_false(cinfo, *bm, "read_scanlines");
398 }
399 if (this->shouldCancelDecode()) {
400 return return_false(cinfo, *bm, "shouldCancelDecode");
401 }
402 rowptr += bpr;
403 }
404 jpeg_finish_decompress(&cinfo);
405 return true;
406 }
407#endif
408
409 // check for supported formats
410 SkScaledBitmapSampler::SrcConfig sc;
411 if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
412 sc = SkScaledBitmapSampler::kRGB;
413#ifdef ANDROID_RGB
414 } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
415 sc = SkScaledBitmapSampler::kRGBX;
416 //} else if (JCS_RGB_565 == cinfo.out_color_space) {
417 // sc = SkScaledBitmapSampler::kRGB_565;
418#endif
419 } else if (1 == cinfo.out_color_components &&
420 JCS_GRAYSCALE == cinfo.out_color_space) {
421 sc = SkScaledBitmapSampler::kGray;
422 } else {
423 return return_false(cinfo, *bm, "jpeg colorspace");
424 }
425
426 SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height,
427 sampleSize);
428
429 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
430 // jpegs are always opauqe (i.e. have no per-pixel alpha)
431 bm->setIsOpaque(true);
432
433 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
434 return true;
435 }
436 if (!this->allocPixelRef(bm, NULL)) {
437 return return_false(cinfo, *bm, "allocPixelRef");
438 }
439
440 SkAutoLockPixels alp(*bm);
441 if (!sampler.begin(bm, sc, this->getDitherImage())) {
442 return return_false(cinfo, *bm, "sampler.begin");
443 }
444
445 uint8_t* srcRow = (uint8_t*)srcStorage.alloc(cinfo.output_width * 4);
446
447 // Possibly skip initial rows [sampler.srcY0]
448 if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
449 return return_false(cinfo, *bm, "skip rows");
450 }
451
452 // now loop through scanlines until y == bm->height() - 1
453 for (int y = 0;; y++) {
454 JSAMPLE* rowptr = (JSAMPLE*)srcRow;
455 int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
456 if (0 == row_count) {
457 return return_false(cinfo, *bm, "read_scanlines");
458 }
459 if (this->shouldCancelDecode()) {
460 return return_false(cinfo, *bm, "shouldCancelDecode");
461 }
462
463 sampler.next(srcRow);
464 if (bm->height() - 1 == y) {
465 // we're done
466 break;
467 }
468
469 if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
470 return return_false(cinfo, *bm, "skip rows");
471 }
472 }
473
474 // we formally skip the rest, so we don't get a complaint from libjpeg
475 if (!skip_src_rows(&cinfo, srcRow,
476 cinfo.output_height - cinfo.output_scanline)) {
477 return return_false(cinfo, *bm, "skip rows");
478 }
479 jpeg_finish_decompress(&cinfo);
480
481// SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config());
482 return true;
483}
484
485///////////////////////////////////////////////////////////////////////////////
486
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487#include "SkColorPriv.h"
488
489// taken from jcolor.c in libjpeg
490#if 0 // 16bit - precise but slow
491 #define CYR 19595 // 0.299
492 #define CYG 38470 // 0.587
493 #define CYB 7471 // 0.114
494
495 #define CUR -11059 // -0.16874
496 #define CUG -21709 // -0.33126
497 #define CUB 32768 // 0.5
498
499 #define CVR 32768 // 0.5
500 #define CVG -27439 // -0.41869
501 #define CVB -5329 // -0.08131
502
503 #define CSHIFT 16
504#else // 8bit - fast, slightly less precise
505 #define CYR 77 // 0.299
506 #define CYG 150 // 0.587
507 #define CYB 29 // 0.114
508
509 #define CUR -43 // -0.16874
510 #define CUG -85 // -0.33126
511 #define CUB 128 // 0.5
512
513 #define CVR 128 // 0.5
514 #define CVG -107 // -0.41869
515 #define CVB -21 // -0.08131
516
517 #define CSHIFT 8
518#endif
519
520static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
521 int r = SkGetPackedR32(c);
522 int g = SkGetPackedG32(c);
523 int b = SkGetPackedB32(c);
524
525 int y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
526 int u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
527 int v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
528
529 dst[0] = SkToU8(y);
530 dst[1] = SkToU8(u + 128);
531 dst[2] = SkToU8(v + 128);
532}
533
534static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
535 int r = SkGetPackedR4444(c);
536 int g = SkGetPackedG4444(c);
537 int b = SkGetPackedB4444(c);
538
539 int y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
540 int u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
541 int v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
542
543 dst[0] = SkToU8(y);
544 dst[1] = SkToU8(u + 128);
545 dst[2] = SkToU8(v + 128);
546}
547
548static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
549 int r = SkGetPackedR16(c);
550 int g = SkGetPackedG16(c);
551 int b = SkGetPackedB16(c);
552
553 int y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
554 int u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
555 int v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
556
557 dst[0] = SkToU8(y);
558 dst[1] = SkToU8(u + 128);
559 dst[2] = SkToU8(v + 128);
560}
561
562///////////////////////////////////////////////////////////////////////////////
563
564typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
565 const void* SK_RESTRICT src, int width,
566 const SkPMColor* SK_RESTRICT ctable);
567
568static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
569 const void* SK_RESTRICT srcRow, int width,
570 const SkPMColor*) {
571 const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
572 while (--width >= 0) {
573#ifdef WE_CONVERT_TO_YUV
574 rgb2yuv_32(dst, *src++);
575#else
576 uint32_t c = *src++;
577 dst[0] = SkGetPackedR32(c);
578 dst[1] = SkGetPackedG32(c);
579 dst[2] = SkGetPackedB32(c);
580#endif
581 dst += 3;
582 }
583}
584
585static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
586 const void* SK_RESTRICT srcRow, int width,
587 const SkPMColor*) {
588 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
589 while (--width >= 0) {
590#ifdef WE_CONVERT_TO_YUV
591 rgb2yuv_4444(dst, *src++);
592#else
593 SkPMColor16 c = *src++;
594 dst[0] = SkPacked4444ToR32(c);
595 dst[1] = SkPacked4444ToG32(c);
596 dst[2] = SkPacked4444ToB32(c);
597#endif
598 dst += 3;
599 }
600}
601
602static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
603 const void* SK_RESTRICT srcRow, int width,
604 const SkPMColor*) {
605 const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
606 while (--width >= 0) {
607#ifdef WE_CONVERT_TO_YUV
608 rgb2yuv_16(dst, *src++);
609#else
610 uint16_t c = *src++;
611 dst[0] = SkPacked16ToR32(c);
612 dst[1] = SkPacked16ToG32(c);
613 dst[2] = SkPacked16ToB32(c);
614#endif
615 dst += 3;
616 }
617}
618
619static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
620 const void* SK_RESTRICT srcRow, int width,
621 const SkPMColor* SK_RESTRICT ctable) {
622 const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
623 while (--width >= 0) {
624#ifdef WE_CONVERT_TO_YUV
625 rgb2yuv_32(dst, ctable[*src++]);
626#else
627 uint32_t c = ctable[*src++];
628 dst[0] = SkGetPackedR32(c);
629 dst[1] = SkGetPackedG32(c);
630 dst[2] = SkGetPackedB32(c);
631#endif
632 dst += 3;
633 }
634}
635
636static WriteScanline ChooseWriter(const SkBitmap& bm) {
637 switch (bm.config()) {
638 case SkBitmap::kARGB_8888_Config:
639 return Write_32_YUV;
640 case SkBitmap::kRGB_565_Config:
641 return Write_16_YUV;
642 case SkBitmap::kARGB_4444_Config:
643 return Write_4444_YUV;
644 case SkBitmap::kIndex8_Config:
645 return Write_Index_YUV;
646 default:
647 return NULL;
648 }
649}
650
651struct sk_destination_mgr : jpeg_destination_mgr {
652 sk_destination_mgr(SkWStream* stream);
653
654 SkWStream* fStream;
655
656 enum {
657 kBufferSize = 1024
658 };
659 uint8_t fBuffer[kBufferSize];
660};
661
662static void sk_init_destination(j_compress_ptr cinfo) {
663 sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
664
665 dest->next_output_byte = dest->fBuffer;
666 dest->free_in_buffer = sk_destination_mgr::kBufferSize;
667}
668
669static boolean sk_empty_output_buffer(j_compress_ptr cinfo) {
670 sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
671
672// if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize - dest->free_in_buffer))
673 if (!dest->fStream->write(dest->fBuffer, sk_destination_mgr::kBufferSize)) {
674 ERREXIT(cinfo, JERR_FILE_WRITE);
675 return false;
676 }
677
678 dest->next_output_byte = dest->fBuffer;
679 dest->free_in_buffer = sk_destination_mgr::kBufferSize;
680 return TRUE;
681}
682
683static void sk_term_destination (j_compress_ptr cinfo) {
684 sk_destination_mgr* dest = (sk_destination_mgr*)cinfo->dest;
685
686 size_t size = sk_destination_mgr::kBufferSize - dest->free_in_buffer;
687 if (size > 0) {
688 if (!dest->fStream->write(dest->fBuffer, size)) {
689 ERREXIT(cinfo, JERR_FILE_WRITE);
690 return;
691 }
692 }
693 dest->fStream->flush();
694}
695
696sk_destination_mgr::sk_destination_mgr(SkWStream* stream)
697 : fStream(stream) {
698 this->init_destination = sk_init_destination;
699 this->empty_output_buffer = sk_empty_output_buffer;
700 this->term_destination = sk_term_destination;
701}
702
703class SkJPEGImageEncoder : public SkImageEncoder {
704protected:
705 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
706#ifdef TIME_ENCODE
707 AutoTimeMillis atm("JPEG Encode");
708#endif
709
710 const WriteScanline writer = ChooseWriter(bm);
711 if (NULL == writer) {
712 return false;
713 }
714
715 SkAutoLockPixels alp(bm);
716 if (NULL == bm.getPixels()) {
717 return false;
718 }
719
720 jpeg_compress_struct cinfo;
721 sk_error_mgr sk_err;
722 sk_destination_mgr sk_wstream(stream);
723
724 // allocate these before set call setjmp
725 SkAutoMalloc oneRow;
726 SkAutoLockColors ctLocker;
727
728 cinfo.err = jpeg_std_error(&sk_err);
729 sk_err.error_exit = sk_error_exit;
730 if (setjmp(sk_err.fJmpBuf)) {
731 return false;
732 }
733 jpeg_create_compress(&cinfo);
734
735 cinfo.dest = &sk_wstream;
736 cinfo.image_width = bm.width();
737 cinfo.image_height = bm.height();
738 cinfo.input_components = 3;
739#ifdef WE_CONVERT_TO_YUV
740 cinfo.in_color_space = JCS_YCbCr;
741#else
742 cinfo.in_color_space = JCS_RGB;
743#endif
744 cinfo.input_gamma = 1;
745
746 jpeg_set_defaults(&cinfo);
747 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
748 cinfo.dct_method = JDCT_IFAST;
749
750 jpeg_start_compress(&cinfo, TRUE);
751
752 const int width = bm.width();
753 uint8_t* oneRowP = (uint8_t*)oneRow.alloc(width * 3);
754
755 const SkPMColor* colors = ctLocker.lockColors(bm);
756 const void* srcRow = bm.getPixels();
757
758 while (cinfo.next_scanline < cinfo.image_height) {
759 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
760
761 writer(oneRowP, srcRow, width, colors);
762 row_pointer[0] = oneRowP;
763 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
764 srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
765 }
766
767 jpeg_finish_compress(&cinfo);
768 jpeg_destroy_compress(&cinfo);
769
770 return true;
771 }
772};
773
reed@android.com00bf85a2009-01-22 13:04:56 +0000774///////////////////////////////////////////////////////////////////////////////
775
776#include "SkTRegistry.h"
777
778static SkImageDecoder* DFactory(SkStream* stream) {
779 static const char gHeader[] = { 0xFF, 0xD8, 0xFF };
780 static const size_t HEADER_SIZE = sizeof(gHeader);
781
782 char buffer[HEADER_SIZE];
783 size_t len = stream->read(buffer, HEADER_SIZE);
784
785 if (len != HEADER_SIZE) {
786 return NULL; // can't read enough
787 }
788 if (memcmp(buffer, gHeader, HEADER_SIZE)) {
789 return NULL;
790 }
791 return SkNEW(SkJPEGImageDecoder);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792}
793
reed@android.com00bf85a2009-01-22 13:04:56 +0000794static SkImageEncoder* EFactory(SkImageEncoder::Type t) {
795 return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796}
797
reed@android.com00bf85a2009-01-22 13:04:56 +0000798static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);
799static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
800