SkImageDecoder_libjpeg.cpp revision 59b740028752e741e27ce7e7652a59ae56b5450f
1
2/*
3 * Copyright 2007 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkImageDecoder.h"
11#include "SkImageEncoder.h"
12#include "SkJpegUtility.h"
13#include "SkColorPriv.h"
14#include "SkDither.h"
15#include "SkScaledBitmapSampler.h"
16#include "SkStream.h"
17#include "SkTemplates.h"
18#include "SkUtils.h"
19
20#include <stdio.h>
21extern "C" {
22    #include "jpeglib.h"
23    #include "jerror.h"
24}
25
26// this enables timing code to report milliseconds for an encode
27//#define TIME_ENCODE
28//#define TIME_DECODE
29
30// this enables our rgb->yuv code, which is faster than libjpeg on ARM
31// disable for the moment, as we have some glitches when width != multiple of 4
32#define WE_CONVERT_TO_YUV
33
34//////////////////////////////////////////////////////////////////////////
35//////////////////////////////////////////////////////////////////////////
36
37class SkJPEGImageDecoder : public SkImageDecoder {
38public:
39    virtual Format getFormat() const {
40        return kJPEG_Format;
41    }
42
43protected:
44    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
45};
46
47//////////////////////////////////////////////////////////////////////////
48
49#include "SkTime.h"
50
51class AutoTimeMillis {
52public:
53    AutoTimeMillis(const char label[]) : fLabel(label) {
54        if (!fLabel) {
55            fLabel = "";
56        }
57        fNow = SkTime::GetMSecs();
58    }
59    ~AutoTimeMillis() {
60        SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
61    }
62private:
63    const char* fLabel;
64    SkMSec      fNow;
65};
66
67/* Automatically clean up after throwing an exception */
68class JPEGAutoClean {
69public:
70    JPEGAutoClean(): cinfo_ptr(NULL) {}
71    ~JPEGAutoClean() {
72        if (cinfo_ptr) {
73            jpeg_destroy_decompress(cinfo_ptr);
74        }
75    }
76    void set(jpeg_decompress_struct* info) {
77        cinfo_ptr = info;
78    }
79private:
80    jpeg_decompress_struct* cinfo_ptr;
81};
82
83#ifdef SK_BUILD_FOR_ANDROID
84
85/* For non-ndk builds we could look at the system's jpeg memory cap and use it
86 * if it is set. However, for now we will use the NDK compliant hardcoded values
87 */
88//#include <cutils/properties.h>
89//static const char KEY_MEM_CAP[] = "ro.media.dec.jpeg.memcap";
90
91static void overwrite_mem_buffer_size(j_decompress_ptr cinfo) {
92#ifdef ANDROID_LARGE_MEMORY_DEVICE
93    cinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
94#else
95    cinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
96#endif
97}
98#endif
99
100
101///////////////////////////////////////////////////////////////////////////////
102
103/*  If we need to better match the request, we might examine the image and
104     output dimensions, and determine if the downsampling jpeg provided is
105     not sufficient. If so, we can recompute a modified sampleSize value to
106     make up the difference.
107
108     To skip this additional scaling, just set sampleSize = 1; below.
109 */
110static int recompute_sampleSize(int sampleSize,
111                                const jpeg_decompress_struct& cinfo) {
112    return sampleSize * cinfo.output_width / cinfo.image_width;
113}
114
115static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) {
116    /* These are initialized to 0, so if they have non-zero values, we assume
117       they are "valid" (i.e. have been computed by libjpeg)
118     */
119    return cinfo.output_width != 0 && cinfo.output_height != 0;
120}
121
122static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer,
123                          int count) {
124    for (int i = 0; i < count; i++) {
125        JSAMPLE* rowptr = (JSAMPLE*)buffer;
126        int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1);
127        if (row_count != 1) {
128            return false;
129        }
130    }
131    return true;
132}
133
134// This guy exists just to aid in debugging, as it allows debuggers to just
135// set a break-point in one place to see all error exists.
136static bool return_false(const jpeg_decompress_struct& cinfo,
137                         const SkBitmap& bm, const char msg[]) {
138#if 0
139    SkDebugf("libjpeg error %d <%s> from %s [%d %d]", cinfo.err->msg_code,
140             cinfo.err->jpeg_message_table[cinfo.err->msg_code], msg,
141             bm.width(), bm.height());
142#endif
143    return false;   // must always return false
144}
145
146// Convert a scanline of CMYK samples to RGBX in place. Note that this
147// method moves the "scanline" pointer in its processing
148static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
149    // At this point we've received CMYK pixels from libjpeg. We
150    // perform a crude conversion to RGB (based on the formulae
151    // from easyrgb.com):
152    //  CMYK -> CMY
153    //    C = ( C * (1 - K) + K )      // for each CMY component
154    //  CMY -> RGB
155    //    R = ( 1 - C ) * 255          // for each RGB component
156    // Unfortunately we are seeing inverted CMYK so all the original terms
157    // are 1-. This yields:
158    //  CMYK -> CMY
159    //    C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
160    // The conversion from CMY->RGB remains the same
161    for (unsigned int x = 0; x < width; ++x, scanline += 4) {
162        scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
163        scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
164        scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
165        scanline[3] = 255;
166    }
167}
168
169bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
170#ifdef TIME_DECODE
171    AutoTimeMillis atm("JPEG Decode");
172#endif
173
174    SkAutoMalloc  srcStorage;
175    JPEGAutoClean autoClean;
176
177    jpeg_decompress_struct  cinfo;
178    skjpeg_error_mgr        sk_err;
179    skjpeg_source_mgr       sk_stream(stream, this, false);
180
181    cinfo.err = jpeg_std_error(&sk_err);
182    sk_err.error_exit = skjpeg_error_exit;
183
184    // All objects need to be instantiated before this setjmp call so that
185    // they will be cleaned up properly if an error occurs.
186    if (setjmp(sk_err.fJmpBuf)) {
187        return return_false(cinfo, *bm, "setjmp");
188    }
189
190    jpeg_create_decompress(&cinfo);
191    autoClean.set(&cinfo);
192
193#ifdef SK_BUILD_FOR_ANDROID
194    overwrite_mem_buffer_size(&cinfo);
195#endif
196
197    //jpeg_stdio_src(&cinfo, file);
198    cinfo.src = &sk_stream;
199
200    int status = jpeg_read_header(&cinfo, true);
201    if (status != JPEG_HEADER_OK) {
202        return return_false(cinfo, *bm, "read_header");
203    }
204
205    /*  Try to fulfill the requested sampleSize. Since jpeg can do it (when it
206        can) much faster that we, just use their num/denom api to approximate
207        the size.
208    */
209    int sampleSize = this->getSampleSize();
210
211    cinfo.dct_method = JDCT_IFAST;
212    cinfo.scale_num = 1;
213    cinfo.scale_denom = sampleSize;
214
215    /* this gives about 30% performance improvement. In theory it may
216       reduce the visual quality, in practice I'm not seeing a difference
217     */
218    cinfo.do_fancy_upsampling = 0;
219
220    /* this gives another few percents */
221    cinfo.do_block_smoothing = 0;
222
223    /* default format is RGB */
224    if (cinfo.jpeg_color_space == JCS_CMYK) {
225        // libjpeg cannot convert from CMYK to RGB - here we set up
226        // so libjpeg will give us CMYK samples back and we will
227        // later manually convert them to RGB
228        cinfo.out_color_space = JCS_CMYK;
229    } else {
230        cinfo.out_color_space = JCS_RGB;
231    }
232
233    SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
234    // only these make sense for jpegs
235    if (config != SkBitmap::kARGB_8888_Config &&
236        config != SkBitmap::kARGB_4444_Config &&
237        config != SkBitmap::kRGB_565_Config) {
238        config = SkBitmap::kARGB_8888_Config;
239    }
240
241#ifdef ANDROID_RGB
242    cinfo.dither_mode = JDITHER_NONE;
243    if (SkBitmap::kARGB_8888_Config == config && JCS_CMYK != cinfo.out_color_space) {
244        cinfo.out_color_space = JCS_RGBA_8888;
245    } else if (SkBitmap::kRGB_565_Config == config && JCS_CMYK != cinfo.out_color_space) {
246        cinfo.out_color_space = JCS_RGB_565;
247        if (this->getDitherImage()) {
248            cinfo.dither_mode = JDITHER_ORDERED;
249        }
250    }
251#endif
252
253    if (sampleSize == 1 && mode == SkImageDecoder::kDecodeBounds_Mode) {
254        bm->setConfig(config, cinfo.image_width, cinfo.image_height);
255        bm->setIsOpaque(true);
256        return true;
257    }
258
259    /*  image_width and image_height are the original dimensions, available
260        after jpeg_read_header(). To see the scaled dimensions, we have to call
261        jpeg_start_decompress(), and then read output_width and output_height.
262    */
263    if (!jpeg_start_decompress(&cinfo)) {
264        /*  If we failed here, we may still have enough information to return
265            to the caller if they just wanted (subsampled bounds). If sampleSize
266            was 1, then we would have already returned. Thus we just check if
267            we're in kDecodeBounds_Mode, and that we have valid output sizes.
268
269            One reason to fail here is that we have insufficient stream data
270            to complete the setup. However, output dimensions seem to get
271            computed very early, which is why this special check can pay off.
272         */
273        if (SkImageDecoder::kDecodeBounds_Mode == mode &&
274                valid_output_dimensions(cinfo)) {
275            SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,
276                                       recompute_sampleSize(sampleSize, cinfo));
277            bm->setConfig(config, smpl.scaledWidth(), smpl.scaledHeight());
278            bm->setIsOpaque(true);
279            return true;
280        } else {
281            return return_false(cinfo, *bm, "start_decompress");
282        }
283    }
284    sampleSize = recompute_sampleSize(sampleSize, cinfo);
285
286    // should we allow the Chooser (if present) to pick a config for us???
287    if (!this->chooseFromOneChoice(config, cinfo.output_width,
288                                   cinfo.output_height)) {
289        return return_false(cinfo, *bm, "chooseFromOneChoice");
290    }
291
292#ifdef ANDROID_RGB
293    /* short-circuit the SkScaledBitmapSampler when possible, as this gives
294       a significant performance boost.
295    */
296    if (sampleSize == 1 &&
297        ((config == SkBitmap::kARGB_8888_Config &&
298                cinfo.out_color_space == JCS_RGBA_8888) ||
299        (config == SkBitmap::kRGB_565_Config &&
300                cinfo.out_color_space == JCS_RGB_565)))
301    {
302        bm->setConfig(config, cinfo.output_width, cinfo.output_height);
303        bm->setIsOpaque(true);
304        if (SkImageDecoder::kDecodeBounds_Mode == mode) {
305            return true;
306        }
307        if (!this->allocPixelRef(bm, NULL)) {
308            return return_false(cinfo, *bm, "allocPixelRef");
309        }
310        SkAutoLockPixels alp(*bm);
311        JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();
312        INT32 const bpr =  bm->rowBytes();
313
314        while (cinfo.output_scanline < cinfo.output_height) {
315            int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
316            // if row_count == 0, then we didn't get a scanline, so abort.
317            // if we supported partial images, we might return true in this case
318            if (0 == row_count) {
319                return return_false(cinfo, *bm, "read_scanlines");
320            }
321            if (this->shouldCancelDecode()) {
322                return return_false(cinfo, *bm, "shouldCancelDecode");
323            }
324            rowptr += bpr;
325        }
326        jpeg_finish_decompress(&cinfo);
327        return true;
328    }
329#endif
330
331    // check for supported formats
332    SkScaledBitmapSampler::SrcConfig sc;
333    if (JCS_CMYK == cinfo.out_color_space) {
334        // In this case we will manually convert the CMYK values to RGB
335        sc = SkScaledBitmapSampler::kRGBX;
336    } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) {
337        sc = SkScaledBitmapSampler::kRGB;
338#ifdef ANDROID_RGB
339    } else if (JCS_RGBA_8888 == cinfo.out_color_space) {
340        sc = SkScaledBitmapSampler::kRGBX;
341    } else if (JCS_RGB_565 == cinfo.out_color_space) {
342        sc = SkScaledBitmapSampler::kRGB_565;
343#endif
344    } else if (1 == cinfo.out_color_components &&
345               JCS_GRAYSCALE == cinfo.out_color_space) {
346        sc = SkScaledBitmapSampler::kGray;
347    } else {
348        return return_false(cinfo, *bm, "jpeg colorspace");
349    }
350
351    SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height,
352                                  sampleSize);
353
354    bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
355    // jpegs are always opaque (i.e. have no per-pixel alpha)
356    bm->setIsOpaque(true);
357
358    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
359        return true;
360    }
361    if (!this->allocPixelRef(bm, NULL)) {
362        return return_false(cinfo, *bm, "allocPixelRef");
363    }
364
365    SkAutoLockPixels alp(*bm);
366    if (!sampler.begin(bm, sc, this->getDitherImage())) {
367        return return_false(cinfo, *bm, "sampler.begin");
368    }
369
370    // The CMYK work-around relies on 4 components per pixel here
371    uint8_t* srcRow = (uint8_t*)srcStorage.reset(cinfo.output_width * 4);
372
373    //  Possibly skip initial rows [sampler.srcY0]
374    if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
375        return return_false(cinfo, *bm, "skip rows");
376    }
377
378    // now loop through scanlines until y == bm->height() - 1
379    for (int y = 0;; y++) {
380        JSAMPLE* rowptr = (JSAMPLE*)srcRow;
381        int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
382        if (0 == row_count) {
383            return return_false(cinfo, *bm, "read_scanlines");
384        }
385        if (this->shouldCancelDecode()) {
386            return return_false(cinfo, *bm, "shouldCancelDecode");
387        }
388
389        if (JCS_CMYK == cinfo.out_color_space) {
390            convert_CMYK_to_RGB(srcRow, cinfo.output_width);
391        }
392
393        sampler.next(srcRow);
394        if (bm->height() - 1 == y) {
395            // we're done
396            break;
397        }
398
399        if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
400            return return_false(cinfo, *bm, "skip rows");
401        }
402    }
403
404    // we formally skip the rest, so we don't get a complaint from libjpeg
405    if (!skip_src_rows(&cinfo, srcRow,
406                       cinfo.output_height - cinfo.output_scanline)) {
407        return return_false(cinfo, *bm, "skip rows");
408    }
409    jpeg_finish_decompress(&cinfo);
410
411//    SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config());
412    return true;
413}
414
415///////////////////////////////////////////////////////////////////////////////
416
417#include "SkColorPriv.h"
418
419// taken from jcolor.c in libjpeg
420#if 0   // 16bit - precise but slow
421    #define CYR     19595   // 0.299
422    #define CYG     38470   // 0.587
423    #define CYB      7471   // 0.114
424
425    #define CUR    -11059   // -0.16874
426    #define CUG    -21709   // -0.33126
427    #define CUB     32768   // 0.5
428
429    #define CVR     32768   // 0.5
430    #define CVG    -27439   // -0.41869
431    #define CVB     -5329   // -0.08131
432
433    #define CSHIFT  16
434#else      // 8bit - fast, slightly less precise
435    #define CYR     77    // 0.299
436    #define CYG     150    // 0.587
437    #define CYB      29    // 0.114
438
439    #define CUR     -43    // -0.16874
440    #define CUG    -85    // -0.33126
441    #define CUB     128    // 0.5
442
443    #define CVR      128   // 0.5
444    #define CVG     -107   // -0.41869
445    #define CVB      -21   // -0.08131
446
447    #define CSHIFT  8
448#endif
449
450static void rgb2yuv_32(uint8_t dst[], SkPMColor c) {
451    int r = SkGetPackedR32(c);
452    int g = SkGetPackedG32(c);
453    int b = SkGetPackedB32(c);
454
455    int  y = ( CYR*r + CYG*g + CYB*b ) >> CSHIFT;
456    int  u = ( CUR*r + CUG*g + CUB*b ) >> CSHIFT;
457    int  v = ( CVR*r + CVG*g + CVB*b ) >> CSHIFT;
458
459    dst[0] = SkToU8(y);
460    dst[1] = SkToU8(u + 128);
461    dst[2] = SkToU8(v + 128);
462}
463
464static void rgb2yuv_4444(uint8_t dst[], U16CPU c) {
465    int r = SkGetPackedR4444(c);
466    int g = SkGetPackedG4444(c);
467    int b = SkGetPackedB4444(c);
468
469    int  y = ( CYR*r + CYG*g + CYB*b ) >> (CSHIFT - 4);
470    int  u = ( CUR*r + CUG*g + CUB*b ) >> (CSHIFT - 4);
471    int  v = ( CVR*r + CVG*g + CVB*b ) >> (CSHIFT - 4);
472
473    dst[0] = SkToU8(y);
474    dst[1] = SkToU8(u + 128);
475    dst[2] = SkToU8(v + 128);
476}
477
478static void rgb2yuv_16(uint8_t dst[], U16CPU c) {
479    int r = SkGetPackedR16(c);
480    int g = SkGetPackedG16(c);
481    int b = SkGetPackedB16(c);
482
483    int  y = ( 2*CYR*r + CYG*g + 2*CYB*b ) >> (CSHIFT - 2);
484    int  u = ( 2*CUR*r + CUG*g + 2*CUB*b ) >> (CSHIFT - 2);
485    int  v = ( 2*CVR*r + CVG*g + 2*CVB*b ) >> (CSHIFT - 2);
486
487    dst[0] = SkToU8(y);
488    dst[1] = SkToU8(u + 128);
489    dst[2] = SkToU8(v + 128);
490}
491
492///////////////////////////////////////////////////////////////////////////////
493
494typedef void (*WriteScanline)(uint8_t* SK_RESTRICT dst,
495                              const void* SK_RESTRICT src, int width,
496                              const SkPMColor* SK_RESTRICT ctable);
497
498static void Write_32_YUV(uint8_t* SK_RESTRICT dst,
499                         const void* SK_RESTRICT srcRow, int width,
500                         const SkPMColor*) {
501    const uint32_t* SK_RESTRICT src = (const uint32_t*)srcRow;
502    while (--width >= 0) {
503#ifdef WE_CONVERT_TO_YUV
504        rgb2yuv_32(dst, *src++);
505#else
506        uint32_t c = *src++;
507        dst[0] = SkGetPackedR32(c);
508        dst[1] = SkGetPackedG32(c);
509        dst[2] = SkGetPackedB32(c);
510#endif
511        dst += 3;
512    }
513}
514
515static void Write_4444_YUV(uint8_t* SK_RESTRICT dst,
516                           const void* SK_RESTRICT srcRow, int width,
517                           const SkPMColor*) {
518    const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)srcRow;
519    while (--width >= 0) {
520#ifdef WE_CONVERT_TO_YUV
521        rgb2yuv_4444(dst, *src++);
522#else
523        SkPMColor16 c = *src++;
524        dst[0] = SkPacked4444ToR32(c);
525        dst[1] = SkPacked4444ToG32(c);
526        dst[2] = SkPacked4444ToB32(c);
527#endif
528        dst += 3;
529    }
530}
531
532static void Write_16_YUV(uint8_t* SK_RESTRICT dst,
533                         const void* SK_RESTRICT srcRow, int width,
534                         const SkPMColor*) {
535    const uint16_t* SK_RESTRICT src = (const uint16_t*)srcRow;
536    while (--width >= 0) {
537#ifdef WE_CONVERT_TO_YUV
538        rgb2yuv_16(dst, *src++);
539#else
540        uint16_t c = *src++;
541        dst[0] = SkPacked16ToR32(c);
542        dst[1] = SkPacked16ToG32(c);
543        dst[2] = SkPacked16ToB32(c);
544#endif
545        dst += 3;
546    }
547}
548
549static void Write_Index_YUV(uint8_t* SK_RESTRICT dst,
550                            const void* SK_RESTRICT srcRow, int width,
551                            const SkPMColor* SK_RESTRICT ctable) {
552    const uint8_t* SK_RESTRICT src = (const uint8_t*)srcRow;
553    while (--width >= 0) {
554#ifdef WE_CONVERT_TO_YUV
555        rgb2yuv_32(dst, ctable[*src++]);
556#else
557        uint32_t c = ctable[*src++];
558        dst[0] = SkGetPackedR32(c);
559        dst[1] = SkGetPackedG32(c);
560        dst[2] = SkGetPackedB32(c);
561#endif
562        dst += 3;
563    }
564}
565
566static WriteScanline ChooseWriter(const SkBitmap& bm) {
567    switch (bm.config()) {
568        case SkBitmap::kARGB_8888_Config:
569            return Write_32_YUV;
570        case SkBitmap::kRGB_565_Config:
571            return Write_16_YUV;
572        case SkBitmap::kARGB_4444_Config:
573            return Write_4444_YUV;
574        case SkBitmap::kIndex8_Config:
575            return Write_Index_YUV;
576        default:
577            return NULL;
578    }
579}
580
581class SkJPEGImageEncoder : public SkImageEncoder {
582protected:
583    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
584#ifdef TIME_ENCODE
585        AutoTimeMillis atm("JPEG Encode");
586#endif
587
588        const WriteScanline writer = ChooseWriter(bm);
589        if (NULL == writer) {
590            return false;
591        }
592
593        SkAutoLockPixels alp(bm);
594        if (NULL == bm.getPixels()) {
595            return false;
596        }
597
598        jpeg_compress_struct    cinfo;
599        skjpeg_error_mgr        sk_err;
600        skjpeg_destination_mgr  sk_wstream(stream);
601
602        // allocate these before set call setjmp
603        SkAutoMalloc    oneRow;
604        SkAutoLockColors ctLocker;
605
606        cinfo.err = jpeg_std_error(&sk_err);
607        sk_err.error_exit = skjpeg_error_exit;
608        if (setjmp(sk_err.fJmpBuf)) {
609            return false;
610        }
611        jpeg_create_compress(&cinfo);
612
613        cinfo.dest = &sk_wstream;
614        cinfo.image_width = bm.width();
615        cinfo.image_height = bm.height();
616        cinfo.input_components = 3;
617#ifdef WE_CONVERT_TO_YUV
618        cinfo.in_color_space = JCS_YCbCr;
619#else
620        cinfo.in_color_space = JCS_RGB;
621#endif
622        cinfo.input_gamma = 1;
623
624        jpeg_set_defaults(&cinfo);
625        jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
626        cinfo.dct_method = JDCT_IFAST;
627
628        jpeg_start_compress(&cinfo, TRUE);
629
630        const int       width = bm.width();
631        uint8_t*        oneRowP = (uint8_t*)oneRow.reset(width * 3);
632
633        const SkPMColor* colors = ctLocker.lockColors(bm);
634        const void*      srcRow = bm.getPixels();
635
636        while (cinfo.next_scanline < cinfo.image_height) {
637            JSAMPROW row_pointer[1];    /* pointer to JSAMPLE row[s] */
638
639            writer(oneRowP, srcRow, width, colors);
640            row_pointer[0] = oneRowP;
641            (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
642            srcRow = (const void*)((const char*)srcRow + bm.rowBytes());
643        }
644
645        jpeg_finish_compress(&cinfo);
646        jpeg_destroy_compress(&cinfo);
647
648        return true;
649    }
650};
651
652///////////////////////////////////////////////////////////////////////////////
653DEFINE_DECODER_CREATOR(JPEGImageDecoder);
654DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
655///////////////////////////////////////////////////////////////////////////////
656
657#include "SkTRegistry.h"
658
659SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) {
660    static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
661    static const size_t HEADER_SIZE = sizeof(gHeader);
662
663    char buffer[HEADER_SIZE];
664    size_t len = stream->read(buffer, HEADER_SIZE);
665
666    if (len != HEADER_SIZE) {
667        return NULL;   // can't read enough
668    }
669    if (memcmp(buffer, gHeader, HEADER_SIZE)) {
670        return NULL;
671    }
672    return SkNEW(SkJPEGImageDecoder);
673}
674
675static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
676    return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
677}
678
679
680static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory);
681static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efactory);
682
683