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 "SkMovie.h"
     11 #include "SkColor.h"
     12 #include "SkColorPriv.h"
     13 #include "SkStream.h"
     14 #include "SkTemplates.h"
     15 #include "SkUtils.h"
     16 
     17 #include "gif_lib.h"
     18 
     19 #if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
     20 #define DGifCloseFile(a, b) DGifCloseFile(a)
     21 #endif
     22 
     23 class SkGIFMovie : public SkMovie {
     24 public:
     25     SkGIFMovie(SkStream* stream);
     26     virtual ~SkGIFMovie();
     27 
     28 protected:
     29     virtual bool onGetInfo(Info*);
     30     virtual bool onSetTime(SkMSec);
     31     virtual bool onGetBitmap(SkBitmap*);
     32 
     33 private:
     34     GifFileType* fGIF;
     35     int fCurrIndex;
     36     int fLastDrawIndex;
     37     SkBitmap fBackup;
     38 };
     39 
     40 static int Decode(GifFileType* fileType, GifByteType* out, int size) {
     41     SkStream* stream = (SkStream*) fileType->UserData;
     42     return (int) stream->read(out, size);
     43 }
     44 
     45 SkGIFMovie::SkGIFMovie(SkStream* stream)
     46 {
     47 #if GIFLIB_MAJOR < 5
     48     fGIF = DGifOpen( stream, Decode );
     49 #else
     50     fGIF = DGifOpen( stream, Decode, NULL );
     51 #endif
     52     if (NULL == fGIF)
     53         return;
     54 
     55     if (DGifSlurp(fGIF) != GIF_OK)
     56     {
     57         DGifCloseFile(fGIF, NULL);
     58         fGIF = NULL;
     59     }
     60     fCurrIndex = -1;
     61     fLastDrawIndex = -1;
     62 }
     63 
     64 SkGIFMovie::~SkGIFMovie()
     65 {
     66     if (fGIF)
     67         DGifCloseFile(fGIF, NULL);
     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             SkASSERT(image->ExtensionBlocks[j].ByteCount >= 4);
     77             const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
     78             return ((b[2] << 8) | b[1]) * 10;
     79         }
     80     }
     81     return 0;
     82 }
     83 
     84 bool SkGIFMovie::onGetInfo(Info* info)
     85 {
     86     if (NULL == fGIF)
     87         return false;
     88 
     89     SkMSec dur = 0;
     90     for (int i = 0; i < fGIF->ImageCount; i++)
     91         dur += savedimage_duration(&fGIF->SavedImages[i]);
     92 
     93     info->fDuration = dur;
     94     info->fWidth = fGIF->SWidth;
     95     info->fHeight = fGIF->SHeight;
     96     info->fIsOpaque = false;    // how to compute?
     97     return true;
     98 }
     99 
    100 bool SkGIFMovie::onSetTime(SkMSec time)
    101 {
    102     if (NULL == fGIF)
    103         return false;
    104 
    105     SkMSec dur = 0;
    106     for (int i = 0; i < fGIF->ImageCount; i++)
    107     {
    108         dur += savedimage_duration(&fGIF->SavedImages[i]);
    109         if (dur >= time)
    110         {
    111             fCurrIndex = i;
    112             return fLastDrawIndex != fCurrIndex;
    113         }
    114     }
    115     fCurrIndex = fGIF->ImageCount - 1;
    116     return true;
    117 }
    118 
    119 static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap,
    120                      int transparent, int width)
    121 {
    122     for (; width > 0; width--, src++, dst++) {
    123         if (*src != transparent) {
    124             const GifColorType& col = cmap->Colors[*src];
    125             *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
    126         }
    127     }
    128 }
    129 
    130 #if GIFLIB_MAJOR < 5
    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 #endif
    178 
    179 static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
    180                        int transparent)
    181 {
    182     int width = bm->width();
    183     int height = bm->height();
    184     const unsigned char* src = (unsigned char*)frame->RasterBits;
    185     uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
    186     GifWord copyWidth = frame->ImageDesc.Width;
    187     if (frame->ImageDesc.Left + copyWidth > width) {
    188         copyWidth = width - frame->ImageDesc.Left;
    189     }
    190 
    191     GifWord copyHeight = frame->ImageDesc.Height;
    192     if (frame->ImageDesc.Top + copyHeight > height) {
    193         copyHeight = height - frame->ImageDesc.Top;
    194     }
    195 
    196     for (; copyHeight > 0; copyHeight--) {
    197         copyLine(dst, src, cmap, transparent, copyWidth);
    198         src += frame->ImageDesc.Width;
    199         dst += width;
    200     }
    201 }
    202 
    203 static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
    204                      uint32_t col)
    205 {
    206     int bmWidth = bm->width();
    207     int bmHeight = bm->height();
    208     uint32_t* dst = bm->getAddr32(left, top);
    209     GifWord copyWidth = width;
    210     if (left + copyWidth > bmWidth) {
    211         copyWidth = bmWidth - left;
    212     }
    213 
    214     GifWord copyHeight = height;
    215     if (top + copyHeight > bmHeight) {
    216         copyHeight = bmHeight - top;
    217     }
    218 
    219     for (; copyHeight > 0; copyHeight--) {
    220         sk_memset32(dst, col, copyWidth);
    221         dst += bmWidth;
    222     }
    223 }
    224 
    225 static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
    226 {
    227     int transparent = -1;
    228 
    229     for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
    230         ExtensionBlock* eb = frame->ExtensionBlocks + i;
    231         if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
    232             eb->ByteCount == 4) {
    233             bool has_transparency = ((eb->Bytes[0] & 1) == 1);
    234             if (has_transparency) {
    235                 transparent = (unsigned char)eb->Bytes[3];
    236             }
    237         }
    238     }
    239 
    240     if (frame->ImageDesc.ColorMap != NULL) {
    241         // use local color table
    242         cmap = frame->ImageDesc.ColorMap;
    243     }
    244 
    245     if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
    246         SkDEBUGFAIL("bad colortable setup");
    247         return;
    248     }
    249 
    250 #if GIFLIB_MAJOR < 5
    251     // before GIFLIB 5, de-interlacing wasn't done by library at load time
    252     if (frame->ImageDesc.Interlace) {
    253         blitInterlace(bm, frame, cmap, transparent);
    254         return;
    255     }
    256 #endif
    257 
    258     blitNormal(bm, frame, cmap, transparent);
    259 }
    260 
    261 static bool checkIfWillBeCleared(const SavedImage* frame)
    262 {
    263     for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
    264         ExtensionBlock* eb = frame->ExtensionBlocks + i;
    265         if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
    266             eb->ByteCount == 4) {
    267             // check disposal method
    268             int disposal = ((eb->Bytes[0] >> 2) & 7);
    269             if (disposal == 2 || disposal == 3) {
    270                 return true;
    271             }
    272         }
    273     }
    274     return false;
    275 }
    276 
    277 static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
    278 {
    279     *trans = false;
    280     *disposal = 0;
    281     for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
    282         ExtensionBlock* eb = frame->ExtensionBlocks + i;
    283         if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
    284             eb->ByteCount == 4) {
    285             *trans = ((eb->Bytes[0] & 1) == 1);
    286             *disposal = ((eb->Bytes[0] >> 2) & 7);
    287         }
    288     }
    289 }
    290 
    291 // return true if area of 'target' is completely covers area of 'covered'
    292 static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
    293 {
    294     if (target->ImageDesc.Left <= covered->ImageDesc.Left
    295         && covered->ImageDesc.Left + covered->ImageDesc.Width <=
    296                target->ImageDesc.Left + target->ImageDesc.Width
    297         && target->ImageDesc.Top <= covered->ImageDesc.Top
    298         && covered->ImageDesc.Top + covered->ImageDesc.Height <=
    299                target->ImageDesc.Top + target->ImageDesc.Height) {
    300         return true;
    301     }
    302     return false;
    303 }
    304 
    305 static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
    306                                  SkBitmap* backup, SkColor color)
    307 {
    308     // We can skip disposal process if next frame is not transparent
    309     // and completely covers current area
    310     bool curTrans;
    311     int curDisposal;
    312     getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
    313     bool nextTrans;
    314     int nextDisposal;
    315     getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
    316     if ((curDisposal == 2 || curDisposal == 3)
    317         && (nextTrans || !checkIfCover(next, cur))) {
    318         switch (curDisposal) {
    319         // restore to background color
    320         // -> 'background' means background under this image.
    321         case 2:
    322             fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
    323                      cur->ImageDesc.Width, cur->ImageDesc.Height,
    324                      color);
    325             break;
    326 
    327         // restore to previous
    328         case 3:
    329             bm->swap(*backup);
    330             break;
    331         }
    332     }
    333 
    334     // Save current image if next frame's disposal method == 3
    335     if (nextDisposal == 3) {
    336         const uint32_t* src = bm->getAddr32(0, 0);
    337         uint32_t* dst = backup->getAddr32(0, 0);
    338         int cnt = bm->width() * bm->height();
    339         memcpy(dst, src, cnt*sizeof(uint32_t));
    340     }
    341 }
    342 
    343 bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
    344 {
    345     const GifFileType* gif = fGIF;
    346     if (NULL == gif)
    347         return false;
    348 
    349     if (gif->ImageCount < 1) {
    350         return false;
    351     }
    352 
    353     const int width = gif->SWidth;
    354     const int height = gif->SHeight;
    355     if (width <= 0 || height <= 0) {
    356         return false;
    357     }
    358 
    359     // no need to draw
    360     if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
    361         return true;
    362     }
    363 
    364     int startIndex = fLastDrawIndex + 1;
    365     if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
    366         // first time
    367 
    368         startIndex = 0;
    369 
    370         // create bitmap
    371         if (!bm->tryAllocN32Pixels(width, height)) {
    372             return false;
    373         }
    374         // create bitmap for backup
    375         if (!fBackup.tryAllocN32Pixels(width, height)) {
    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(SkStreamRewindable* 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*(*)(SkStreamRewindable*)> gReg(Factory);
    452