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 = ⊂ 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