grab from latest android



git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
new file mode 100644
index 0000000..519366a
--- /dev/null
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -0,0 +1,340 @@
+/* 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->Image.ColorMap;
+    if (NULL == cmap) {
+        cmap = gif->SColorMap;
+    }
+    // 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;
+}
+