1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/*
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2006 The Android Open Source Project
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com *
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file.
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com */
7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
88a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkColor.h"
98a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkColorPriv.h"
10dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org#include "SkColorTable.h"
11dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org#include "SkImageDecoder.h"
1229d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com#include "SkRTConf.h"
13dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org#include "SkScaledBitmapSampler.h"
148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkStream.h"
158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkTemplates.h"
1629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com#include "SkUtils.h"
178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "gif_lib.h"
198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comclass SkGIFImageDecoder : public SkImageDecoder {
218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.compublic:
22a936e37cc76614868f5b489395bceeb340cc04cdcommit-bot@chromium.org    virtual Format getFormat() const SK_OVERRIDE {
238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return kGIF_Format;
248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
25d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comprotected:
27a936e37cc76614868f5b489395bceeb340cc04cdcommit-bot@chromium.org    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
28a936e37cc76614868f5b489395bceeb340cc04cdcommit-bot@chromium.org
29a936e37cc76614868f5b489395bceeb340cc04cdcommit-bot@chromium.orgprivate:
30a936e37cc76614868f5b489395bceeb340cc04cdcommit-bot@chromium.org    typedef SkImageDecoder INHERITED;
318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com};
328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatic const uint8_t gStartingIterlaceYValue[] = {
348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    0, 4, 2, 1
358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com};
368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatic const uint8_t gDeltaIterlaceYValue[] = {
378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    8, 8, 4, 2
388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com};
398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
4029d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.comSK_CONF_DECLARE(bool, c_suppressGIFImageDecoderWarnings,
4129d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                "images.gif.suppressDecoderWarnings", true,
4229d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                "Suppress GIF warnings and errors when calling image decode "
4329d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                "functions.");
4429d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com
4529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com
468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com/*  Implement the GIF interlace algorithm in an iterator.
478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    1) grab every 8th line beginning at 0
488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    2) grab every 8th line beginning at 4
498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    3) grab every 4th line beginning at 2
508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    4) grab every 2nd line beginning at 1
518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com*/
528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comclass GifInterlaceIter {
538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.compublic:
548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    GifInterlaceIter(int height) : fHeight(height) {
558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fStartYPtr = gStartingIterlaceYValue;
568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fDeltaYPtr = gDeltaIterlaceYValue;
578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fCurrY = *fStartYPtr++;
598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fDeltaY = *fDeltaYPtr++;
608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
61d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    int currY() const {
638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkASSERT(fStartYPtr);
648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkASSERT(fDeltaYPtr);
658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return fCurrY;
668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    void next() {
698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkASSERT(fStartYPtr);
708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkASSERT(fDeltaYPtr);
718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        int y = fCurrY + fDeltaY;
738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // We went from an if statement to a while loop so that we iterate
748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // through fStartYPtr until a valid row is found. This is so that images
758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // that are smaller than 5x5 will not trash memory.
768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        while (y >= fHeight) {
778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (gStartingIterlaceYValue +
788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                // we done
808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                SkDEBUGCODE(fStartYPtr = NULL;)
818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                SkDEBUGCODE(fDeltaYPtr = NULL;)
828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                y = 0;
838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            } else {
848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                y = *fStartYPtr++;
858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                fDeltaY = *fDeltaYPtr++;
868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fCurrY = y;
898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
90d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comprivate:
928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const int fHeight;
938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    int fCurrY;
948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    int fDeltaY;
958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const uint8_t* fStartYPtr;
968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const uint8_t* fDeltaYPtr;
978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com};
988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com///////////////////////////////////////////////////////////////////////////////
1008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatic int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
1028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                              int size) {
1038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkStream* stream = (SkStream*) fileType->UserData;
1048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return (int) stream->read(out, size);
1058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid CheckFreeExtension(SavedImage* Image) {
1088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (Image->ExtensionBlocks) {
109bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#if GIFLIB_MAJOR < 5
1108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        FreeExtension(Image);
111bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#else
112bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com        GifFreeExtensions(&Image->ExtensionBlockCount, &Image->ExtensionBlocks);
113bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#endif
1148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com// return NULL on failure
1188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatic const ColorMapObject* find_colormap(const GifFileType* gif) {
1198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const ColorMapObject* cmap = gif->Image.ColorMap;
1208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (NULL == cmap) {
1218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        cmap = gif->SColorMap;
1228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
12357f4969724a1dd88c8d9ae35a863e6cf621181d5djsollen@google.com
12457f4969724a1dd88c8d9ae35a863e6cf621181d5djsollen@google.com    if (NULL == cmap) {
12557f4969724a1dd88c8d9ae35a863e6cf621181d5djsollen@google.com        // no colormap found
12657f4969724a1dd88c8d9ae35a863e6cf621181d5djsollen@google.com        return NULL;
12757f4969724a1dd88c8d9ae35a863e6cf621181d5djsollen@google.com    }
1288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // some sanity checks
129bc7d2fb5be746f6e0d9bd60c9e181eec20fe31eareed@android.com    if (cmap && ((unsigned)cmap->ColorCount > 256 ||
130bc7d2fb5be746f6e0d9bd60c9e181eec20fe31eareed@android.com                 cmap->ColorCount != (1 << cmap->BitsPerPixel))) {
1318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        cmap = NULL;
1328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return cmap;
1348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com// return -1 if not found (i.e. we're completely opaque)
1378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatic int find_transpIndex(const SavedImage& image, int colorCount) {
1388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    int transpIndex = -1;
1398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    for (int i = 0; i < image.ExtensionBlockCount; ++i) {
1408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        const ExtensionBlock* eb = image.ExtensionBlocks + i;
1418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (eb->Function == 0xF9 && eb->ByteCount == 4) {
1428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (eb->Bytes[0] & 1) {
1438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                transpIndex = (unsigned char)eb->Bytes[3];
1448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                // check for valid transpIndex
1458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                if (transpIndex >= colorCount) {
1468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    transpIndex = -1;
1478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                }
1488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
1498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
1508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
1518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return transpIndex;
1538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
15529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.comstatic bool error_return(const SkBitmap& bm, const char msg[]) {
15629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com    if (!c_suppressGIFImageDecoderWarnings) {
15729d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com        SkDebugf("libgif error [%s] bitmap [%d %d] pixels %p colortable %p\n",
15829d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                 msg, bm.width(), bm.height(), bm.getPixels(),
15929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                 bm.getColorTable());
16029d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com    }
1618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return false;
1628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
16329d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.comstatic void gif_warning(const SkBitmap& bm, const char msg[]) {
16429d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com    if (!c_suppressGIFImageDecoderWarnings) {
16529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com        SkDebugf("libgif warning [%s] bitmap [%d %d] pixels %p colortable %p\n",
16629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                 msg, bm.width(), bm.height(), bm.getPixels(),
16729d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                 bm.getColorTable());
16829d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com    }
16929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com}
1708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
171dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org/**
172dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org *  Skip rows in the source gif image.
173dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org *  @param gif Source image.
174dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org *  @param dst Scratch output needed by gif library call. Must be >= width bytes.
175dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org *  @param width Bytes per row in the source image.
176dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org *  @param rowsToSkip Number of rows to skip.
177dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org *  @return True on success, false on GIF_ERROR.
178dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org */
179dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.orgstatic bool skip_src_rows(GifFileType* gif, uint8_t* dst, int width, int rowsToSkip) {
180dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org    for (int i = 0; i < rowsToSkip; i++) {
181dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org        if (DGifGetLine(gif, dst, width) == GIF_ERROR) {
182dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            return false;
183dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org        }
184dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org    }
185dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org    return true;
186dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org}
187dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org
1883a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com/**
1893a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com *  GIFs with fewer then 256 color entries will sometimes index out of
1903a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com *  bounds of the color table (this is malformed, but libgif does not
1913a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com *  check sicne it is rare).  This function checks for this error and
1923a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com *  fixes it.  This makes the output image consistantly deterministic.
1933a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com */
1943a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.comstatic void sanitize_indexed_bitmap(SkBitmap* bm) {
1950689d7b12e7c427a077b003d3d8ae759d86f798freed    if ((kIndex_8_SkColorType == bm->colorType()) && !(bm->empty())) {
1963a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com        SkAutoLockPixels alp(*bm);
19749f085dddff10473b6ebf832a974288300224e60bsalomon        if (bm->getPixels()) {
1983a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com            SkColorTable* ct = bm->getColorTable();  // Index8 must have it.
1993a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com            SkASSERT(ct != NULL);
2003a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com            uint32_t count = ct->count();
2013a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com            SkASSERT(count > 0);
2023a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com            SkASSERT(count <= 0x100);
2033a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com            if (count != 0x100) {  // Full colortables can't go wrong.
2043a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                // Count is a power of 2; asserted elsewhere.
2053a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                uint8_t byteMask = (~(count - 1));
2063a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                bool warning = false;
2073a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                uint8_t* addr = static_cast<uint8_t*>(bm->getPixels());
2083a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                int height = bm->height();
2093a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                int width = bm->width();
2103a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                size_t rowBytes = bm->rowBytes();
2113a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                while (--height >= 0) {
2123a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                    uint8_t* ptr = addr;
2133a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                    int x = width;
2143a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                    while (--x >= 0) {
2153a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                        if (0 != ((*ptr) & byteMask)) {
2163a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                            warning = true;
2173a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                            *ptr = 0;
2183a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                        }
2193a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                        ++ptr;
2203a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                    }
2213a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                    addr += rowBytes;
2223a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                }
2233a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                if (warning) {
2243a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                    gif_warning(*bm, "Index out of bounds.");
2253a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                }
2263a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com            }
2273a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com        }
2283a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com    }
2293a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com}
2303a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com
2313f1f06a26bdb2022a5c72f93ae623a57b6659464reed@android.combool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
232bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#if GIFLIB_MAJOR < 5
2338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
234bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#else
235bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com    GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);
236bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#endif
2378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (NULL == gif) {
23829d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com        return error_return(*bm, "DGifOpen");
2398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
2408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);
2428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SavedImage temp_save;
2448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    temp_save.ExtensionBlocks=NULL;
2458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    temp_save.ExtensionBlockCount=0;
2468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);
2478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    int width, height;
2498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    GifRecordType recType;
2508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    GifByteType *extData;
251bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#if GIFLIB_MAJOR >= 5
252bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com    int extFunction;
253bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#endif
2549781ca586618cc8ea055f54021e706824313d4f5reed@android.com    int transpIndex = -1;   // -1 means we don't have it (yet)
25529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com    int fillIndex = gif->SBackGroundColor;
256d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
2578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    do {
2588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
25929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            return error_return(*bm, "DGifGetRecordType");
2608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
261d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
2628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        switch (recType) {
2638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        case IMAGE_DESC_RECORD_TYPE: {
2648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (DGifGetImageDesc(gif) == GIF_ERROR) {
26529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                return error_return(*bm, "IMAGE_DESC_RECORD_TYPE");
2668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
267d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
2688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (gif->ImageCount < 1) {    // sanity check
26929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                return error_return(*bm, "ImageCount < 1");
2708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
271d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
2728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            width = gif->SWidth;
2738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            height = gif->SHeight;
27429d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com
27529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
27629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            const GifImageDesc& desc = image->ImageDesc;
27729d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com
27829d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            int imageLeft = desc.Left;
27929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            int imageTop = desc.Top;
28029d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            const int innerWidth = desc.Width;
28129d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            const int innerHeight = desc.Height;
28229d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            if (innerWidth <= 0 || innerHeight <= 0) {
28329d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                return error_return(*bm, "invalid dimensions");
28429d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            }
28529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com
28629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            // check for valid descriptor
28729d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            if (innerWidth > width) {
28829d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                gif_warning(*bm, "image too wide, expanding output to size");
28929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                width = innerWidth;
29029d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                imageLeft = 0;
29129d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            } else if (imageLeft + innerWidth > width) {
29229d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                gif_warning(*bm, "shifting image left to fit");
29329d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                imageLeft = width - innerWidth;
29429d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            } else if (imageLeft < 0) {
29529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                gif_warning(*bm, "shifting image right to fit");
29629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                imageLeft = 0;
29729d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            }
29829d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com
29929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com
30029d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            if (innerHeight > height) {
30129d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                gif_warning(*bm, "image too tall,  expanding output to size");
30229d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                height = innerHeight;
30329d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                imageTop = 0;
30429d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            } else if (imageTop + innerHeight > height) {
30529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                gif_warning(*bm, "shifting image up to fit");
30629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                imageTop = height - innerHeight;
30729d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            } else if (imageTop < 0) {
30829d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                gif_warning(*bm, "shifting image down to fit");
30929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                imageTop = 0;
310dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            }
311dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org
3125926b86b90c68bffefbdc8639e41b5bc9102cec6reed#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
313dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            // FIXME: We could give the caller a choice of images or configs.
3146c22573edb234ad14df947278cfed010669a39a7reed            if (!this->chooseFromOneChoice(kIndex_8_SkColorType, width, height)) {
31529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                return error_return(*bm, "chooseFromOneChoice");
3168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
3175926b86b90c68bffefbdc8639e41b5bc9102cec6reed#endif
318c49cabfe5cddc1691a00f713ae1b077d9409389fskia.committer@gmail.com
319dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            SkScaledBitmapSampler sampler(width, height, this->getSampleSize());
320dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org
3216c22573edb234ad14df947278cfed010669a39a7reed            bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
3226c22573edb234ad14df947278cfed010669a39a7reed                                          kIndex_8_SkColorType, kPremul_SkAlphaType));
323dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org
324a936e37cc76614868f5b489395bceeb340cc04cdcommit-bot@chromium.org            if (SkImageDecoder::kDecodeBounds_Mode == mode) {
3258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                return true;
326a936e37cc76614868f5b489395bceeb340cc04cdcommit-bot@chromium.org            }
327a936e37cc76614868f5b489395bceeb340cc04cdcommit-bot@chromium.org
328d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
3298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            // now we decode the colortable
3308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            int colorCount = 0;
3318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            {
33229d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                // Declare colorPtr here for scope.
3330a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com                SkPMColor colorPtr[256]; // storage for worst-case
33429d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                const ColorMapObject* cmap = find_colormap(gif);
3350a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com                SkAlphaType alphaType = kOpaque_SkAlphaType;
33629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                if (cmap != NULL) {
3373a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com                    SkASSERT(cmap->ColorCount == (1 << (cmap->BitsPerPixel)));
33829d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    colorCount = cmap->ColorCount;
33929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    if (colorCount > 256) {
34029d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                        colorCount = 256;  // our kIndex8 can't support more
34129d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    }
34229d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    for (int index = 0; index < colorCount; index++) {
34329d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                        colorPtr[index] = SkPackARGB32(0xFF,
34429d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                                                       cmap->Colors[index].Red,
34529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                                                       cmap->Colors[index].Green,
34629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                                                       cmap->Colors[index].Blue);
34729d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    }
34829d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                } else {
34929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    // find_colormap() returned NULL.  Some (rare, broken)
35029d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    // GIFs don't have a color table, so we force one.
35129d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    gif_warning(*bm, "missing colormap");
35229d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    colorCount = 256;
35329d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    sk_memset32(colorPtr, SK_ColorWHITE, colorCount);
354dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                }
3559781ca586618cc8ea055f54021e706824313d4f5reed@android.com                transpIndex = find_transpIndex(temp_save, colorCount);
35629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                if (transpIndex >= 0) {
357dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                    colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a transparent SkPMColor
3580a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com                    alphaType = kPremul_SkAlphaType;
35929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    fillIndex = transpIndex;
36029d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                } else if (fillIndex >= colorCount) {
36129d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    // gif->SBackGroundColor should be less than colorCount.
36229d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    fillIndex = 0;  // If not, fix it.
363dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                }
3648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
3650a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com                SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable,
3660a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com                                                  (colorPtr, colorCount,
3670a6151d66cc32d91eca037c91e557158cf8a2be2reed@google.com                                                   alphaType)));
3688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                if (!this->allocPixelRef(bm, ctable)) {
36929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    return error_return(*bm, "allocPixelRef");
3708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                }
3718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
372d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
3738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            // abort if either inner dimension is <= 0
3748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (innerWidth <= 0 || innerHeight <= 0) {
37529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                return error_return(*bm, "non-pos inner width/height");
3768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
3778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
378dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            SkAutoLockPixels alp(*bm);
379dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org
380dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            SkAutoMalloc storage(innerWidth);
381dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            uint8_t* scanline = (uint8_t*) storage.get();
382dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org
383dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            // GIF has an option to store the scanlines of an image, plus a larger background,
384dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            // filled by a fill color. In this case, we will use a subset of the larger bitmap
385dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            // for sampling.
386dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            SkBitmap subset;
387dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            SkBitmap* workingBitmap;
3888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            // are we only a subset of the total bounds?
38929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            if ((imageTop | imageLeft) > 0 ||
390dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                 innerWidth < width || innerHeight < height) {
391dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                // Fill the background.
39229d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                memset(bm->getPixels(), fillIndex, bm->getSize());
393dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org
394dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                // Create a subset of the bitmap.
39529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                SkIRect subsetRect(SkIRect::MakeXYWH(imageLeft / sampler.srcDX(),
39629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                                                     imageTop / sampler.srcDY(),
397dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                                                     innerWidth / sampler.srcDX(),
398dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                                                     innerHeight / sampler.srcDY()));
399dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                if (!bm->extractSubset(&subset, subsetRect)) {
40029d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    return error_return(*bm, "Extract failed.");
401dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                }
402dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                // Update the sampler. We'll now be only sampling into the subset.
403dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->getSampleSize());
404dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                workingBitmap = &subset;
405dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            } else {
406dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                workingBitmap = bm;
407dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            }
408dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org
409dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            // bm is already locked, but if we had to take a subset, it must be locked also,
410dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            // so that getPixels() will point to its pixels.
411dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            SkAutoLockPixels alpWorking(*workingBitmap);
412dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org
413dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *this)) {
41429d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                return error_return(*bm, "Sampler failed to begin.");
4158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
416d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
4178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            // now decode each scanline
418dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            if (gif->Image.Interlace) {
419dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                // Iterate over the height of the source data. The sampler will
420dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                // take care of skipping unneeded rows.
4218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                GifInterlaceIter iter(innerHeight);
42229d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                for (int y = 0; y < innerHeight; y++) {
423dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                    if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
42429d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                        gif_warning(*bm, "interlace DGifGetLine");
42529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                        memset(scanline, fillIndex, innerWidth);
42629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                        for (; y < innerHeight; y++) {
42729d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                            sampler.sampleInterlaced(scanline, iter.currY());
42829d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                            iter.next();
42929d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                        }
43029d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                        return true;
4318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    }
432dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                    sampler.sampleInterlaced(scanline, iter.currY());
4338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    iter.next();
4348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                }
435dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org            } else {
4368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                // easy, non-interlace case
437dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                const int outHeight = workingBitmap->height();
438dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                skip_src_rows(gif, scanline, innerWidth, sampler.srcY0());
439dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                for (int y = 0; y < outHeight; y++) {
4408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
44129d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                        gif_warning(*bm, "DGifGetLine");
44229d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                        memset(scanline, fillIndex, innerWidth);
44329d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                        for (; y < outHeight; y++) {
44429d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                            sampler.next(scanline);
44529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                        }
44629d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                        return true;
4478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    }
448dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                    // scanline now contains the raw data. Sample it.
449dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                    sampler.next(scanline);
450dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                    if (y < outHeight - 1) {
451dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                        skip_src_rows(gif, scanline, innerWidth, sampler.srcDY() - 1);
452dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                    }
4538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                }
454dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                // skip the rest of the rows (if any)
455dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() + 1;
456dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                SkASSERT(read <= innerHeight);
457dac4a1d518a4788c3e2475d68cbe8683b4a448ffcommit-bot@chromium.org                skip_src_rows(gif, scanline, innerWidth, innerHeight - read);
4588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
4593a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com            sanitize_indexed_bitmap(bm);
46029d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com            return true;
4618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            } break;
462d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
4638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        case EXTENSION_RECORD_TYPE:
464bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#if GIFLIB_MAJOR < 5
4658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (DGifGetExtension(gif, &temp_save.Function,
4668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                                 &extData) == GIF_ERROR) {
467bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#else
468bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com            if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
469bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#endif
47029d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                return error_return(*bm, "DGifGetExtension");
4718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
4728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
4738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            while (extData != NULL) {
4748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                /* Create an extension block with our data */
475bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#if GIFLIB_MAJOR < 5
4768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                if (AddExtensionBlock(&temp_save, extData[0],
4778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                                      &extData[1]) == GIF_ERROR) {
478bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#else
479bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com                if (GifAddExtensionBlock(&gif->ExtensionBlockCount,
480bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com                                         &gif->ExtensionBlocks,
481bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com                                         extFunction,
482bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com                                         extData[0],
483bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com                                         &extData[1]) == GIF_ERROR) {
484bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#endif
48529d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    return error_return(*bm, "AddExtensionBlock");
4868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                }
4878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
48829d4e638641d6d089a0361619ff2a583fd1a827fhalcanary@google.com                    return error_return(*bm, "DGifGetExtensionNext");
4898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                }
490bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#if GIFLIB_MAJOR < 5
4918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                temp_save.Function = 0;
492bb89613380332065a2abf71b8b229eabcf2c0be8reed@google.com#endif
4938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
4948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
495d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
4968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        case TERMINATE_RECORD_TYPE:
4978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
498d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
499d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com        default:    /* Should be trapped by DGifGetRecordType */
5008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
5018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
5028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    } while (recType != TERMINATE_RECORD_TYPE);
5038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
5043a8ebd9e71dab5c6640c40b8576dbe0d901c261bhalcanary@google.com    sanitize_indexed_bitmap(bm);
5058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return true;
5068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
5078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
5088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com///////////////////////////////////////////////////////////////////////////////
509ec51cb863409e238b40c5beef458669d9a824481robertphillips@google.comDEFINE_DECODER_CREATOR(GIFImageDecoder);
510ec51cb863409e238b40c5beef458669d9a824481robertphillips@google.com///////////////////////////////////////////////////////////////////////////////
5118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
512b5571b3324cf18629a255ec85e189447069c9b14scroggo@google.comstatic bool is_gif(SkStreamRewindable* stream) {
5138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    char buf[GIF_STAMP_LEN];
5148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
5158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
5168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
5178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
51839edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com            return true;
5198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
5208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
52139edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com    return false;
52239edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com}
52339edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com
524b5571b3324cf18629a255ec85e189447069c9b14scroggo@google.comstatic SkImageDecoder* sk_libgif_dfactory(SkStreamRewindable* stream) {
52539edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com    if (is_gif(stream)) {
52639edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com        return SkNEW(SkGIFImageDecoder);
52739edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com    }
5288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return NULL;
5298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
5308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
531bd6343b1d60d2a85e930f33f4b06b4502b3e8caamtklein@google.comstatic SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory);
53239edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com
533b5571b3324cf18629a255ec85e189447069c9b14scroggo@google.comstatic SkImageDecoder::Format get_format_gif(SkStreamRewindable* stream) {
53439edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com    if (is_gif(stream)) {
53539edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com        return SkImageDecoder::kGIF_Format;
53639edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com    }
53739edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com    return SkImageDecoder::kUnknown_Format;
53839edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com}
53939edf4cd94e6fbeb8c1187a588b314e9795c81e4scroggo@google.com
540bd6343b1d60d2a85e930f33f4b06b4502b3e8caamtklein@google.comstatic SkImageDecoder_FormatReg gFormatReg(get_format_gif);
541