Index: skia/images/SkImageDecoder_libgif.cpp |
=================================================================== |
--- skia/images/SkImageDecoder_libgif.cpp (revision 16859) |
+++ skia/images/SkImageDecoder_libgif.cpp (working copy) |
@@ -1,340 +0,0 @@ |
-/* 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; |
-} |
- |
-static bool error_return(GifFileType* gif, const SkBitmap& bm, |
- const char msg[]) { |
-#if 0 |
- SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n", |
- msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable()); |
-#endif |
- return false; |
-} |
- |
-bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, |
- SkBitmap::Config prefConfig, Mode mode) { |
- GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); |
- if (NULL == gif) { |
- return error_return(gif, *bm, "DGifOpen"); |
- } |
- |
- 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 error_return(gif, *bm, "DGifGetRecordType"); |
- } |
- |
- switch (recType) { |
- case IMAGE_DESC_RECORD_TYPE: { |
- if (DGifGetImageDesc(gif) == GIF_ERROR) { |
- return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE"); |
- } |
- |
- if (gif->ImageCount < 1) { // sanity check |
- return error_return(gif, *bm, "ImageCount < 1"); |
- } |
- |
- width = gif->SWidth; |
- height = gif->SHeight; |
- if (width <= 0 || height <= 0 || |
- !this->chooseFromOneChoice(SkBitmap::kIndex8_Config, |
- width, height)) { |
- return error_return(gif, *bm, "chooseFromOneChoice"); |
- } |
- |
- 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 error_return(gif, *bm, "TopLeft"); |
- } |
- |
- // now we decode the colortable |
- int colorCount = 0; |
- { |
- const ColorMapObject* cmap = find_colormap(gif); |
- if (NULL == cmap) { |
- return error_return(gif, *bm, "null cmap"); |
- } |
- |
- 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 error_return(gif, *bm, "allocPixelRef"); |
- } |
- } |
- |
- 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 error_return(gif, *bm, "non-pos inner width/height"); |
- } |
- |
- // 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 error_return(gif, *bm, "interlace DGifGetLine"); |
- } |
- iter.next(); |
- } |
- } |
- else |
- { |
- // easy, non-interlace case |
- for (int y = 0; y < innerHeight; y++) { |
- if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { |
- return error_return(gif, *bm, "DGifGetLine"); |
- } |
- scanline += rowBytes; |
- } |
- } |
- goto DONE; |
- } break; |
- |
- case EXTENSION_RECORD_TYPE: |
- if (DGifGetExtension(gif, &temp_save.Function, |
- &extData) == GIF_ERROR) { |
- return error_return(gif, *bm, "DGifGetExtension"); |
- } |
- |
- while (extData != NULL) { |
- /* Create an extension block with our data */ |
- if (AddExtensionBlock(&temp_save, extData[0], |
- &extData[1]) == GIF_ERROR) { |
- return error_return(gif, *bm, "AddExtensionBlock"); |
- } |
- if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { |
- return error_return(gif, *bm, "DGifGetExtensionNext"); |
- } |
- 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; |
-} |
- |