Home | History | Annotate | Download | only in images
      1 
      2 /*
      3  * Copyright 2006 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "SkImageDecoder.h"
     11 #include "SkColor.h"
     12 #include "SkColorPriv.h"
     13 #include "SkStream.h"
     14 #include "SkTemplates.h"
     15 #include "SkPackBits.h"
     16 
     17 #include "gif_lib.h"
     18 
     19 class SkGIFImageDecoder : public SkImageDecoder {
     20 public:
     21     virtual Format getFormat() const SK_OVERRIDE {
     22         return kGIF_Format;
     23     }
     24 
     25 protected:
     26     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
     27 
     28 private:
     29     typedef SkImageDecoder INHERITED;
     30 };
     31 
     32 static const uint8_t gStartingIterlaceYValue[] = {
     33     0, 4, 2, 1
     34 };
     35 static const uint8_t gDeltaIterlaceYValue[] = {
     36     8, 8, 4, 2
     37 };
     38 
     39 /*  Implement the GIF interlace algorithm in an iterator.
     40     1) grab every 8th line beginning at 0
     41     2) grab every 8th line beginning at 4
     42     3) grab every 4th line beginning at 2
     43     4) grab every 2nd line beginning at 1
     44 */
     45 class GifInterlaceIter {
     46 public:
     47     GifInterlaceIter(int height) : fHeight(height) {
     48         fStartYPtr = gStartingIterlaceYValue;
     49         fDeltaYPtr = gDeltaIterlaceYValue;
     50 
     51         fCurrY = *fStartYPtr++;
     52         fDeltaY = *fDeltaYPtr++;
     53     }
     54 
     55     int currY() const {
     56         SkASSERT(fStartYPtr);
     57         SkASSERT(fDeltaYPtr);
     58         return fCurrY;
     59     }
     60 
     61     void next() {
     62         SkASSERT(fStartYPtr);
     63         SkASSERT(fDeltaYPtr);
     64 
     65         int y = fCurrY + fDeltaY;
     66         // We went from an if statement to a while loop so that we iterate
     67         // through fStartYPtr until a valid row is found. This is so that images
     68         // that are smaller than 5x5 will not trash memory.
     69         while (y >= fHeight) {
     70             if (gStartingIterlaceYValue +
     71                     SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
     72                 // we done
     73                 SkDEBUGCODE(fStartYPtr = NULL;)
     74                 SkDEBUGCODE(fDeltaYPtr = NULL;)
     75                 y = 0;
     76             } else {
     77                 y = *fStartYPtr++;
     78                 fDeltaY = *fDeltaYPtr++;
     79             }
     80         }
     81         fCurrY = y;
     82     }
     83 
     84 private:
     85     const int fHeight;
     86     int fCurrY;
     87     int fDeltaY;
     88     const uint8_t* fStartYPtr;
     89     const uint8_t* fDeltaYPtr;
     90 };
     91 
     92 ///////////////////////////////////////////////////////////////////////////////
     93 
     94 static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
     95                               int size) {
     96     SkStream* stream = (SkStream*) fileType->UserData;
     97     return (int) stream->read(out, size);
     98 }
     99 
    100 void CheckFreeExtension(SavedImage* Image) {
    101     if (Image->ExtensionBlocks) {
    102 #if GIFLIB_MAJOR < 5
    103         FreeExtension(Image);
    104 #else
    105         GifFreeExtensions(&Image->ExtensionBlockCount, &Image->ExtensionBlocks);
    106 #endif
    107     }
    108 }
    109 
    110 // return NULL on failure
    111 static const ColorMapObject* find_colormap(const GifFileType* gif) {
    112     const ColorMapObject* cmap = gif->Image.ColorMap;
    113     if (NULL == cmap) {
    114         cmap = gif->SColorMap;
    115     }
    116 
    117     if (NULL == cmap) {
    118         // no colormap found
    119         return NULL;
    120     }
    121     // some sanity checks
    122     if (cmap && ((unsigned)cmap->ColorCount > 256 ||
    123                  cmap->ColorCount != (1 << cmap->BitsPerPixel))) {
    124         cmap = NULL;
    125     }
    126     return cmap;
    127 }
    128 
    129 // return -1 if not found (i.e. we're completely opaque)
    130 static int find_transpIndex(const SavedImage& image, int colorCount) {
    131     int transpIndex = -1;
    132     for (int i = 0; i < image.ExtensionBlockCount; ++i) {
    133         const ExtensionBlock* eb = image.ExtensionBlocks + i;
    134         if (eb->Function == 0xF9 && eb->ByteCount == 4) {
    135             if (eb->Bytes[0] & 1) {
    136                 transpIndex = (unsigned char)eb->Bytes[3];
    137                 // check for valid transpIndex
    138                 if (transpIndex >= colorCount) {
    139                     transpIndex = -1;
    140                 }
    141                 break;
    142             }
    143         }
    144     }
    145     return transpIndex;
    146 }
    147 
    148 static bool error_return(GifFileType* gif, const SkBitmap& bm,
    149                          const char msg[]) {
    150 #if 0
    151     SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n",
    152              msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable());
    153 #endif
    154     return false;
    155 }
    156 
    157 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
    158 #if GIFLIB_MAJOR < 5
    159     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
    160 #else
    161     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);
    162 #endif
    163     if (NULL == gif) {
    164         return error_return(gif, *bm, "DGifOpen");
    165     }
    166 
    167     SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);
    168 
    169     SavedImage temp_save;
    170     temp_save.ExtensionBlocks=NULL;
    171     temp_save.ExtensionBlockCount=0;
    172     SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);
    173 
    174     int width, height;
    175     GifRecordType recType;
    176     GifByteType *extData;
    177 #if GIFLIB_MAJOR >= 5
    178     int extFunction;
    179 #endif
    180     int transpIndex = -1;   // -1 means we don't have it (yet)
    181 
    182     do {
    183         if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
    184             return error_return(gif, *bm, "DGifGetRecordType");
    185         }
    186 
    187         switch (recType) {
    188         case IMAGE_DESC_RECORD_TYPE: {
    189             if (DGifGetImageDesc(gif) == GIF_ERROR) {
    190                 return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE");
    191             }
    192 
    193             if (gif->ImageCount < 1) {    // sanity check
    194                 return error_return(gif, *bm, "ImageCount < 1");
    195             }
    196 
    197             width = gif->SWidth;
    198             height = gif->SHeight;
    199             if (width <= 0 || height <= 0 ||
    200                 !this->chooseFromOneChoice(SkBitmap::kIndex8_Config,
    201                                            width, height)) {
    202                 return error_return(gif, *bm, "chooseFromOneChoice");
    203             }
    204 
    205             bm->setConfig(SkBitmap::kIndex8_Config, width, height);
    206             if (SkImageDecoder::kDecodeBounds_Mode == mode) {
    207                 return true;
    208             }
    209 
    210             SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
    211             const GifImageDesc& desc = image->ImageDesc;
    212 
    213             // check for valid descriptor
    214             if (   (desc.Top | desc.Left) < 0 ||
    215                     desc.Left + desc.Width > width ||
    216                     desc.Top + desc.Height > height) {
    217                 return error_return(gif, *bm, "TopLeft");
    218             }
    219 
    220             // now we decode the colortable
    221             int colorCount = 0;
    222             {
    223                 const ColorMapObject* cmap = find_colormap(gif);
    224                 if (NULL == cmap) {
    225                     return error_return(gif, *bm, "null cmap");
    226                 }
    227 
    228                 colorCount = cmap->ColorCount;
    229                 SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount));
    230                 SkPMColor* colorPtr = ctable->lockColors();
    231                 for (int index = 0; index < colorCount; index++)
    232                     colorPtr[index] = SkPackARGB32(0xFF,
    233                                                    cmap->Colors[index].Red,
    234                                                    cmap->Colors[index].Green,
    235                                                    cmap->Colors[index].Blue);
    236 
    237                 transpIndex = find_transpIndex(temp_save, colorCount);
    238                 if (transpIndex < 0)
    239                     ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
    240                 else
    241                     colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor
    242                 ctable->unlockColors(true);
    243 
    244                 SkAutoUnref aurts(ctable);
    245                 if (!this->allocPixelRef(bm, ctable)) {
    246                     return error_return(gif, *bm, "allocPixelRef");
    247                 }
    248             }
    249 
    250             SkAutoLockPixels alp(*bm);
    251 
    252             // time to decode the scanlines
    253             //
    254             uint8_t*  scanline = bm->getAddr8(0, 0);
    255             const int rowBytes = bm->rowBytes();
    256             const int innerWidth = desc.Width;
    257             const int innerHeight = desc.Height;
    258 
    259             // abort if either inner dimension is <= 0
    260             if (innerWidth <= 0 || innerHeight <= 0) {
    261                 return error_return(gif, *bm, "non-pos inner width/height");
    262             }
    263 
    264             // are we only a subset of the total bounds?
    265             if ((desc.Top | desc.Left) > 0 ||
    266                  innerWidth < width || innerHeight < height)
    267             {
    268                 int fill;
    269                 if (transpIndex >= 0) {
    270                     fill = transpIndex;
    271                 } else {
    272                     fill = gif->SBackGroundColor;
    273                 }
    274                 // check for valid fill index/color
    275                 if (static_cast<unsigned>(fill) >=
    276                         static_cast<unsigned>(colorCount)) {
    277                     fill = 0;
    278                 }
    279                 memset(scanline, fill, bm->getSize());
    280                 // bump our starting address
    281                 scanline += desc.Top * rowBytes + desc.Left;
    282             }
    283 
    284             // now decode each scanline
    285             if (gif->Image.Interlace)
    286             {
    287                 GifInterlaceIter iter(innerHeight);
    288                 for (int y = 0; y < innerHeight; y++)
    289                 {
    290                     uint8_t* row = scanline + iter.currY() * rowBytes;
    291                     if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) {
    292                         return error_return(gif, *bm, "interlace DGifGetLine");
    293                     }
    294                     iter.next();
    295                 }
    296             }
    297             else
    298             {
    299                 // easy, non-interlace case
    300                 for (int y = 0; y < innerHeight; y++) {
    301                     if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
    302                         return error_return(gif, *bm, "DGifGetLine");
    303                     }
    304                     scanline += rowBytes;
    305                 }
    306             }
    307             goto DONE;
    308             } break;
    309 
    310         case EXTENSION_RECORD_TYPE:
    311 #if GIFLIB_MAJOR < 5
    312             if (DGifGetExtension(gif, &temp_save.Function,
    313                                  &extData) == GIF_ERROR) {
    314 #else
    315             if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
    316 #endif
    317                 return error_return(gif, *bm, "DGifGetExtension");
    318             }
    319 
    320             while (extData != NULL) {
    321                 /* Create an extension block with our data */
    322 #if GIFLIB_MAJOR < 5
    323                 if (AddExtensionBlock(&temp_save, extData[0],
    324                                       &extData[1]) == GIF_ERROR) {
    325 #else
    326                 if (GifAddExtensionBlock(&gif->ExtensionBlockCount,
    327                                          &gif->ExtensionBlocks,
    328                                          extFunction,
    329                                          extData[0],
    330                                          &extData[1]) == GIF_ERROR) {
    331 #endif
    332                     return error_return(gif, *bm, "AddExtensionBlock");
    333                 }
    334                 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
    335                     return error_return(gif, *bm, "DGifGetExtensionNext");
    336                 }
    337 #if GIFLIB_MAJOR < 5
    338                 temp_save.Function = 0;
    339 #endif
    340             }
    341             break;
    342 
    343         case TERMINATE_RECORD_TYPE:
    344             break;
    345 
    346         default:    /* Should be trapped by DGifGetRecordType */
    347             break;
    348         }
    349     } while (recType != TERMINATE_RECORD_TYPE);
    350 
    351 DONE:
    352     return true;
    353 }
    354 
    355 ///////////////////////////////////////////////////////////////////////////////
    356 DEFINE_DECODER_CREATOR(GIFImageDecoder);
    357 ///////////////////////////////////////////////////////////////////////////////
    358 
    359 static bool is_gif(SkStream* stream) {
    360     char buf[GIF_STAMP_LEN];
    361     if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
    362         if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
    363                 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
    364                 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
    365             return true;
    366         }
    367     }
    368     return false;
    369 }
    370 
    371 #include "SkTRegistry.h"
    372 
    373 static SkImageDecoder* sk_libgif_dfactory(SkStream* stream) {
    374     if (is_gif(stream)) {
    375         return SkNEW(SkGIFImageDecoder);
    376     }
    377     return NULL;
    378 }
    379 
    380 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libgif_dfactory);
    381 
    382 static SkImageDecoder::Format get_format_gif(SkStream* stream) {
    383     if (is_gif(stream)) {
    384         return SkImageDecoder::kGIF_Format;
    385     }
    386     return SkImageDecoder::kUnknown_Format;
    387 }
    388 
    389 static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_gif);
    390