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