Home | History | Annotate | Download | only in codec
      1 /*
      2  * Copyright 2015 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkCodec_libpng.h"
      9 #include "SkCodecPriv.h"
     10 #include "SkColorPriv.h"
     11 #include "SkColorTable.h"
     12 #include "SkBitmap.h"
     13 #include "SkMath.h"
     14 #include "SkScanlineDecoder.h"
     15 #include "SkSize.h"
     16 #include "SkStream.h"
     17 #include "SkSwizzler.h"
     18 
     19 ///////////////////////////////////////////////////////////////////////////////
     20 // Helper macros
     21 ///////////////////////////////////////////////////////////////////////////////
     22 
     23 #ifndef png_jmpbuf
     24 #  define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
     25 #endif
     26 
     27 /* These were dropped in libpng >= 1.4 */
     28 #ifndef png_infopp_NULL
     29     #define png_infopp_NULL NULL
     30 #endif
     31 
     32 #ifndef png_bytepp_NULL
     33     #define png_bytepp_NULL NULL
     34 #endif
     35 
     36 #ifndef int_p_NULL
     37     #define int_p_NULL NULL
     38 #endif
     39 
     40 #ifndef png_flush_ptr_NULL
     41     #define png_flush_ptr_NULL NULL
     42 #endif
     43 
     44 ///////////////////////////////////////////////////////////////////////////////
     45 // Callback functions
     46 ///////////////////////////////////////////////////////////////////////////////
     47 
     48 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
     49     SkCodecPrintf("------ png error %s\n", msg);
     50     longjmp(png_jmpbuf(png_ptr), 1);
     51 }
     52 
     53 void sk_warning_fn(png_structp, png_const_charp msg) {
     54     SkCodecPrintf("----- png warning %s\n", msg);
     55 }
     56 
     57 static void sk_read_fn(png_structp png_ptr, png_bytep data,
     58                        png_size_t length) {
     59     SkStream* stream = static_cast<SkStream*>(png_get_io_ptr(png_ptr));
     60     const size_t bytes = stream->read(data, length);
     61     if (bytes != length) {
     62         // FIXME: We want to report the fact that the stream was truncated.
     63         // One way to do that might be to pass a enum to longjmp so setjmp can
     64         // specify the failure.
     65         png_error(png_ptr, "Read Error!");
     66     }
     67 }
     68 
     69 ///////////////////////////////////////////////////////////////////////////////
     70 // Helpers
     71 ///////////////////////////////////////////////////////////////////////////////
     72 
     73 class AutoCleanPng : public SkNoncopyable {
     74 public:
     75     AutoCleanPng(png_structp png_ptr)
     76         : fPng_ptr(png_ptr)
     77         , fInfo_ptr(NULL) {}
     78 
     79     ~AutoCleanPng() {
     80         // fInfo_ptr will never be non-NULL unless fPng_ptr is.
     81         if (fPng_ptr) {
     82             png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : NULL;
     83             png_destroy_read_struct(&fPng_ptr, info_pp, png_infopp_NULL);
     84         }
     85     }
     86 
     87     void setInfoPtr(png_infop info_ptr) {
     88         SkASSERT(NULL == fInfo_ptr);
     89         fInfo_ptr = info_ptr;
     90     }
     91 
     92     void detach() {
     93         fPng_ptr = NULL;
     94         fInfo_ptr = NULL;
     95     }
     96 
     97 private:
     98     png_structp     fPng_ptr;
     99     png_infop       fInfo_ptr;
    100 };
    101 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
    102 
    103 // call only if color_type is PALETTE. Returns true if the ctable has alpha
    104 static bool has_transparency_in_palette(png_structp png_ptr,
    105                                         png_infop info_ptr) {
    106     if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
    107         return false;
    108     }
    109 
    110     png_bytep trans;
    111     int num_trans;
    112     png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
    113     return num_trans > 0;
    114 }
    115 
    116 // Method for coverting to either an SkPMColor or a similarly packed
    117 // unpremultiplied color.
    118 typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
    119 
    120 // Note: SkColorTable claims to store SkPMColors, which is not necessarily
    121 // the case here.
    122 bool SkPngCodec::decodePalette(bool premultiply, int bitDepth, int* ctableCount) {
    123     int numPalette;
    124     png_colorp palette;
    125     png_bytep trans;
    126 
    127     if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) {
    128         return false;
    129     }
    130 
    131     // Note: These are not necessarily SkPMColors
    132     SkPMColor colorStorage[256];    // worst-case storage
    133     SkPMColor* colorPtr = colorStorage;
    134 
    135     int numTrans;
    136     if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
    137         png_get_tRNS(fPng_ptr, fInfo_ptr, &trans, &numTrans, NULL);
    138     } else {
    139         numTrans = 0;
    140     }
    141 
    142     // check for bad images that might make us crash
    143     if (numTrans > numPalette) {
    144         numTrans = numPalette;
    145     }
    146 
    147     int index = 0;
    148     int transLessThanFF = 0;
    149 
    150     // Choose which function to use to create the color table. If the final destination's
    151     // colortype is unpremultiplied, the color table will store unpremultiplied colors.
    152     PackColorProc proc;
    153     if (premultiply) {
    154         proc = &SkPreMultiplyARGB;
    155     } else {
    156         proc = &SkPackARGB32NoCheck;
    157     }
    158     for (; index < numTrans; index++) {
    159         transLessThanFF |= (int)*trans - 0xFF;
    160         *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue);
    161         palette++;
    162     }
    163 
    164     fReallyHasAlpha = transLessThanFF < 0;
    165 
    166     for (; index < numPalette; index++) {
    167         *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
    168         palette++;
    169     }
    170 
    171     /*  BUGGY IMAGE WORKAROUND
    172         Invalid images could contain pixel values that are greater than the number of palette
    173         entries. Since we use pixel values as indices into the palette this could result in reading
    174         beyond the end of the palette which could leak the contents of uninitialized memory. To
    175         ensure this doesn't happen, we grow the colortable to the maximum size that can be
    176         addressed by the bitdepth of the image and fill it with the last palette color or black if
    177         the palette is empty (really broken image).
    178     */
    179     int colorCount = SkTMax(numPalette, 1 << SkTMin(bitDepth, 8));
    180     SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0);
    181     for (; index < colorCount; index++) {
    182         *colorPtr++ = lastColor;
    183     }
    184 
    185     // Set the new color count
    186     if (ctableCount != NULL) {
    187         *ctableCount = colorCount;
    188     }
    189 
    190     fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorStorage, colorCount)));
    191     return true;
    192 }
    193 
    194 ///////////////////////////////////////////////////////////////////////////////
    195 // Creation
    196 ///////////////////////////////////////////////////////////////////////////////
    197 
    198 #define PNG_BYTES_TO_CHECK 4
    199 
    200 bool SkPngCodec::IsPng(SkStream* stream) {
    201     char buf[PNG_BYTES_TO_CHECK];
    202     if (stream->read(buf, PNG_BYTES_TO_CHECK) != PNG_BYTES_TO_CHECK) {
    203         return false;
    204     }
    205     if (png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
    206         return false;
    207     }
    208     return true;
    209 }
    210 
    211 // Reads the header, and initializes the passed in fields, if not NULL (except
    212 // stream, which is passed to the read function).
    213 // Returns true on success, in which case the caller is responsible for calling
    214 // png_destroy_read_struct. If it returns false, the passed in fields (except
    215 // stream) are unchanged.
    216 static bool read_header(SkStream* stream, png_structp* png_ptrp,
    217                         png_infop* info_ptrp, SkImageInfo* imageInfo) {
    218     // The image is known to be a PNG. Decode enough to know the SkImageInfo.
    219     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
    220                                                  sk_error_fn, sk_warning_fn);
    221     if (!png_ptr) {
    222         return false;
    223     }
    224 
    225     AutoCleanPng autoClean(png_ptr);
    226 
    227     png_infop info_ptr = png_create_info_struct(png_ptr);
    228     if (info_ptr == NULL) {
    229         return false;
    230     }
    231 
    232     autoClean.setInfoPtr(info_ptr);
    233 
    234     // FIXME: Could we use the return value of setjmp to specify the type of
    235     // error?
    236     if (setjmp(png_jmpbuf(png_ptr))) {
    237         return false;
    238     }
    239 
    240     png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn);
    241 
    242     // FIXME: This is where the old code hooks up the Peeker. Does it need to
    243     // be set this early? (i.e. where are the user chunks? early in the stream,
    244     // potentially?)
    245     // If it does, we need to figure out a way to set it here.
    246 
    247     // The call to png_read_info() gives us all of the information from the
    248     // PNG file before the first IDAT (image data chunk).
    249     png_read_info(png_ptr, info_ptr);
    250     png_uint_32 origWidth, origHeight;
    251     int bitDepth, colorType;
    252     png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
    253                  &colorType, int_p_NULL, int_p_NULL, int_p_NULL);
    254 
    255     // sanity check for size
    256     {
    257         int64_t size = sk_64_mul(origWidth, origHeight);
    258         // now check that if we are 4-bytes per pixel, we also don't overflow
    259         if (size < 0 || size > (0x7FFFFFFF >> 2)) {
    260             return false;
    261         }
    262     }
    263 
    264     // Tell libpng to strip 16 bit/color files down to 8 bits/color
    265     if (bitDepth == 16) {
    266         png_set_strip_16(png_ptr);
    267     }
    268 #ifdef PNG_READ_PACK_SUPPORTED
    269     // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
    270     // byte into separate bytes (useful for paletted and grayscale images).
    271     if (bitDepth < 8) {
    272         png_set_packing(png_ptr);
    273     }
    274 #endif
    275     // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
    276     if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
    277         png_set_expand_gray_1_2_4_to_8(png_ptr);
    278     }
    279 
    280 
    281     // Now determine the default SkColorType and SkAlphaType.
    282     SkColorType skColorType;
    283     SkAlphaType skAlphaType;
    284     switch (colorType) {
    285         case PNG_COLOR_TYPE_PALETTE:
    286             skColorType = kIndex_8_SkColorType;
    287             skAlphaType = has_transparency_in_palette(png_ptr, info_ptr) ?
    288                     kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
    289             break;
    290         case PNG_COLOR_TYPE_GRAY:
    291             if (false) {
    292                 // FIXME: Is this the wrong default behavior? This means if the
    293                 // caller supplies the info we gave them, they'll get Alpha 8.
    294                 skColorType = kAlpha_8_SkColorType;
    295                 // FIXME: Strangely, the canonical type for Alpha 8 is Premul.
    296                 skAlphaType = kPremul_SkAlphaType;
    297             } else {
    298                 skColorType = kN32_SkColorType;
    299                 skAlphaType = kOpaque_SkAlphaType;
    300             }
    301             break;
    302         default:
    303             // Note: This *almost* mimics the code in SkImageDecoder_libpng.
    304             // has_transparency_in_palette makes an additional check - whether
    305             // numTrans is greater than 0. Why does the other code not make that
    306             // check?
    307             if (has_transparency_in_palette(png_ptr, info_ptr)
    308                 || PNG_COLOR_TYPE_RGB_ALPHA == colorType
    309                 || PNG_COLOR_TYPE_GRAY_ALPHA == colorType)
    310             {
    311                 skAlphaType = kUnpremul_SkAlphaType;
    312             } else {
    313                 skAlphaType = kOpaque_SkAlphaType;
    314             }
    315             skColorType = kN32_SkColorType;
    316             break;
    317     }
    318 
    319     {
    320         // FIXME: Again, this block needs to go into onGetPixels.
    321         bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType && skColorType != kAlpha_8_SkColorType;
    322 
    323         // Unless the user is requesting A8, convert a grayscale image into RGB.
    324         // GRAY_ALPHA will always be converted to RGB
    325         if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
    326             png_set_gray_to_rgb(png_ptr);
    327         }
    328 
    329         // Add filler (or alpha) byte (after each RGB triplet) if necessary.
    330         // FIXME: It seems like we could just use RGB as the SrcConfig here.
    331         if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) {
    332             png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
    333         }
    334     }
    335 
    336     // FIXME: Also need to check for sRGB (skbug.com/3471).
    337 
    338     if (imageInfo) {
    339         *imageInfo = SkImageInfo::Make(origWidth, origHeight, skColorType,
    340                                        skAlphaType);
    341     }
    342     autoClean.detach();
    343     if (png_ptrp) {
    344         *png_ptrp = png_ptr;
    345     }
    346     if (info_ptrp) {
    347         *info_ptrp = info_ptr;
    348     }
    349     return true;
    350 }
    351 
    352 SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
    353     SkAutoTDelete<SkStream> streamDeleter(stream);
    354     png_structp png_ptr;
    355     png_infop info_ptr;
    356     SkImageInfo imageInfo;
    357     if (read_header(stream, &png_ptr, &info_ptr, &imageInfo)) {
    358         return SkNEW_ARGS(SkPngCodec, (imageInfo, streamDeleter.detach(), png_ptr, info_ptr));
    359     }
    360     return NULL;
    361 }
    362 
    363 #define INVALID_NUMBER_PASSES -1
    364 SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream,
    365                        png_structp png_ptr, png_infop info_ptr)
    366     : INHERITED(info, stream)
    367     , fPng_ptr(png_ptr)
    368     , fInfo_ptr(info_ptr)
    369     , fSrcConfig(SkSwizzler::kUnknown)
    370     , fNumberPasses(INVALID_NUMBER_PASSES)
    371     , fReallyHasAlpha(false)
    372 {}
    373 
    374 SkPngCodec::~SkPngCodec() {
    375     this->destroyReadStruct();
    376 }
    377 
    378 void SkPngCodec::destroyReadStruct() {
    379     if (fPng_ptr) {
    380         // We will never have a NULL fInfo_ptr with a non-NULL fPng_ptr
    381         SkASSERT(fInfo_ptr);
    382         png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
    383         fPng_ptr = NULL;
    384         fInfo_ptr = NULL;
    385     }
    386 }
    387 
    388 ///////////////////////////////////////////////////////////////////////////////
    389 // Getting the pixels
    390 ///////////////////////////////////////////////////////////////////////////////
    391 
    392 static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
    393     // TODO: Support other conversions
    394     if (dst.profileType() != src.profileType()) {
    395         return false;
    396     }
    397 
    398     // Check for supported alpha types
    399     if (src.alphaType() != dst.alphaType()) {
    400         if (kOpaque_SkAlphaType == src.alphaType()) {
    401             // If the source is opaque, we must decode to opaque
    402             return false;
    403         }
    404 
    405         // The source is not opaque
    406         switch (dst.alphaType()) {
    407             case kPremul_SkAlphaType:
    408             case kUnpremul_SkAlphaType:
    409                 // The source is not opaque, so either of these is okay
    410                 break;
    411             default:
    412                 // We cannot decode a non-opaque image to opaque (or unknown)
    413                 return false;
    414         }
    415     }
    416 
    417     // Check for supported color types
    418     switch (dst.colorType()) {
    419         // Allow output to kN32 from any type of input
    420         case kN32_SkColorType:
    421             return true;
    422         default:
    423             return dst.colorType() == src.colorType();
    424     }
    425 }
    426 
    427 SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
    428                                                void* dst, size_t rowBytes,
    429                                                const Options& options,
    430                                                SkPMColor ctable[],
    431                                                int* ctableCount) {
    432     // FIXME: Could we use the return value of setjmp to specify the type of
    433     // error?
    434     if (setjmp(png_jmpbuf(fPng_ptr))) {
    435         SkCodecPrintf("setjmp long jump!\n");
    436         return kInvalidInput;
    437     }
    438 
    439     // FIXME: We already retrieved this information. Store it in SkPngCodec?
    440     png_uint_32 origWidth, origHeight;
    441     int bitDepth, pngColorType, interlaceType;
    442     png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth,
    443                  &pngColorType, &interlaceType, int_p_NULL, int_p_NULL);
    444 
    445     fNumberPasses = (interlaceType != PNG_INTERLACE_NONE) ?
    446             png_set_interlace_handling(fPng_ptr) : 1;
    447 
    448     // Set to the default before calling decodePalette, which may change it.
    449     fReallyHasAlpha = false;
    450     if (PNG_COLOR_TYPE_PALETTE == pngColorType) {
    451         fSrcConfig = SkSwizzler::kIndex;
    452         if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(), bitDepth,
    453                 ctableCount)) {
    454             return kInvalidInput;
    455         }
    456     } else if (kAlpha_8_SkColorType == requestedInfo.colorType()) {
    457         // Note: we check the destination, since otherwise we would have
    458         // told png to upscale.
    459         SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
    460         fSrcConfig = SkSwizzler::kGray;
    461     } else if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
    462         fSrcConfig = SkSwizzler::kRGBX;
    463     } else {
    464         fSrcConfig = SkSwizzler::kRGBA;
    465     }
    466 
    467     // Copy the color table to the client if they request kIndex8 mode
    468     copy_color_table(requestedInfo, fColorTable, ctable, ctableCount);
    469 
    470     // Create the swizzler.  SkPngCodec retains ownership of the color table.
    471     const SkPMColor* colors = fColorTable ? fColorTable->readColors() : NULL;
    472     fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo,
    473             dst, rowBytes, options.fZeroInitialized));
    474     if (!fSwizzler) {
    475         // FIXME: CreateSwizzler could fail for another reason.
    476         return kUnimplemented;
    477     }
    478 
    479     // FIXME: Here is where we should likely insert some of the modifications
    480     // made in the factory.
    481     png_read_update_info(fPng_ptr, fInfo_ptr);
    482 
    483     return kSuccess;
    484 }
    485 
    486 bool SkPngCodec::handleRewind() {
    487     switch (this->rewindIfNeeded()) {
    488         case kNoRewindNecessary_RewindState:
    489             return true;
    490         case kCouldNotRewind_RewindState:
    491             return false;
    492         case kRewound_RewindState: {
    493             // This sets fPng_ptr and fInfo_ptr to NULL. If read_header
    494             // succeeds, they will be repopulated, and if it fails, they will
    495             // remain NULL. Any future accesses to fPng_ptr and fInfo_ptr will
    496             // come through this function which will rewind and again attempt
    497             // to reinitialize them.
    498             this->destroyReadStruct();
    499             png_structp png_ptr;
    500             png_infop info_ptr;
    501             if (read_header(this->stream(), &png_ptr, &info_ptr, NULL)) {
    502                 fPng_ptr = png_ptr;
    503                 fInfo_ptr = info_ptr;
    504                 return true;
    505             }
    506             return false;
    507         }
    508         default:
    509             SkASSERT(false);
    510             return false;
    511     }
    512 }
    513 
    514 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
    515                                         size_t rowBytes, const Options& options,
    516                                         SkPMColor ctable[], int* ctableCount) {
    517     if (!this->handleRewind()) {
    518         return kCouldNotRewind;
    519     }
    520     if (requestedInfo.dimensions() != this->getInfo().dimensions()) {
    521         return kInvalidScale;
    522     }
    523     if (!conversion_possible(requestedInfo, this->getInfo())) {
    524         return kInvalidConversion;
    525     }
    526 
    527     // Note that ctable and ctableCount may be modified if there is a color table
    528     const Result result = this->initializeSwizzler(requestedInfo, dst, rowBytes,
    529                                                    options, ctable, ctableCount);
    530 
    531     if (result != kSuccess) {
    532         return result;
    533     }
    534 
    535     // FIXME: Could we use the return value of setjmp to specify the type of
    536     // error?
    537     if (setjmp(png_jmpbuf(fPng_ptr))) {
    538         SkCodecPrintf("setjmp long jump!\n");
    539         return kInvalidInput;
    540     }
    541 
    542     SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
    543     SkAutoMalloc storage;
    544     if (fNumberPasses > 1) {
    545         const int width = requestedInfo.width();
    546         const int height = requestedInfo.height();
    547         const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig);
    548         const size_t rowBytes = width * bpp;
    549 
    550         storage.reset(width * height * bpp);
    551         uint8_t* const base = static_cast<uint8_t*>(storage.get());
    552 
    553         for (int i = 0; i < fNumberPasses; i++) {
    554             uint8_t* row = base;
    555             for (int y = 0; y < height; y++) {
    556                 uint8_t* bmRow = row;
    557                 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1);
    558                 row += rowBytes;
    559             }
    560         }
    561 
    562         // Now swizzle it.
    563         uint8_t* row = base;
    564         for (int y = 0; y < height; y++) {
    565             fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(row));
    566             row += rowBytes;
    567         }
    568     } else {
    569         storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig));
    570         uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
    571         for (int y = 0; y < requestedInfo.height(); y++) {
    572             png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
    573             fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(srcRow));
    574         }
    575     }
    576 
    577     // FIXME: do we need substituteTranspColor? Note that we cannot do it for
    578     // scanline decoding, but we could do it here. Alternatively, we could do
    579     // it as we go, instead of in post-processing like SkPNGImageDecoder.
    580 
    581     this->finish();
    582     return kSuccess;
    583 }
    584 
    585 void SkPngCodec::finish() {
    586     if (setjmp(png_jmpbuf(fPng_ptr))) {
    587         // We've already read all the scanlines. This is a success.
    588         return;
    589     }
    590     /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
    591     png_read_end(fPng_ptr, fInfo_ptr);
    592 }
    593 
    594 class SkPngScanlineDecoder : public SkScanlineDecoder {
    595 public:
    596     SkPngScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec)
    597         : INHERITED(dstInfo)
    598         , fCodec(codec)
    599         , fHasAlpha(false)
    600     {
    601         fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig));
    602         fSrcRow = static_cast<uint8_t*>(fStorage.get());
    603     }
    604 
    605     SkImageGenerator::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
    606         if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
    607             SkCodecPrintf("setjmp long jump!\n");
    608             return SkImageGenerator::kInvalidInput;
    609         }
    610 
    611         for (int i = 0; i < count; i++) {
    612             png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
    613             fCodec->fSwizzler->setDstRow(dst);
    614             fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(fSrcRow));
    615             dst = SkTAddOffset<void>(dst, rowBytes);
    616         }
    617         return SkImageGenerator::kSuccess;
    618     }
    619 
    620     SkImageGenerator::Result onSkipScanlines(int count) override {
    621         // FIXME: Could we use the return value of setjmp to specify the type of
    622         // error?
    623         if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
    624             SkCodecPrintf("setjmp long jump!\n");
    625             return SkImageGenerator::kInvalidInput;
    626         }
    627 
    628         png_read_rows(fCodec->fPng_ptr, png_bytepp_NULL, png_bytepp_NULL, count);
    629         return SkImageGenerator::kSuccess;
    630     }
    631 
    632     void onFinish() override {
    633         fCodec->finish();
    634     }
    635 
    636     bool onReallyHasAlpha() const override { return fHasAlpha; }
    637 
    638 private:
    639     SkPngCodec*         fCodec;     // Unowned.
    640     bool                fHasAlpha;
    641     SkAutoMalloc        fStorage;
    642     uint8_t*            fSrcRow;
    643 
    644     typedef SkScanlineDecoder INHERITED;
    645 };
    646 
    647 SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo,
    648         const Options& options, SkPMColor ctable[], int* ctableCount) {
    649     if (!this->handleRewind()) {
    650         return NULL;
    651     }
    652 
    653     // Check to see if scaling was requested.
    654     if (dstInfo.dimensions() != this->getInfo().dimensions()) {
    655         return NULL;
    656     }
    657 
    658     if (!conversion_possible(dstInfo, this->getInfo())) {
    659         SkCodecPrintf("no conversion possible\n");
    660         return NULL;
    661     }
    662 
    663     // Note: We set dst to NULL since we do not know it yet. rowBytes is not needed,
    664     // since we'll be manually updating the dstRow, but the SkSwizzler requires it to
    665     // be at least dstInfo.minRowBytes.
    666     if (this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options, ctable,
    667             ctableCount) != kSuccess) {
    668         SkCodecPrintf("failed to initialize the swizzler.\n");
    669         return NULL;
    670     }
    671 
    672     SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
    673     if (fNumberPasses > 1) {
    674         // We cannot efficiently do scanline decoding.
    675         return NULL;
    676     }
    677 
    678     return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, this));
    679 }
    680 
    681