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 "SkCodecPriv.h"
      9 #include "SkColorPriv.h"
     10 #include "SkColorTable.h"
     11 #include "SkGifCodec.h"
     12 #include "SkStream.h"
     13 #include "SkSwizzler.h"
     14 #include "SkUtils.h"
     15 
     16 #include "gif_lib.h"
     17 
     18 /*
     19  * Checks the start of the stream to see if the image is a gif
     20  */
     21 bool SkGifCodec::IsGif(const void* buf, size_t bytesRead) {
     22     if (bytesRead >= GIF_STAMP_LEN) {
     23         if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
     24             memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
     25             memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0)
     26         {
     27             return true;
     28         }
     29     }
     30     return false;
     31 }
     32 
     33 /*
     34  * Error function
     35  */
     36 static SkCodec::Result gif_error(const char* msg, SkCodec::Result result = SkCodec::kInvalidInput) {
     37     SkCodecPrintf("Gif Error: %s\n", msg);
     38     return result;
     39 }
     40 
     41 
     42 /*
     43  * Read function that will be passed to gif_lib
     44  */
     45 static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, int32_t size) {
     46     SkStream* stream = (SkStream*) fileType->UserData;
     47     return (int32_t) stream->read(out, size);
     48 }
     49 
     50 /*
     51  * Open the gif file
     52  */
     53 static GifFileType* open_gif(SkStream* stream) {
     54 #if GIFLIB_MAJOR < 5
     55     return DGifOpen(stream, read_bytes_callback);
     56 #else
     57     return DGifOpen(stream, read_bytes_callback, nullptr);
     58 #endif
     59 }
     60 
     61 /*
     62  * Check if a there is an index of the color table for a transparent pixel
     63  */
     64 static uint32_t find_trans_index(const SavedImage& image) {
     65     // If there is a transparent index specified, it will be contained in an
     66     // extension block.  We will loop through extension blocks in reverse order
     67     // to check the most recent extension blocks first.
     68     for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) {
     69         // Get an extension block
     70         const ExtensionBlock& extBlock = image.ExtensionBlocks[i];
     71 
     72         // Specifically, we need to check for a graphics control extension,
     73         // which may contain transparency information.  Also, note that a valid
     74         // graphics control extension is always four bytes.  The fourth byte
     75         // is the transparent index (if it exists), so we need at least four
     76         // bytes.
     77         if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && extBlock.ByteCount >= 4) {
     78             // Check the transparent color flag which indicates whether a
     79             // transparent index exists.  It is the least significant bit of
     80             // the first byte of the extension block.
     81             if (1 == (extBlock.Bytes[0] & 1)) {
     82                 // Use uint32_t to prevent sign extending
     83                 return extBlock.Bytes[3];
     84             }
     85 
     86             // There should only be one graphics control extension for the image frame
     87             break;
     88         }
     89     }
     90 
     91     // Use maximum unsigned int (surely an invalid index) to indicate that a valid
     92     // index was not found.
     93     return SK_MaxU32;
     94 }
     95 
     96 inline uint32_t ceil_div(uint32_t a, uint32_t b) {
     97     return (a + b - 1) / b;
     98 }
     99 
    100 /*
    101  * Gets the output row corresponding to the encoded row for interlaced gifs
    102  */
    103 inline uint32_t get_output_row_interlaced(uint32_t encodedRow, uint32_t height) {
    104     SkASSERT(encodedRow < height);
    105     // First pass
    106     if (encodedRow * 8 < height) {
    107         return encodedRow * 8;
    108     }
    109     // Second pass
    110     if (encodedRow * 4 < height) {
    111         return 4 + 8 * (encodedRow - ceil_div(height, 8));
    112     }
    113     // Third pass
    114     if (encodedRow * 2 < height) {
    115         return 2 + 4 * (encodedRow - ceil_div(height, 4));
    116     }
    117     // Fourth pass
    118     return 1 + 2 * (encodedRow - ceil_div(height, 2));
    119 }
    120 
    121 /*
    122  * This function cleans up the gif object after the decode completes
    123  * It is used in a SkAutoTCallIProc template
    124  */
    125 void SkGifCodec::CloseGif(GifFileType* gif) {
    126 #if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
    127     DGifCloseFile(gif);
    128 #else
    129     DGifCloseFile(gif, nullptr);
    130 #endif
    131 }
    132 
    133 /*
    134  * This function free extension data that has been saved to assist the image
    135  * decoder
    136  */
    137 void SkGifCodec::FreeExtension(SavedImage* image) {
    138     if (NULL != image->ExtensionBlocks) {
    139 #if GIFLIB_MAJOR < 5
    140         FreeExtension(image);
    141 #else
    142         GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks);
    143 #endif
    144     }
    145 }
    146 
    147 /*
    148  * Read enough of the stream to initialize the SkGifCodec.
    149  * Returns a bool representing success or failure.
    150  *
    151  * @param codecOut
    152  * If it returned true, and codecOut was not nullptr,
    153  * codecOut will be set to a new SkGifCodec.
    154  *
    155  * @param gifOut
    156  * If it returned true, and codecOut was nullptr,
    157  * gifOut must be non-nullptr and gifOut will be set to a new
    158  * GifFileType pointer.
    159  *
    160  * @param stream
    161  * Deleted on failure.
    162  * codecOut will take ownership of it in the case where we created a codec.
    163  * Ownership is unchanged when we returned a gifOut.
    164  *
    165  */
    166 bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut) {
    167     SkAutoTDelete<SkStream> streamDeleter(stream);
    168 
    169     // Read gif header, logical screen descriptor, and global color table
    170     SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream));
    171 
    172     if (nullptr == gif) {
    173         gif_error("DGifOpen failed.\n");
    174         return false;
    175     }
    176 
    177     // Read through gif extensions to get to the image data.  Set the
    178     // transparent index based on the extension data.
    179     uint32_t transIndex;
    180     SkCodec::Result result = ReadUpToFirstImage(gif, &transIndex);
    181     if (kSuccess != result){
    182         return false;
    183     }
    184 
    185     // Read the image descriptor
    186     if (GIF_ERROR == DGifGetImageDesc(gif)) {
    187         return false;
    188     }
    189     // If reading the image descriptor is successful, the image count will be
    190     // incremented.
    191     SkASSERT(gif->ImageCount >= 1);
    192 
    193     if (nullptr != codecOut) {
    194         SkISize size;
    195         SkIRect frameRect;
    196         if (!GetDimensions(gif, &size, &frameRect)) {
    197             gif_error("Invalid gif size.\n");
    198             return false;
    199         }
    200         bool frameIsSubset = (size != frameRect.size());
    201 
    202         // Determine the recommended alpha type.  The transIndex might be valid if it less
    203         // than 256.  We are not certain that the index is valid until we process the color
    204         // table, since some gifs have color tables with less than 256 colors.  If
    205         // there might be a valid transparent index, we must indicate that the image has
    206         // alpha.
    207         // In the case where we must support alpha, we have the option to set the
    208         // suggested alpha type to kPremul or kUnpremul.  Both are valid since the alpha
    209         // component will always be 0xFF or the entire 32-bit pixel will be set to zero.
    210         // We prefer kPremul because we support kPremul, and it is more efficient to use
    211         // kPremul directly even when kUnpremul is supported.
    212         SkAlphaType alphaType = (transIndex < 256) ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
    213 
    214         // Return the codec
    215         // kIndex is the most natural color type for gifs, so we set this as
    216         // the default.
    217         SkImageInfo imageInfo = SkImageInfo::Make(size.width(), size.height(), kIndex_8_SkColorType,
    218                 alphaType);
    219         *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach(), transIndex,
    220                 frameRect, frameIsSubset);
    221     } else {
    222         SkASSERT(nullptr != gifOut);
    223         streamDeleter.detach();
    224         *gifOut = gif.detach();
    225     }
    226     return true;
    227 }
    228 
    229 /*
    230  * Assumes IsGif was called and returned true
    231  * Creates a gif decoder
    232  * Reads enough of the stream to determine the image format
    233  */
    234 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) {
    235     SkCodec* codec = nullptr;
    236     if (ReadHeader(stream, &codec, nullptr)) {
    237         return codec;
    238     }
    239     return nullptr;
    240 }
    241 
    242 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType* gif,
    243         uint32_t transIndex, const SkIRect& frameRect, bool frameIsSubset)
    244     : INHERITED(srcInfo, stream)
    245     , fGif(gif)
    246     , fSrcBuffer(new uint8_t[this->getInfo().width()])
    247     , fFrameRect(frameRect)
    248     // If it is valid, fTransIndex will be used to set fFillIndex.  We don't know if
    249     // fTransIndex is valid until we process the color table, since fTransIndex may
    250     // be greater than the size of the color table.
    251     , fTransIndex(transIndex)
    252     // Default fFillIndex is 0.  We will overwrite this if fTransIndex is valid, or if
    253     // there is a valid background color.
    254     , fFillIndex(0)
    255     , fFrameIsSubset(frameIsSubset)
    256     , fSwizzler(NULL)
    257     , fColorTable(NULL)
    258 {}
    259 
    260 bool SkGifCodec::onRewind() {
    261     GifFileType* gifOut = nullptr;
    262     if (!ReadHeader(this->stream(), nullptr, &gifOut)) {
    263         return false;
    264     }
    265 
    266     SkASSERT(nullptr != gifOut);
    267     fGif.reset(gifOut);
    268     return true;
    269 }
    270 
    271 SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex) {
    272     // Use this as a container to hold information about any gif extension
    273     // blocks.  This generally stores transparency and animation instructions.
    274     SavedImage saveExt;
    275     SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt);
    276     saveExt.ExtensionBlocks = nullptr;
    277     saveExt.ExtensionBlockCount = 0;
    278     GifByteType* extData;
    279     int32_t extFunction;
    280 
    281     // We will loop over components of gif images until we find an image.  Once
    282     // we find an image, we will decode and return it.  While many gif files
    283     // contain more than one image, we will simply decode the first image.
    284     GifRecordType recordType;
    285     do {
    286         // Get the current record type
    287         if (GIF_ERROR == DGifGetRecordType(gif, &recordType)) {
    288             return gif_error("DGifGetRecordType failed.\n", kInvalidInput);
    289         }
    290         switch (recordType) {
    291             case IMAGE_DESC_RECORD_TYPE: {
    292                 *transIndex = find_trans_index(saveExt);
    293 
    294                 // FIXME: Gif files may have multiple images stored in a single
    295                 //        file.  This is most commonly used to enable
    296                 //        animations.  Since we are leaving animated gifs as a
    297                 //        TODO, we will return kSuccess after decoding the
    298                 //        first image in the file.  This is the same behavior
    299                 //        as SkImageDecoder_libgif.
    300                 //
    301                 //        Most times this works pretty well, but sometimes it
    302                 //        doesn't.  For example, I have an animated test image
    303                 //        where the first image in the file is 1x1, but the
    304                 //        subsequent images are meaningful.  This currently
    305                 //        displays the 1x1 image, which is not ideal.  Right
    306                 //        now I am leaving this as an issue that will be
    307                 //        addressed when we implement animated gifs.
    308                 //
    309                 //        It is also possible (not explicitly disallowed in the
    310                 //        specification) that gif files provide multiple
    311                 //        images in a single file that are all meant to be
    312                 //        displayed in the same frame together.  I will
    313                 //        currently leave this unimplemented until I find a
    314                 //        test case that expects this behavior.
    315                 return kSuccess;
    316             }
    317             // Extensions are used to specify special properties of the image
    318             // such as transparency or animation.
    319             case EXTENSION_RECORD_TYPE:
    320                 // Read extension data
    321                 if (GIF_ERROR == DGifGetExtension(gif, &extFunction, &extData)) {
    322                     return gif_error("Could not get extension.\n", kIncompleteInput);
    323                 }
    324 
    325                 // Create an extension block with our data
    326                 while (nullptr != extData) {
    327                     // Add a single block
    328 
    329 #if GIFLIB_MAJOR < 5
    330                     if (AddExtensionBlock(&saveExt, extData[0],
    331                                           &extData[1]) == GIF_ERROR) {
    332 #else
    333                     if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBlockCount,
    334                                                           &saveExt.ExtensionBlocks,
    335                                                           extFunction, extData[0], &extData[1])) {
    336 #endif
    337                         return gif_error("Could not add extension block.\n", kIncompleteInput);
    338                     }
    339                     // Move to the next block
    340                     if (GIF_ERROR == DGifGetExtensionNext(gif, &extData)) {
    341                         return gif_error("Could not get next extension.\n", kIncompleteInput);
    342                     }
    343                 }
    344                 break;
    345 
    346             // Signals the end of the gif file
    347             case TERMINATE_RECORD_TYPE:
    348                 break;
    349 
    350             default:
    351                 // DGifGetRecordType returns an error if the record type does
    352                 // not match one of the above cases.  This should not be
    353                 // reached.
    354                 SkASSERT(false);
    355                 break;
    356         }
    357     } while (TERMINATE_RECORD_TYPE != recordType);
    358 
    359     return gif_error("Could not find any images to decode in gif file.\n", kInvalidInput);
    360 }
    361 
    362 bool SkGifCodec::GetDimensions(GifFileType* gif, SkISize* size, SkIRect* frameRect) {
    363     // Get the encoded dimension values
    364     SavedImage* image = &gif->SavedImages[gif->ImageCount - 1];
    365     const GifImageDesc& desc = image->ImageDesc;
    366     int frameLeft = desc.Left;
    367     int frameTop = desc.Top;
    368     int frameWidth = desc.Width;
    369     int frameHeight = desc.Height;
    370     int width = gif->SWidth;
    371     int height = gif->SHeight;
    372 
    373     // Ensure that the decode dimensions are large enough to contain the frame
    374     width = SkTMax(width, frameWidth + frameLeft);
    375     height = SkTMax(height, frameHeight + frameTop);
    376 
    377     // All of these dimensions should be positive, as they are encoded as unsigned 16-bit integers.
    378     // It is unclear why giflib casts them to ints.  We will go ahead and check that they are
    379     // in fact positive.
    380     if (frameLeft < 0 || frameTop < 0 || frameWidth < 0 || frameHeight < 0 || width <= 0 ||
    381             height <= 0) {
    382         return false;
    383     }
    384 
    385     frameRect->setXYWH(frameLeft, frameTop, frameWidth, frameHeight);
    386     size->set(width, height);
    387     return true;
    388 }
    389 
    390 void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr,
    391         int* inputColorCount) {
    392     // Set up our own color table
    393     const uint32_t maxColors = 256;
    394     SkPMColor colorPtr[256];
    395     if (NULL != inputColorCount) {
    396         // We set the number of colors to maxColors in order to ensure
    397         // safe memory accesses.  Otherwise, an invalid pixel could
    398         // access memory outside of our color table array.
    399         *inputColorCount = maxColors;
    400     }
    401 
    402     // Get local color table
    403     ColorMapObject* colorMap = fGif->Image.ColorMap;
    404     // If there is no local color table, use the global color table
    405     if (NULL == colorMap) {
    406         colorMap = fGif->SColorMap;
    407     }
    408 
    409     uint32_t colorCount = 0;
    410     if (NULL != colorMap) {
    411         colorCount = colorMap->ColorCount;
    412         // giflib guarantees these properties
    413         SkASSERT(colorCount == (unsigned) (1 << (colorMap->BitsPerPixel)));
    414         SkASSERT(colorCount <= 256);
    415         for (uint32_t i = 0; i < colorCount; i++) {
    416             colorPtr[i] = SkPackARGB32(0xFF, colorMap->Colors[i].Red,
    417                     colorMap->Colors[i].Green, colorMap->Colors[i].Blue);
    418         }
    419     }
    420 
    421     // Fill in the color table for indices greater than color count.
    422     // This allows for predictable, safe behavior.
    423     if (colorCount > 0) {
    424         // Gifs have the option to specify the color at a single index of the color
    425         // table as transparent.  If the transparent index is greater than the
    426         // colorCount, we know that there is no valid transparent color in the color
    427         // table.  If there is not valid transparent index, we will try to use the
    428         // backgroundIndex as the fill index.  If the backgroundIndex is also not
    429         // valid, we will let fFillIndex default to 0 (it is set to zero in the
    430         // constructor).  This behavior is not specified but matches
    431         // SkImageDecoder_libgif.
    432         uint32_t backgroundIndex = fGif->SBackGroundColor;
    433         if (fTransIndex < colorCount) {
    434             colorPtr[fTransIndex] = SK_ColorTRANSPARENT;
    435             fFillIndex = fTransIndex;
    436         } else if (backgroundIndex < colorCount) {
    437             fFillIndex = backgroundIndex;
    438         }
    439 
    440         for (uint32_t i = colorCount; i < maxColors; i++) {
    441             colorPtr[i] = colorPtr[fFillIndex];
    442         }
    443     } else {
    444         sk_memset32(colorPtr, 0xFF000000, maxColors);
    445     }
    446 
    447     fColorTable.reset(new SkColorTable(colorPtr, maxColors));
    448     copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount);
    449 }
    450 
    451 SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr,
    452         int* inputColorCount, const Options& opts) {
    453     // Check for valid input parameters
    454     if (!conversion_possible(dstInfo, this->getInfo())) {
    455         return gif_error("Cannot convert input type to output type.\n",
    456                 kInvalidConversion);
    457     }
    458 
    459     // Initialize color table and copy to the client if necessary
    460     this->initializeColorTable(dstInfo, inputColorPtr, inputColorCount);
    461 
    462     this->initializeSwizzler(dstInfo, opts);
    463     return kSuccess;
    464 }
    465 
    466 void SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) {
    467     const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
    468     const SkIRect* frameRect = fFrameIsSubset ? &fFrameRect : nullptr;
    469     fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex, colorPtr, dstInfo, opts,
    470             frameRect));
    471     SkASSERT(fSwizzler);
    472 }
    473 
    474 bool SkGifCodec::readRow() {
    475     return GIF_ERROR != DGifGetLine(fGif, fSrcBuffer.get(), fFrameRect.width());
    476 }
    477 
    478 /*
    479  * Initiates the gif decode
    480  */
    481 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
    482                                         void* dst, size_t dstRowBytes,
    483                                         const Options& opts,
    484                                         SkPMColor* inputColorPtr,
    485                                         int* inputColorCount,
    486                                         int* rowsDecoded) {
    487     Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, opts);
    488     if (kSuccess != result) {
    489         return result;
    490     }
    491 
    492     if (dstInfo.dimensions() != this->getInfo().dimensions()) {
    493         return gif_error("Scaling not supported.\n", kInvalidScale);
    494     }
    495 
    496     // Initialize the swizzler
    497     if (fFrameIsSubset) {
    498         // Fill the background
    499         SkSampler::Fill(dstInfo, dst, dstRowBytes, this->getFillValue(dstInfo.colorType()),
    500                 opts.fZeroInitialized);
    501     }
    502 
    503     // Iterate over rows of the input
    504     for (int y = fFrameRect.top(); y < fFrameRect.bottom(); y++) {
    505         if (!this->readRow()) {
    506             *rowsDecoded = y;
    507             return gif_error("Could not decode line.\n", kIncompleteInput);
    508         }
    509         void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * this->outputScanline(y));
    510         fSwizzler->swizzle(dstRow, fSrcBuffer.get());
    511     }
    512     return kSuccess;
    513 }
    514 
    515 // FIXME: This is similar to the implementation for bmp and png.  Can we share more code or
    516 //        possibly make this non-virtual?
    517 uint32_t SkGifCodec::onGetFillValue(SkColorType colorType) const {
    518     const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
    519     return get_color_table_fill_value(colorType, colorPtr, fFillIndex);
    520 }
    521 
    522 SkCodec::Result SkGifCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
    523         const SkCodec::Options& opts, SkPMColor inputColorPtr[], int* inputColorCount) {
    524     return this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, this->options());
    525 }
    526 
    527 void SkGifCodec::handleScanlineFrame(int count, int* rowsBeforeFrame, int* rowsInFrame) {
    528     if (fFrameIsSubset) {
    529         const int currRow = this->currScanline();
    530 
    531         // The number of rows that remain to be skipped before reaching rows that we
    532         // actually must decode into.
    533         // This must be at least zero.  We also make sure that it is less than or
    534         // equal to count, since we will skip at most count rows.
    535         *rowsBeforeFrame = SkTMin(count, SkTMax(0, fFrameRect.top() - currRow));
    536 
    537         // Rows left to decode once we reach the start of the frame.
    538         const int rowsLeft = count - *rowsBeforeFrame;
    539 
    540         // Count the number of that extend beyond the bottom of the frame.  We do not
    541         // need to decode into these rows.
    542         const int rowsAfterFrame = SkTMax(0, currRow + rowsLeft - fFrameRect.bottom());
    543 
    544         // Set the actual number of source rows that we need to decode.
    545         *rowsInFrame = rowsLeft - rowsAfterFrame;
    546     } else {
    547         *rowsBeforeFrame = 0;
    548         *rowsInFrame = count;
    549     }
    550 }
    551 
    552 int SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
    553     int rowsBeforeFrame;
    554     int rowsInFrame;
    555     this->handleScanlineFrame(count, &rowsBeforeFrame, &rowsInFrame);
    556 
    557     if (fFrameIsSubset) {
    558         // Fill the requested rows
    559         SkImageInfo fillInfo = this->dstInfo().makeWH(this->dstInfo().width(), count);
    560         uint32_t fillValue = this->onGetFillValue(this->dstInfo().colorType());
    561         fSwizzler->fill(fillInfo, dst, rowBytes, fillValue, this->options().fZeroInitialized);
    562 
    563         // Start to write pixels at the start of the image frame
    564         dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame);
    565     }
    566 
    567     for (int i = 0; i < rowsInFrame; i++) {
    568         if (!this->readRow()) {
    569             return i + rowsBeforeFrame;
    570         }
    571         fSwizzler->swizzle(dst, fSrcBuffer.get());
    572         dst = SkTAddOffset<void>(dst, rowBytes);
    573     }
    574 
    575     return count;
    576 }
    577 
    578 bool SkGifCodec::onSkipScanlines(int count) {
    579     int rowsBeforeFrame;
    580     int rowsInFrame;
    581     this->handleScanlineFrame(count, &rowsBeforeFrame, &rowsInFrame);
    582 
    583     for (int i = 0; i < rowsInFrame; i++) {
    584         if (!this->readRow()) {
    585             return false;
    586         }
    587     }
    588 
    589     return true;
    590 }
    591 
    592 SkCodec::SkScanlineOrder SkGifCodec::onGetScanlineOrder() const {
    593     if (fGif->Image.Interlace) {
    594         return kOutOfOrder_SkScanlineOrder;
    595     }
    596     return kTopDown_SkScanlineOrder;
    597 }
    598 
    599 int SkGifCodec::onOutputScanline(int inputScanline) const {
    600     if (fGif->Image.Interlace) {
    601         if (inputScanline < fFrameRect.top() || inputScanline >= fFrameRect.bottom()) {
    602             return inputScanline;
    603         }
    604         return get_output_row_interlaced(inputScanline - fFrameRect.top(), fFrameRect.height()) +
    605                 fFrameRect.top();
    606     }
    607     return inputScanline;
    608 }
    609