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         FreeExtension(Image);
    103     }
    104 }
    105 
    106 // return NULL on failure
    107 static const ColorMapObject* find_colormap(const GifFileType* gif) {
    108     const ColorMapObject* cmap = gif->Image.ColorMap;
    109     if (NULL == cmap) {
    110         cmap = gif->SColorMap;
    111     }
    112 
    113     if (NULL == cmap) {
    114         // no colormap found
    115         return NULL;
    116     }
    117     // some sanity checks
    118     if (cmap && ((unsigned)cmap->ColorCount > 256 ||
    119                  cmap->ColorCount != (1 << cmap->BitsPerPixel))) {
    120         cmap = NULL;
    121     }
    122     return cmap;
    123 }
    124 
    125 // return -1 if not found (i.e. we're completely opaque)
    126 static int find_transpIndex(const SavedImage& image, int colorCount) {
    127     int transpIndex = -1;
    128     for (int i = 0; i < image.ExtensionBlockCount; ++i) {
    129         const ExtensionBlock* eb = image.ExtensionBlocks + i;
    130         if (eb->Function == 0xF9 && eb->ByteCount == 4) {
    131             if (eb->Bytes[0] & 1) {
    132                 transpIndex = (unsigned char)eb->Bytes[3];
    133                 // check for valid transpIndex
    134                 if (transpIndex >= colorCount) {
    135                     transpIndex = -1;
    136                 }
    137                 break;
    138             }
    139         }
    140     }
    141     return transpIndex;
    142 }
    143 
    144 static bool error_return(GifFileType* gif, const SkBitmap& bm,
    145                          const char msg[]) {
    146 #if 0
    147     SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n",
    148              msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable());
    149 #endif
    150     return false;
    151 }
    152 
    153 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
    154     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
    155     if (NULL == gif) {
    156         return error_return(gif, *bm, "DGifOpen");
    157     }
    158 
    159     SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);
    160 
    161     SavedImage temp_save;
    162     temp_save.ExtensionBlocks=NULL;
    163     temp_save.ExtensionBlockCount=0;
    164     SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);
    165 
    166     int width, height;
    167     GifRecordType recType;
    168     GifByteType *extData;
    169     int transpIndex = -1;   // -1 means we don't have it (yet)
    170 
    171     do {
    172         if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
    173             return error_return(gif, *bm, "DGifGetRecordType");
    174         }
    175 
    176         switch (recType) {
    177         case IMAGE_DESC_RECORD_TYPE: {
    178             if (DGifGetImageDesc(gif) == GIF_ERROR) {
    179                 return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE");
    180             }
    181 
    182             if (gif->ImageCount < 1) {    // sanity check
    183                 return error_return(gif, *bm, "ImageCount < 1");
    184             }
    185 
    186             width = gif->SWidth;
    187             height = gif->SHeight;
    188             if (width <= 0 || height <= 0 ||
    189                 !this->chooseFromOneChoice(SkBitmap::kIndex8_Config,
    190                                            width, height)) {
    191                 return error_return(gif, *bm, "chooseFromOneChoice");
    192             }
    193 
    194             if (SkImageDecoder::kDecodeBounds_Mode == mode) {
    195                 bm->setConfig(SkBitmap::kIndex8_Config, width, height);
    196                 return true;
    197             }
    198 #ifdef SK_BUILD_FOR_ANDROID
    199             // No Bitmap reuse supported for this format
    200             if (!bm->isNull()) {
    201                 return false;
    202             }
    203 #endif
    204 
    205             bm->setConfig(SkBitmap::kIndex8_Config, width, height);
    206             SavedImage* image = &gif->SavedImages[gif->ImageCount-1];
    207             const GifImageDesc& desc = image->ImageDesc;
    208 
    209             // check for valid descriptor
    210             if (   (desc.Top | desc.Left) < 0 ||
    211                     desc.Left + desc.Width > width ||
    212                     desc.Top + desc.Height > height) {
    213                 return error_return(gif, *bm, "TopLeft");
    214             }
    215 
    216             // now we decode the colortable
    217             int colorCount = 0;
    218             {
    219                 const ColorMapObject* cmap = find_colormap(gif);
    220                 if (NULL == cmap) {
    221                     return error_return(gif, *bm, "null cmap");
    222                 }
    223 
    224                 colorCount = cmap->ColorCount;
    225                 SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount));
    226                 SkPMColor* colorPtr = ctable->lockColors();
    227                 for (int index = 0; index < colorCount; index++)
    228                     colorPtr[index] = SkPackARGB32(0xFF,
    229                                                    cmap->Colors[index].Red,
    230                                                    cmap->Colors[index].Green,
    231                                                    cmap->Colors[index].Blue);
    232 
    233                 transpIndex = find_transpIndex(temp_save, colorCount);
    234                 if (transpIndex < 0)
    235                     ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
    236                 else
    237                     colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor
    238                 ctable->unlockColors(true);
    239 
    240                 SkAutoUnref aurts(ctable);
    241                 if (!this->allocPixelRef(bm, ctable)) {
    242                     return error_return(gif, *bm, "allocPixelRef");
    243                 }
    244             }
    245 
    246             SkAutoLockPixels alp(*bm);
    247 
    248             // time to decode the scanlines
    249             //
    250             uint8_t*  scanline = bm->getAddr8(0, 0);
    251             const int rowBytes = bm->rowBytes();
    252             const int innerWidth = desc.Width;
    253             const int innerHeight = desc.Height;
    254 
    255             // abort if either inner dimension is <= 0
    256             if (innerWidth <= 0 || innerHeight <= 0) {
    257                 return error_return(gif, *bm, "non-pos inner width/height");
    258             }
    259 
    260             // are we only a subset of the total bounds?
    261             if ((desc.Top | desc.Left) > 0 ||
    262                  innerWidth < width || innerHeight < height)
    263             {
    264                 int fill;
    265                 if (transpIndex >= 0) {
    266                     fill = transpIndex;
    267                 } else {
    268                     fill = gif->SBackGroundColor;
    269                 }
    270                 // check for valid fill index/color
    271                 if (static_cast<unsigned>(fill) >=
    272                         static_cast<unsigned>(colorCount)) {
    273                     fill = 0;
    274                 }
    275                 memset(scanline, fill, bm->getSize());
    276                 // bump our starting address
    277                 scanline += desc.Top * rowBytes + desc.Left;
    278             }
    279 
    280             // now decode each scanline
    281             if (gif->Image.Interlace)
    282             {
    283                 GifInterlaceIter iter(innerHeight);
    284                 for (int y = 0; y < innerHeight; y++)
    285                 {
    286                     uint8_t* row = scanline + iter.currY() * rowBytes;
    287                     if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) {
    288                         return error_return(gif, *bm, "interlace DGifGetLine");
    289                     }
    290                     iter.next();
    291                 }
    292             }
    293             else
    294             {
    295                 // easy, non-interlace case
    296                 for (int y = 0; y < innerHeight; y++) {
    297                     if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {
    298                         return error_return(gif, *bm, "DGifGetLine");
    299                     }
    300                     scanline += rowBytes;
    301                 }
    302             }
    303             goto DONE;
    304             } break;
    305 
    306         case EXTENSION_RECORD_TYPE:
    307             if (DGifGetExtension(gif, &temp_save.Function,
    308                                  &extData) == GIF_ERROR) {
    309                 return error_return(gif, *bm, "DGifGetExtension");
    310             }
    311 
    312             while (extData != NULL) {
    313                 /* Create an extension block with our data */
    314                 if (AddExtensionBlock(&temp_save, extData[0],
    315                                       &extData[1]) == GIF_ERROR) {
    316                     return error_return(gif, *bm, "AddExtensionBlock");
    317                 }
    318                 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
    319                     return error_return(gif, *bm, "DGifGetExtensionNext");
    320                 }
    321                 temp_save.Function = 0;
    322             }
    323             break;
    324 
    325         case TERMINATE_RECORD_TYPE:
    326             break;
    327 
    328         default:	/* Should be trapped by DGifGetRecordType */
    329             break;
    330         }
    331     } while (recType != TERMINATE_RECORD_TYPE);
    332 
    333 DONE:
    334     return true;
    335 }
    336 
    337 ///////////////////////////////////////////////////////////////////////////////
    338 
    339 #include "SkTRegistry.h"
    340 
    341 static SkImageDecoder* Factory(SkStream* stream) {
    342     char buf[GIF_STAMP_LEN];
    343     if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
    344         if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
    345                 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
    346                 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
    347             return SkNEW(SkGIFImageDecoder);
    348         }
    349     }
    350     return NULL;
    351 }
    352 
    353 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
    354