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