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