diff options
Diffstat (limited to 'libsgl/images/SkImageDecoder_libgif.cpp')
-rw-r--r-- | libsgl/images/SkImageDecoder_libgif.cpp | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/libsgl/images/SkImageDecoder_libgif.cpp b/libsgl/images/SkImageDecoder_libgif.cpp new file mode 100644 index 0000000..9353c37 --- /dev/null +++ b/libsgl/images/SkImageDecoder_libgif.cpp @@ -0,0 +1,331 @@ +/* libs/graphics/images/SkImageDecoder_libgif.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkImageDecoder.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkPackBits.h" + +#include "gif_lib.h" + +class SkGIFImageDecoder : public SkImageDecoder { +public: + virtual Format getFormat() const { + return kGIF_Format; + } + +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, + SkBitmap::Config pref, Mode mode); +}; + +static const uint8_t gStartingIterlaceYValue[] = { + 0, 4, 2, 1 +}; +static const uint8_t gDeltaIterlaceYValue[] = { + 8, 8, 4, 2 +}; + +/* Implement the GIF interlace algorithm in an iterator. + 1) grab every 8th line beginning at 0 + 2) grab every 8th line beginning at 4 + 3) grab every 4th line beginning at 2 + 4) grab every 2nd line beginning at 1 +*/ +class GifInterlaceIter { +public: + GifInterlaceIter(int height) : fHeight(height) { + fStartYPtr = gStartingIterlaceYValue; + fDeltaYPtr = gDeltaIterlaceYValue; + + fCurrY = *fStartYPtr++; + fDeltaY = *fDeltaYPtr++; + } + + int currY() const { + SkASSERT(fStartYPtr); + SkASSERT(fDeltaYPtr); + return fCurrY; + } + + void next() { + SkASSERT(fStartYPtr); + SkASSERT(fDeltaYPtr); + + int y = fCurrY + fDeltaY; + // We went from an if statement to a while loop so that we iterate + // through fStartYPtr until a valid row is found. This is so that images + // that are smaller than 5x5 will not trash memory. + while (y >= fHeight) { + if (gStartingIterlaceYValue + + SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) { + // we done + SkDEBUGCODE(fStartYPtr = NULL;) + SkDEBUGCODE(fDeltaYPtr = NULL;) + y = 0; + } else { + y = *fStartYPtr++; + fDeltaY = *fDeltaYPtr++; + } + } + fCurrY = y; + } + +private: + const int fHeight; + int fCurrY; + int fDeltaY; + const uint8_t* fStartYPtr; + const uint8_t* fDeltaYPtr; +}; + +/////////////////////////////////////////////////////////////////////////////// + +//#define GIF_STAMP "GIF" /* First chars in file - GIF stamp. */ +//#define GIF_STAMP_LEN (sizeof(GIF_STAMP) - 1) + +static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out, + int size) { + SkStream* stream = (SkStream*) fileType->UserData; + return (int) stream->read(out, size); +} + +void CheckFreeExtension(SavedImage* Image) { + if (Image->ExtensionBlocks) { + FreeExtension(Image); + } +} + +// return NULL on failure +static const ColorMapObject* find_colormap(const GifFileType* gif) { + const ColorMapObject* cmap = gif->SColorMap; + if (NULL == cmap) { + cmap = gif->Image.ColorMap; + } + // some sanity checks + if ((unsigned)cmap->ColorCount > 256 || + cmap->ColorCount != (1 << cmap->BitsPerPixel)) { + cmap = NULL; + } + return cmap; +} + +// return -1 if not found (i.e. we're completely opaque) +static int find_transpIndex(const SavedImage& image, int colorCount) { + int transpIndex = -1; + for (int i = 0; i < image.ExtensionBlockCount; ++i) { + const ExtensionBlock* eb = image.ExtensionBlocks + i; + if (eb->Function == 0xF9 && eb->ByteCount == 4) { + if (eb->Bytes[0] & 1) { + transpIndex = (unsigned char)eb->Bytes[3]; + // check for valid transpIndex + if (transpIndex >= colorCount) { + transpIndex = -1; + } + break; + } + } + } + return transpIndex; +} + +bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, + SkBitmap::Config prefConfig, Mode mode) { + GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); + if (NULL == gif) { + return false; + } + + SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif); + + SavedImage temp_save; + temp_save.ExtensionBlocks=NULL; + temp_save.ExtensionBlockCount=0; + SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save); + + int width, height; + GifRecordType recType; + GifByteType *extData; + + do { + if (DGifGetRecordType(gif, &recType) == GIF_ERROR) { + return false; + } + + switch (recType) { + case IMAGE_DESC_RECORD_TYPE: { + if (DGifGetImageDesc(gif) == GIF_ERROR) { + return false; + } + + if (gif->ImageCount < 1) { // sanity check + return false; + } + + width = gif->SWidth; + height = gif->SHeight; + if (width <= 0 || height <= 0 || + !this->chooseFromOneChoice(SkBitmap::kIndex8_Config, + width, height)) { + return false; + } + + bm->setConfig(SkBitmap::kIndex8_Config, width, height); + if (SkImageDecoder::kDecodeBounds_Mode == mode) + return true; + + SavedImage* image = &gif->SavedImages[gif->ImageCount-1]; + const GifImageDesc& desc = image->ImageDesc; + + // check for valid descriptor + if ( (desc.Top | desc.Left) < 0 || + desc.Left + desc.Width > width || + desc.Top + desc.Height > height) { + return false; + } + + // now we decode the colortable + int colorCount = 0; + { + const ColorMapObject* cmap = find_colormap(gif); + if (NULL == cmap) { + return false; + } + + colorCount = cmap->ColorCount; + SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount)); + SkPMColor* colorPtr = ctable->lockColors(); + for (int index = 0; index < colorCount; index++) + colorPtr[index] = SkPackARGB32(0xFF, + cmap->Colors[index].Red, + cmap->Colors[index].Green, + cmap->Colors[index].Blue); + + int transpIndex = find_transpIndex(temp_save, colorCount); + if (transpIndex < 0) + ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag); + else + colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor + ctable->unlockColors(true); + + SkAutoUnref aurts(ctable); + if (!this->allocPixelRef(bm, ctable)) { + return false; + } + } + + SkAutoLockPixels alp(*bm); + + // time to decode the scanlines + // + uint8_t* scanline = bm->getAddr8(0, 0); + const int rowBytes = bm->rowBytes(); + const int innerWidth = desc.Width; + const int innerHeight = desc.Height; + + // abort if either inner dimension is <= 0 + if (innerWidth <= 0 || innerHeight <= 0) { + return false; + } + + // are we only a subset of the total bounds? + if ((desc.Top | desc.Left) > 0 || + innerWidth < width || innerHeight < height) + { + uint8_t fill = (uint8_t)gif->SBackGroundColor; + // check for valid fill index/color + if (fill >= (unsigned)colorCount) { + fill = 0; + } + memset(scanline, gif->SBackGroundColor, bm->getSize()); + // bump our starting address + scanline += desc.Top * rowBytes + desc.Left; + } + + // now decode each scanline + if (gif->Image.Interlace) + { + GifInterlaceIter iter(innerHeight); + for (int y = 0; y < innerHeight; y++) + { + uint8_t* row = scanline + iter.currY() * rowBytes; + if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) { + return false; + } + iter.next(); + } + } + else + { + // easy, non-interlace case + for (int y = 0; y < innerHeight; y++) { + if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { + return false; + } + scanline += rowBytes; + } + } + goto DONE; + } break; + + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(gif, &temp_save.Function, + &extData) == GIF_ERROR) { + return false; + } + + while (extData != NULL) { + /* Create an extension block with our data */ + if (AddExtensionBlock(&temp_save, extData[0], + &extData[1]) == GIF_ERROR) { + return false; + } + if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { + return false; + } + temp_save.Function = 0; + } + break; + + case TERMINATE_RECORD_TYPE: + break; + + default: /* Should be trapped by DGifGetRecordType */ + break; + } + } while (recType != TERMINATE_RECORD_TYPE); + +DONE: + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream* stream) { + char buf[GIF_STAMP_LEN]; + if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { + if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || + memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || + memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { + return SkNEW(SkGIFImageDecoder); + } + } + return NULL; +} + |