Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright 2006 The Android Open Source Project
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 
      9 #include "Movie.h"
     10 #include "SkColor.h"
     11 #include "SkColorPriv.h"
     12 #include "SkStream.h"
     13 #include "SkTemplates.h"
     14 #include "SkUtils.h"
     15 
     16 #include "gif_lib.h"
     17 
     18 #if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
     19 #define DGifCloseFile(a, b) DGifCloseFile(a)
     20 #endif
     21 
     22 class GIFMovie : public Movie {
     23 public:
     24     GIFMovie(SkStream* stream);
     25     virtual ~GIFMovie();
     26 
     27 protected:
     28     virtual bool onGetInfo(Info*);
     29     virtual bool onSetTime(SkMSec);
     30     virtual bool onGetBitmap(SkBitmap*);
     31 
     32 private:
     33     GifFileType* fGIF;
     34     int fCurrIndex;
     35     int fLastDrawIndex;
     36     SkBitmap fBackup;
     37     SkColor fPaintingColor;
     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 GIFMovie::GIFMovie(SkStream* stream)
     46 {
     47 #if GIFLIB_MAJOR < 5
     48     fGIF = DGifOpen( stream, Decode );
     49 #else
     50     fGIF = DGifOpen( stream, Decode, nullptr );
     51 #endif
     52     if (nullptr == fGIF)
     53         return;
     54 
     55     if (DGifSlurp(fGIF) != GIF_OK)
     56     {
     57         DGifCloseFile(fGIF, nullptr);
     58         fGIF = nullptr;
     59     }
     60     fCurrIndex = -1;
     61     fLastDrawIndex = -1;
     62     fPaintingColor = SkPackARGB32(0, 0, 0, 0);
     63 }
     64 
     65 GIFMovie::~GIFMovie()
     66 {
     67     if (fGIF)
     68         DGifCloseFile(fGIF, nullptr);
     69 }
     70 
     71 static SkMSec savedimage_duration(const SavedImage* image)
     72 {
     73     for (int j = 0; j < image->ExtensionBlockCount; j++)
     74     {
     75         if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
     76         {
     77             SkASSERT(image->ExtensionBlocks[j].ByteCount >= 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 GIFMovie::onGetInfo(Info* info)
     86 {
     87     if (nullptr == 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 GIFMovie::onSetTime(SkMSec time)
    102 {
    103     if (nullptr == 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 && *src < cmap->ColorCount) {
    125             const GifColorType& col = cmap->Colors[*src];
    126             *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
    127         }
    128     }
    129 }
    130 
    131 #if GIFLIB_MAJOR < 5
    132 static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src,
    133                                const ColorMapObject* cmap, int transparent, int copyWidth,
    134                                int copyHeight, const GifImageDesc& imageDesc, int rowStep,
    135                                int startRow)
    136 {
    137     int row;
    138     // every 'rowStep'th row, starting with row 'startRow'
    139     for (row = startRow; row < copyHeight; row += rowStep) {
    140         uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row);
    141         copyLine(dst, src, cmap, transparent, copyWidth);
    142         src += imageDesc.Width;
    143     }
    144 
    145     // pad for rest height
    146     src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep);
    147 }
    148 
    149 static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
    150                           int transparent)
    151 {
    152     int width = bm->width();
    153     int height = bm->height();
    154     GifWord copyWidth = frame->ImageDesc.Width;
    155     if (frame->ImageDesc.Left + copyWidth > width) {
    156         copyWidth = width - frame->ImageDesc.Left;
    157     }
    158 
    159     GifWord copyHeight = frame->ImageDesc.Height;
    160     if (frame->ImageDesc.Top + copyHeight > height) {
    161         copyHeight = height - frame->ImageDesc.Top;
    162     }
    163 
    164     // deinterlace
    165     const unsigned char* src = (unsigned char*)frame->RasterBits;
    166 
    167     // group 1 - every 8th row, starting with row 0
    168     copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0);
    169 
    170     // group 2 - every 8th row, starting with row 4
    171     copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4);
    172 
    173     // group 3 - every 4th row, starting with row 2
    174     copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2);
    175 
    176     copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1);
    177 }
    178 #endif
    179 
    180 static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
    181                        int transparent)
    182 {
    183     int width = bm->width();
    184     int height = bm->height();
    185     const unsigned char* src = (unsigned char*)frame->RasterBits;
    186     uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
    187     GifWord copyWidth = frame->ImageDesc.Width;
    188     if (frame->ImageDesc.Left + copyWidth > width) {
    189         copyWidth = width - frame->ImageDesc.Left;
    190     }
    191 
    192     GifWord copyHeight = frame->ImageDesc.Height;
    193     if (frame->ImageDesc.Top + copyHeight > height) {
    194         copyHeight = height - frame->ImageDesc.Top;
    195     }
    196 
    197     for (; copyHeight > 0; copyHeight--) {
    198         copyLine(dst, src, cmap, transparent, copyWidth);
    199         src += frame->ImageDesc.Width;
    200         dst += width;
    201     }
    202 }
    203 
    204 static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
    205                      uint32_t col)
    206 {
    207     int bmWidth = bm->width();
    208     int bmHeight = bm->height();
    209     uint32_t* dst = bm->getAddr32(left, top);
    210     GifWord copyWidth = width;
    211     if (left + copyWidth > bmWidth) {
    212         copyWidth = bmWidth - left;
    213     }
    214 
    215     GifWord copyHeight = height;
    216     if (top + copyHeight > bmHeight) {
    217         copyHeight = bmHeight - top;
    218     }
    219 
    220     for (; copyHeight > 0; copyHeight--) {
    221         sk_memset32(dst, col, copyWidth);
    222         dst += bmWidth;
    223     }
    224 }
    225 
    226 static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
    227 {
    228     int transparent = -1;
    229 
    230     for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
    231         ExtensionBlock* eb = frame->ExtensionBlocks + i;
    232         if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
    233             eb->ByteCount == 4) {
    234             bool has_transparency = ((eb->Bytes[0] & 1) == 1);
    235             if (has_transparency) {
    236                 transparent = (unsigned char)eb->Bytes[3];
    237             }
    238         }
    239     }
    240 
    241     if (frame->ImageDesc.ColorMap != nullptr) {
    242         // use local color table
    243         cmap = frame->ImageDesc.ColorMap;
    244     }
    245 
    246     if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
    247         SkDEBUGFAIL("bad colortable setup");
    248         return;
    249     }
    250 
    251 #if GIFLIB_MAJOR < 5
    252     // before GIFLIB 5, de-interlacing wasn't done by library at load time
    253     if (frame->ImageDesc.Interlace) {
    254         blitInterlace(bm, frame, cmap, transparent);
    255         return;
    256     }
    257 #endif
    258 
    259     blitNormal(bm, frame, cmap, transparent);
    260 }
    261 
    262 static bool checkIfWillBeCleared(const SavedImage* frame)
    263 {
    264     for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
    265         ExtensionBlock* eb = frame->ExtensionBlocks + i;
    266         if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
    267             eb->ByteCount == 4) {
    268             // check disposal method
    269             int disposal = ((eb->Bytes[0] >> 2) & 7);
    270             if (disposal == 2 || disposal == 3) {
    271                 return true;
    272             }
    273         }
    274     }
    275     return false;
    276 }
    277 
    278 static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
    279 {
    280     *trans = false;
    281     *disposal = 0;
    282     for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
    283         ExtensionBlock* eb = frame->ExtensionBlocks + i;
    284         if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
    285             eb->ByteCount == 4) {
    286             *trans = ((eb->Bytes[0] & 1) == 1);
    287             *disposal = ((eb->Bytes[0] >> 2) & 7);
    288         }
    289     }
    290 }
    291 
    292 // return true if area of 'target' is completely covers area of 'covered'
    293 static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
    294 {
    295     if (target->ImageDesc.Left <= covered->ImageDesc.Left
    296         && covered->ImageDesc.Left + covered->ImageDesc.Width <=
    297                target->ImageDesc.Left + target->ImageDesc.Width
    298         && target->ImageDesc.Top <= covered->ImageDesc.Top
    299         && covered->ImageDesc.Top + covered->ImageDesc.Height <=
    300                target->ImageDesc.Top + target->ImageDesc.Height) {
    301         return true;
    302     }
    303     return false;
    304 }
    305 
    306 static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
    307                                  SkBitmap* backup, SkColor color)
    308 {
    309     // We can skip disposal process if next frame is not transparent
    310     // and completely covers current area
    311     bool curTrans;
    312     int curDisposal;
    313     getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
    314     bool nextTrans;
    315     int nextDisposal;
    316     getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
    317     if ((curDisposal == 2 || curDisposal == 3)
    318         && (nextTrans || !checkIfCover(next, cur))) {
    319         switch (curDisposal) {
    320         // restore to background color
    321         // -> 'background' means background under this image.
    322         case 2:
    323             fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
    324                      cur->ImageDesc.Width, cur->ImageDesc.Height,
    325                      color);
    326             break;
    327 
    328         // restore to previous
    329         case 3:
    330             bm->swap(*backup);
    331             break;
    332         }
    333     }
    334 
    335     // Save current image if next frame's disposal method == 3
    336     if (nextDisposal == 3) {
    337         const uint32_t* src = bm->getAddr32(0, 0);
    338         uint32_t* dst = backup->getAddr32(0, 0);
    339         int cnt = bm->width() * bm->height();
    340         memcpy(dst, src, cnt*sizeof(uint32_t));
    341     }
    342 }
    343 
    344 bool GIFMovie::onGetBitmap(SkBitmap* bm)
    345 {
    346     const GifFileType* gif = fGIF;
    347     if (nullptr == gif)
    348         return false;
    349 
    350     if (gif->ImageCount < 1) {
    351         return false;
    352     }
    353 
    354     const int width = gif->SWidth;
    355     const int height = gif->SHeight;
    356     if (width <= 0 || height <= 0) {
    357         return false;
    358     }
    359 
    360     // no need to draw
    361     if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
    362         return true;
    363     }
    364 
    365     int startIndex = fLastDrawIndex + 1;
    366     if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
    367         // first time
    368 
    369         startIndex = 0;
    370 
    371         // create bitmap
    372         if (!bm->tryAllocN32Pixels(width, height)) {
    373             return false;
    374         }
    375         // create bitmap for backup
    376         if (!fBackup.tryAllocN32Pixels(width, height)) {
    377             return false;
    378         }
    379     } else if (startIndex > fCurrIndex) {
    380         // rewind to 1st frame for repeat
    381         startIndex = 0;
    382     }
    383 
    384     int lastIndex = fCurrIndex;
    385     if (lastIndex < 0) {
    386         // first time
    387         lastIndex = 0;
    388     } else if (lastIndex > fGIF->ImageCount - 1) {
    389         // this block must not be reached.
    390         lastIndex = fGIF->ImageCount - 1;
    391     }
    392 
    393     SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
    394     if (gif->SColorMap != nullptr && gif->SBackGroundColor < gif->SColorMap->ColorCount) {
    395         const GifColorType& col = gif->SColorMap->Colors[gif->SBackGroundColor];
    396         bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
    397     }
    398 
    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 != nullptr) {
    407                 fPaintingColor = bgColor;
    408             } else {
    409                 fPaintingColor = SkColorSetARGB(0, 0, 0, 0);
    410             }
    411 
    412             bm->eraseColor(fPaintingColor);
    413             fBackup.eraseColor(fPaintingColor);
    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, fPaintingColor);
    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 Movie* Movie::DecodeStream(SkStreamRewindable* stream) {
    436     char buf[GIF_STAMP_LEN];
    437     if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
    438         if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
    439                 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
    440                 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
    441             // must rewind here, since our construct wants to re-read the data
    442             stream->rewind();
    443             return new GIFMovie(stream);
    444         }
    445     }
    446     return nullptr;
    447 }
    448