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 {
     22         return kGIF_Format;
     23     }
     24 
     25 protected:
     26     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
     27 };
     28 
     29 static const uint8_t gStartingIterlaceYValue[] = {
     30     0, 4, 2, 1
     31 };
     32 static const uint8_t gDeltaIterlaceYValue[] = {
     33     8, 8, 4, 2
     34 };
     35 
     36 /*  Implement the GIF interlace algorithm in an iterator.
     37     1) grab every 8th line beginning at 0
     38     2) grab every 8th line beginning at 4
     39     3) grab every 4th line beginning at 2
     40     4) grab every 2nd line beginning at 1
     41 */
     42 class GifInterlaceIter {
     43 public:
     44     GifInterlaceIter(int height) : fHeight(height) {
     45         fStartYPtr = gStartingIterlaceYValue;
     46         fDeltaYPtr = gDeltaIterlaceYValue;
     47 
     48         fCurrY = *fStartYPtr++;
     49         fDeltaY = *fDeltaYPtr++;
     50     }
     51 
     52     int currY() const {
     53         SkASSERT(fStartYPtr);
     54         SkASSERT(fDeltaYPtr);
     55         return fCurrY;
     56     }
     57 
     58     void next() {
     59         SkASSERT(fStartYPtr);
     60         SkASSERT(fDeltaYPtr);
     61 
     62         int y = fCurrY + fDeltaY;
     63         // We went from an if statement to a while loop so that we iterate
     64         // through fStartYPtr until a valid row is found. This is so that images
     65         // that are smaller than 5x5 will not trash memory.
     66         while (y >= fHeight) {
     67             if (gStartingIterlaceYValue +
     68                     SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
     69                 // we done
     70                 SkDEBUGCODE(fStartYPtr = NULL;)
     71                 SkDEBUGCODE(fDeltaYPtr = NULL;)
     72                 y = 0;
     73             } else {
     74                 y = *fStartYPtr++;
     75                 fDeltaY = *fDeltaYPtr++;
     76             }
     77         }
     78         fCurrY = y;
     79     }
     80 
     81 private:
     82     const int fHeight;
     83     int fCurrY;
     84     int fDeltaY;
     85     const uint8_t* fStartYPtr;
     86     const uint8_t* fDeltaYPtr;
     87 };
     88 
     89 ///////////////////////////////////////////////////////////////////////////////
     90 
     91 //#define GIF_STAMP       "GIF"    /* First chars in file - GIF stamp. */
     92 //#define GIF_STAMP_LEN   (sizeof(GIF_STAMP) - 1)
     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             if (SkImageDecoder::kDecodeBounds_Mode == mode) {
    206                 bm->setConfig(SkBitmap::kIndex8_Config, width, height);
    207                 return true;
    208             }
    209 #ifdef SK_BUILD_FOR_ANDROID
    210             // No Bitmap reuse supported for this format
    211             if (!bm->isNull()) {
    212                 return false;
    213             }
    214 #endif
    215 
    216             bm->setConfig(SkBitmap::kIndex8_Config, width, height);
    217             SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
    218             const GifImageDesc& desc = image->ImageDesc;
    219 
    220             // check for valid descriptor
    221             if (   (desc.Top | desc.Left) < 0 ||
    222                     desc.Left + desc.Width > width ||
    223                     desc.Top + desc.Height > height) {
    224                 return error_return(gif, *bm, "TopLeft");
    225             }
    226 
    227             // now we decode the colortable
    228             int colorCount = 0;
    229             {
    230                 const ColorMapObject* cmap = find_colormap(gif);
    231                 if (NULL == cmap) {
    232                     return error_return(gif, *bm, "null cmap");
    233                 }
    234 
    235                 colorCount = cmap->ColorCount;
    236                 SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount));
    237                 SkPMColor* colorPtr = ctable->lockColors();
    238                 for (int index = 0; index < colorCount; index++)
    239                     colorPtr[index] = SkPackARGB32(0xFF,
    240                                                    cmap->Colors[index].Red,
    241                                                    cmap->Colors[index].Green,
    242                                                    cmap->Colors[index].Blue);
    243 
    244                 transpIndex = find_transpIndex(temp_save, colorCount);
    245                 if (transpIndex < 0)
    246                     ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
    247                 else
    248                     colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor
    249                 ctable->unlockColors(true);
    250 
    251                 SkAutoUnref aurts(ctable);
    252                 if (!this->allocPixelRef(bm, ctable)) {
    253                     return error_return(gif, *bm, "allocPixelRef");
    254                 }
    255             }
    256 
    257             SkAutoLockPixels alp(*bm);
    258 
    259             // time to decode the scanlines
    260             //
    261             uint8_t*  scanline = bm->getAddr8(0, 0);
    262             const int rowBytes = bm->rowBytes();
    263             const int innerWidth = desc.Width;
    264             const int innerHeight = desc.Height;
    265 
    266             // abort if either inner dimension is <= 0
    267             if (innerWidth <= 0 || innerHeight <= 0) {
    268                 return error_return(gif, *bm, "non-pos inner width/height");
    269             }
    270 
    271             // are we only a subset of the total bounds?
    272             if ((desc.Top | desc.Left) > 0 ||
    273                  innerWidth < width || innerHeight < height)
    274             {
    275                 int fill;
    276                 if (transpIndex >= 0) {
    277                     fill = transpIndex;
    278                 } else {
    279                     fill = gif->SBackGroundColor;
    280                 }
    281                 // check for valid fill index/color
    282                 if (static_cast<unsigned>(fill) >=
    283                         static_cast<unsigned>(colorCount)) {
    284                     fill = 0;
    285                 }
    286                 memset(scanline, fill, bm->getSize());
    287                 // bump our starting address
    288                 scanline += desc.Top * rowBytes + desc.Left;
    289             }
    290 
    291             // now decode each scanline
    292             if (gif->Image.Interlace)
    293             {
    294                 GifInterlaceIter iter(innerHeight);
    295                 for (int y = 0; y < innerHeight; y++)
    296                 {
    297                     uint8_t* row = scanline + iter.currY() * rowBytes;
    298                     if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) {
    299                         return error_return(gif, *bm, "interlace DGifGetLine");
    300                     }
    301                     iter.next();
    302                 }
    303             }
    304             else
    305             {
    306                 // easy, non-interlace case
    307                 for (int y = 0; y < innerHeight; y++) {
    308                     if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
    309                         return error_return(gif, *bm, "DGifGetLine");
    310                     }
    311                     scanline += rowBytes;
    312                 }
    313             }
    314             goto DONE;
    315             } break;
    316 
    317         case EXTENSION_RECORD_TYPE:
    318 #if GIFLIB_MAJOR < 5
    319             if (DGifGetExtension(gif, &temp_save.Function,
    320                                  &extData) == GIF_ERROR) {
    321 #else
    322             if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
    323 #endif
    324                 return error_return(gif, *bm, "DGifGetExtension");
    325             }
    326 
    327             while (extData != NULL) {
    328                 /* Create an extension block with our data */
    329 #if GIFLIB_MAJOR < 5
    330                 if (AddExtensionBlock(&temp_save, extData[0],
    331                                       &extData[1]) == GIF_ERROR) {
    332 #else
    333                 if (GifAddExtensionBlock(&gif->ExtensionBlockCount,
    334                                          &gif->ExtensionBlocks,
    335                                          extFunction,
    336                                          extData[0],
    337                                          &extData[1]) == GIF_ERROR) {
    338 #endif
    339                     return error_return(gif, *bm, "AddExtensionBlock");
    340                 }
    341                 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
    342                     return error_return(gif, *bm, "DGifGetExtensionNext");
    343                 }
    344 #if GIFLIB_MAJOR < 5
    345                 temp_save.Function = 0;
    346 #endif
    347             }
    348             break;
    349 
    350         case TERMINATE_RECORD_TYPE:
    351             break;
    352 
    353         default:	/* Should be trapped by DGifGetRecordType */
    354             break;
    355         }
    356     } while (recType != TERMINATE_RECORD_TYPE);
    357 
    358 DONE:
    359     return true;
    360 }
    361 
    362 ///////////////////////////////////////////////////////////////////////////////
    363 DEFINE_DECODER_CREATOR(GIFImageDecoder);
    364 ///////////////////////////////////////////////////////////////////////////////
    365 
    366 #include "SkTRegistry.h"
    367 
    368 static SkImageDecoder* sk_libgif_dfactory(SkStream* stream) {
    369     char buf[GIF_STAMP_LEN];
    370     if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
    371         if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
    372                 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
    373                 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
    374             return SkNEW(SkGIFImageDecoder);
    375         }
    376     }
    377     return NULL;
    378 }
    379 
    380 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libgif_dfactory);
    381