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 "SkMovie.h"
     19 #include "SkColor.h"
     20 #include "SkColorPriv.h"
     21 #include "SkStream.h"
     22 #include "SkTemplates.h"
     23 #include "SkUtils.h"
     24 
     25 #include "gif_lib.h"
     26 
     27 class SkGIFMovie : public SkMovie {
     28 public:
     29     SkGIFMovie(SkStream* stream);
     30     virtual ~SkGIFMovie();
     31 
     32 protected:
     33     virtual bool onGetInfo(Info*);
     34     virtual bool onSetTime(SkMSec);
     35     virtual bool onGetBitmap(SkBitmap*);
     36 
     37 private:
     38     GifFileType* fGIF;
     39     int fCurrIndex;
     40     int fLastDrawIndex;
     41     SkBitmap fBackup;
     42 };
     43 
     44 static int Decode(GifFileType* fileType, GifByteType* out, int size) {
     45     SkStream* stream = (SkStream*) fileType->UserData;
     46     return (int) stream->read(out, size);
     47 }
     48 
     49 SkGIFMovie::SkGIFMovie(SkStream* stream)
     50 {
     51     fGIF = DGifOpen( stream, Decode );
     52     if (NULL == fGIF)
     53         return;
     54 
     55     if (DGifSlurp(fGIF) != GIF_OK)
     56     {
     57         DGifCloseFile(fGIF);
     58         fGIF = NULL;
     59     }
     60     fCurrIndex = -1;
     61     fLastDrawIndex = -1;
     62 }
     63 
     64 SkGIFMovie::~SkGIFMovie()
     65 {
     66     if (fGIF)
     67         DGifCloseFile(fGIF);
     68 }
     69 
     70 static SkMSec savedimage_duration(const SavedImage* image)
     71 {
     72     for (int j = 0; j < image->ExtensionBlockCount; j++)
     73     {
     74         if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
     75         {
     76             int size = image->ExtensionBlocks[j].ByteCount;
     77             SkASSERT(size >= 4);
     78             const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
     79             return ((b[2] << 8) | b[1]) * 10;
     80         }
     81     }
     82     return 0;
     83 }
     84 
     85 bool SkGIFMovie::onGetInfo(Info* info)
     86 {
     87     if (NULL == fGIF)
     88         return false;
     89 
     90     SkMSec dur = 0;
     91     for (int i = 0; i < fGIF->ImageCount; i++)
     92         dur += savedimage_duration(&fGIF->SavedImages[i]);
     93 
     94     info->fDuration = dur;
     95     info->fWidth = fGIF->SWidth;
     96     info->fHeight = fGIF->SHeight;
     97     info->fIsOpaque = false;    // how to compute?
     98     return true;
     99 }
    100 
    101 bool SkGIFMovie::onSetTime(SkMSec time)
    102 {
    103     if (NULL == fGIF)
    104         return false;
    105 
    106     SkMSec dur = 0;
    107     for (int i = 0; i < fGIF->ImageCount; i++)
    108     {
    109         dur += savedimage_duration(&fGIF->SavedImages[i]);
    110         if (dur >= time)
    111         {
    112             fCurrIndex = i;
    113             return fLastDrawIndex != fCurrIndex;
    114         }
    115     }
    116     fCurrIndex = fGIF->ImageCount - 1;
    117     return true;
    118 }
    119 
    120 static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap,
    121                      int transparent, int width)
    122 {
    123     for (; width > 0; width--, src++, dst++) {
    124         if (*src != transparent) {
    125             const GifColorType& col = cmap->Colors[*src];
    126             *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
    127         }
    128     }
    129 }
    130 
    131 static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src,
    132                                const ColorMapObject* cmap, int transparent, int copyWidth,
    133                                int copyHeight, const GifImageDesc& imageDesc, int rowStep,
    134                                int startRow)
    135 {
    136     int row;
    137     // every 'rowStep'th row, starting with row 'startRow'
    138     for (row = startRow; row < copyHeight; row += rowStep) {
    139         uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row);
    140         copyLine(dst, src, cmap, transparent, copyWidth);
    141         src += imageDesc.Width;
    142     }
    143 
    144     // pad for rest height
    145     src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep);
    146 }
    147 
    148 static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
    149                           int transparent)
    150 {
    151     int width = bm->width();
    152     int height = bm->height();
    153     GifWord copyWidth = frame->ImageDesc.Width;
    154     if (frame->ImageDesc.Left + copyWidth > width) {
    155         copyWidth = width - frame->ImageDesc.Left;
    156     }
    157 
    158     GifWord copyHeight = frame->ImageDesc.Height;
    159     if (frame->ImageDesc.Top + copyHeight > height) {
    160         copyHeight = height - frame->ImageDesc.Top;
    161     }
    162 
    163     // deinterlace
    164     const unsigned char* src = (unsigned char*)frame->RasterBits;
    165 
    166     // group 1 - every 8th row, starting with row 0
    167     copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0);
    168 
    169     // group 2 - every 8th row, starting with row 4
    170     copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4);
    171 
    172     // group 3 - every 4th row, starting with row 2
    173     copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2);
    174 
    175     copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1);
    176 }
    177 
    178 static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
    179                        int transparent)
    180 {
    181     int width = bm->width();
    182     int height = bm->height();
    183     const unsigned char* src = (unsigned char*)frame->RasterBits;
    184     uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
    185     GifWord copyWidth = frame->ImageDesc.Width;
    186     if (frame->ImageDesc.Left + copyWidth > width) {
    187         copyWidth = width - frame->ImageDesc.Left;
    188     }
    189 
    190     GifWord copyHeight = frame->ImageDesc.Height;
    191     if (frame->ImageDesc.Top + copyHeight > height) {
    192         copyHeight = height - frame->ImageDesc.Top;
    193     }
    194 
    195     int srcPad, dstPad;
    196     dstPad = width - copyWidth;
    197     srcPad = frame->ImageDesc.Width - copyWidth;
    198     for (; copyHeight > 0; copyHeight--) {
    199         copyLine(dst, src, cmap, transparent, copyWidth);
    200         src += frame->ImageDesc.Width;
    201         dst += width;
    202     }
    203 }
    204 
    205 static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
    206                      uint32_t col)
    207 {
    208     int bmWidth = bm->width();
    209     int bmHeight = bm->height();
    210     uint32_t* dst = bm->getAddr32(left, top);
    211     GifWord copyWidth = width;
    212     if (left + copyWidth > bmWidth) {
    213         copyWidth = bmWidth - left;
    214     }
    215 
    216     GifWord copyHeight = height;
    217     if (top + copyHeight > bmHeight) {
    218         copyHeight = bmHeight - top;
    219     }
    220 
    221     for (; copyHeight > 0; copyHeight--) {
    222         sk_memset32(dst, col, copyWidth);
    223         dst += bmWidth;
    224     }
    225 }
    226 
    227 static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
    228 {
    229     int transparent = -1;
    230 
    231     for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
    232         ExtensionBlock* eb = frame->ExtensionBlocks + i;
    233         if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
    234             eb->ByteCount == 4) {
    235             bool has_transparency = ((eb->Bytes[0] & 1) == 1);
    236             if (has_transparency) {
    237                 transparent = (unsigned char)eb->Bytes[3];
    238             }
    239         }
    240     }
    241 
    242     if (frame->ImageDesc.ColorMap != NULL) {
    243         // use local color table
    244         cmap = frame->ImageDesc.ColorMap;
    245     }
    246 
    247     if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
    248         SkASSERT(!"bad colortable setup");
    249         return;
    250     }
    251 
    252     if (frame->ImageDesc.Interlace) {
    253         blitInterlace(bm, frame, cmap, transparent);
    254     } else {
    255         blitNormal(bm, frame, cmap, transparent);
    256     }
    257 }
    258 
    259 static bool checkIfWillBeCleared(const SavedImage* frame)
    260 {
    261     for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
    262         ExtensionBlock* eb = frame->ExtensionBlocks + i;
    263         if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
    264             eb->ByteCount == 4) {
    265             // check disposal method
    266             int disposal = ((eb->Bytes[0] >> 2) & 7);
    267             if (disposal == 2 || disposal == 3) {
    268                 return true;
    269             }
    270         }
    271     }
    272     return false;
    273 }
    274 
    275 static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
    276 {
    277     *trans = false;
    278     *disposal = 0;
    279     for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
    280         ExtensionBlock* eb = frame->ExtensionBlocks + i;
    281         if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
    282             eb->ByteCount == 4) {
    283             *trans = ((eb->Bytes[0] & 1) == 1);
    284             *disposal = ((eb->Bytes[0] >> 2) & 7);
    285         }
    286     }
    287 }
    288 
    289 // return true if area of 'target' is completely covers area of 'covered'
    290 static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
    291 {
    292     if (target->ImageDesc.Left <= covered->ImageDesc.Left
    293         && covered->ImageDesc.Left + covered->ImageDesc.Width <=
    294                target->ImageDesc.Left + target->ImageDesc.Width
    295         && target->ImageDesc.Top <= covered->ImageDesc.Top
    296         && covered->ImageDesc.Top + covered->ImageDesc.Height <=
    297                target->ImageDesc.Top + target->ImageDesc.Height) {
    298         return true;
    299     }
    300     return false;
    301 }
    302 
    303 static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
    304                                  SkBitmap* backup, SkColor color)
    305 {
    306     // We can skip disposal process if next frame is not transparent
    307     // and completely covers current area
    308     bool curTrans;
    309     int curDisposal;
    310     getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
    311     bool nextTrans;
    312     int nextDisposal;
    313     getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
    314     if ((curDisposal == 2 || curDisposal == 3)
    315         && (nextTrans || !checkIfCover(next, cur))) {
    316         switch (curDisposal) {
    317         // restore to background color
    318         // -> 'background' means background under this image.
    319         case 2:
    320             fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
    321                      cur->ImageDesc.Width, cur->ImageDesc.Height,
    322                      color);
    323             break;
    324 
    325         // restore to previous
    326         case 3:
    327             bm->swap(*backup);
    328             break;
    329         }
    330     }
    331 
    332     // Save current image if next frame's disposal method == 3
    333     if (nextDisposal == 3) {
    334         const uint32_t* src = bm->getAddr32(0, 0);
    335         uint32_t* dst = backup->getAddr32(0, 0);
    336         int cnt = bm->width() * bm->height();
    337         memcpy(dst, src, cnt*sizeof(uint32_t));
    338     }
    339 }
    340 
    341 bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
    342 {
    343     const GifFileType* gif = fGIF;
    344     if (NULL == gif)
    345         return false;
    346 
    347     if (gif->ImageCount < 1) {
    348         return false;
    349     }
    350 
    351     const int width = gif->SWidth;
    352     const int height = gif->SHeight;
    353     if (width <= 0 || height <= 0) {
    354         return false;
    355     }
    356 
    357     // no need to draw
    358     if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
    359         return true;
    360     }
    361 
    362     int startIndex = fLastDrawIndex + 1;
    363     if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
    364         // first time
    365 
    366         startIndex = 0;
    367 
    368         // create bitmap
    369         bm->setConfig(SkBitmap::kARGB_8888_Config, width, height, 0);
    370         if (!bm->allocPixels(NULL)) {
    371             return false;
    372         }
    373         // create bitmap for backup
    374         fBackup.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0);
    375         if (!fBackup.allocPixels(NULL)) {
    376             return false;
    377         }
    378     } else if (startIndex > fCurrIndex) {
    379         // rewind to 1st frame for repeat
    380         startIndex = 0;
    381     }
    382 
    383     int lastIndex = fCurrIndex;
    384     if (lastIndex < 0) {
    385         // first time
    386         lastIndex = 0;
    387     } else if (lastIndex > fGIF->ImageCount - 1) {
    388         // this block must not be reached.
    389         lastIndex = fGIF->ImageCount - 1;
    390     }
    391 
    392     SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
    393     if (gif->SColorMap != NULL) {
    394         const GifColorType& col = gif->SColorMap->Colors[fGIF->SBackGroundColor];
    395         bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
    396     }
    397 
    398     static SkColor paintingColor = SkPackARGB32(0, 0, 0, 0);
    399     // draw each frames - not intelligent way
    400     for (int i = startIndex; i <= lastIndex; i++) {
    401         const SavedImage* cur = &fGIF->SavedImages[i];
    402         if (i == 0) {
    403             bool trans;
    404             int disposal;
    405             getTransparencyAndDisposalMethod(cur, &trans, &disposal);
    406             if (!trans && gif->SColorMap != NULL) {
    407                 paintingColor = bgColor;
    408             } else {
    409                 paintingColor = SkColorSetARGB(0, 0, 0, 0);
    410             }
    411 
    412             bm->eraseColor(paintingColor);
    413             fBackup.eraseColor(paintingColor);
    414         } else {
    415             // Dispose previous frame before move to next frame.
    416             const SavedImage* prev = &fGIF->SavedImages[i-1];
    417             disposeFrameIfNeeded(bm, prev, cur, &fBackup, paintingColor);
    418         }
    419 
    420         // Draw frame
    421         // We can skip this process if this index is not last and disposal
    422         // method == 2 or method == 3
    423         if (i == lastIndex || !checkIfWillBeCleared(cur)) {
    424             drawFrame(bm, cur, gif->SColorMap);
    425         }
    426     }
    427 
    428     // save index
    429     fLastDrawIndex = lastIndex;
    430     return true;
    431 }
    432 
    433 ///////////////////////////////////////////////////////////////////////////////
    434 
    435 #include "SkTRegistry.h"
    436 
    437 SkMovie* Factory(SkStream* stream) {
    438     char buf[GIF_STAMP_LEN];
    439     if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
    440         if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
    441                 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
    442                 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
    443             // must rewind here, since our construct wants to re-read the data
    444             stream->rewind();
    445             return SkNEW_ARGS(SkGIFMovie, (stream));
    446         }
    447     }
    448     return NULL;
    449 }
    450 
    451 static SkTRegistry<SkMovie*, SkStream*> gReg(Factory);
    452