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