Home | History | Annotate | Download | only in images
      1 /* libs/graphics/images/SkImageDecoder_libgif.cpp
      2 **
      3 ** Copyright 2006, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 #include "SkImageDecoder.h"
     19 #include "SkColor.h"
     20 #include "SkColorPriv.h"
     21 #include "SkStream.h"
     22 #include "SkTemplates.h"
     23 #include "SkPackBits.h"
     24 
     25 #include "gif_lib.h"
     26 
     27 class SkGIFImageDecoder : public SkImageDecoder {
     28 public:
     29     virtual Format getFormat() const {
     30         return kGIF_Format;
     31     }
     32 
     33 protected:
     34     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
     35 };
     36 
     37 static const uint8_t gStartingIterlaceYValue[] = {
     38     0, 4, 2, 1
     39 };
     40 static const uint8_t gDeltaIterlaceYValue[] = {
     41     8, 8, 4, 2
     42 };
     43 
     44 /*  Implement the GIF interlace algorithm in an iterator.
     45     1) grab every 8th line beginning at 0
     46     2) grab every 8th line beginning at 4
     47     3) grab every 4th line beginning at 2
     48     4) grab every 2nd line beginning at 1
     49 */
     50 class GifInterlaceIter {
     51 public:
     52     GifInterlaceIter(int height) : fHeight(height) {
     53         fStartYPtr = gStartingIterlaceYValue;
     54         fDeltaYPtr = gDeltaIterlaceYValue;
     55 
     56         fCurrY = *fStartYPtr++;
     57         fDeltaY = *fDeltaYPtr++;
     58     }
     59 
     60     int currY() const {
     61         SkASSERT(fStartYPtr);
     62         SkASSERT(fDeltaYPtr);
     63         return fCurrY;
     64     }
     65 
     66     void next() {
     67         SkASSERT(fStartYPtr);
     68         SkASSERT(fDeltaYPtr);
     69 
     70         int y = fCurrY + fDeltaY;
     71         // We went from an if statement to a while loop so that we iterate
     72         // through fStartYPtr until a valid row is found. This is so that images
     73         // that are smaller than 5x5 will not trash memory.
     74         while (y >= fHeight) {
     75             if (gStartingIterlaceYValue +
     76                     SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) {
     77                 // we done
     78                 SkDEBUGCODE(fStartYPtr = NULL;)
     79                 SkDEBUGCODE(fDeltaYPtr = NULL;)
     80                 y = 0;
     81             } else {
     82                 y = *fStartYPtr++;
     83                 fDeltaY = *fDeltaYPtr++;
     84             }
     85         }
     86         fCurrY = y;
     87     }
     88 
     89 private:
     90     const int fHeight;
     91     int fCurrY;
     92     int fDeltaY;
     93     const uint8_t* fStartYPtr;
     94     const uint8_t* fDeltaYPtr;
     95 };
     96 
     97 ///////////////////////////////////////////////////////////////////////////////
     98 
     99 //#define GIF_STAMP       "GIF"    /* First chars in file - GIF stamp. */
    100 //#define GIF_STAMP_LEN   (sizeof(GIF_STAMP) - 1)
    101 
    102 static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
    103                               int size) {
    104     SkStream* stream = (SkStream*) fileType->UserData;
    105     return (int) stream->read(out, size);
    106 }
    107 
    108 void CheckFreeExtension(SavedImage* Image) {
    109     if (Image->ExtensionBlocks) {
    110         FreeExtension(Image);
    111     }
    112 }
    113 
    114 // return NULL on failure
    115 static const ColorMapObject* find_colormap(const GifFileType* gif) {
    116     const ColorMapObject* cmap = gif->Image.ColorMap;
    117     if (NULL == cmap) {
    118         cmap = gif->SColorMap;
    119     }
    120 
    121     if (NULL == cmap) {
    122         // no colormap found
    123         return NULL;
    124     }
    125     // some sanity checks
    126     if (cmap && ((unsigned)cmap->ColorCount > 256 ||
    127                  cmap->ColorCount != (1 << cmap->BitsPerPixel))) {
    128         cmap = NULL;
    129     }
    130     return cmap;
    131 }
    132 
    133 // return -1 if not found (i.e. we're completely opaque)
    134 static int find_transpIndex(const SavedImage& image, int colorCount) {
    135     int transpIndex = -1;
    136     for (int i = 0; i < image.ExtensionBlockCount; ++i) {
    137         const ExtensionBlock* eb = image.ExtensionBlocks + i;
    138         if (eb->Function == 0xF9 && eb->ByteCount == 4) {
    139             if (eb->Bytes[0] & 1) {
    140                 transpIndex = (unsigned char)eb->Bytes[3];
    141                 // check for valid transpIndex
    142                 if (transpIndex >= colorCount) {
    143                     transpIndex = -1;
    144                 }
    145                 break;
    146             }
    147         }
    148     }
    149     return transpIndex;
    150 }
    151 
    152 static bool error_return(GifFileType* gif, const SkBitmap& bm,
    153                          const char msg[]) {
    154 #if 0
    155     SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n",
    156              msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable());
    157 #endif
    158     return false;
    159 }
    160 
    161 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
    162     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
    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     int transpIndex = -1;   // -1 means we don't have it (yet)
    178 
    179     do {
    180         if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {
    181             return error_return(gif, *bm, "DGifGetRecordType");
    182         }
    183 
    184         switch (recType) {
    185         case IMAGE_DESC_RECORD_TYPE: {
    186             if (DGifGetImageDesc(gif) == GIF_ERROR) {
    187                 return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE");
    188             }
    189 
    190             if (gif->ImageCount < 1) {    // sanity check
    191                 return error_return(gif, *bm, "ImageCount < 1");
    192             }
    193 
    194             width = gif->SWidth;
    195             height = gif->SHeight;
    196             if (width <= 0 || height <= 0 ||
    197                 !this->chooseFromOneChoice(SkBitmap::kIndex8_Config,
    198                                            width, height)) {
    199                 return error_return(gif, *bm, "chooseFromOneChoice");
    200             }
    201 
    202             bm->setConfig(SkBitmap::kIndex8_Config, width, height);
    203             if (SkImageDecoder::kDecodeBounds_Mode == mode)
    204                 return true;
    205 
    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