gfx/skia/trunk/src/images/SkMovie_gif.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/images/SkMovie_gif.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,447 @@
     1.4 +
     1.5 +/*
     1.6 + * Copyright 2006 The Android Open Source Project
     1.7 + *
     1.8 + * Use of this source code is governed by a BSD-style license that can be
     1.9 + * found in the LICENSE file.
    1.10 + */
    1.11 +
    1.12 +
    1.13 +#include "SkMovie.h"
    1.14 +#include "SkColor.h"
    1.15 +#include "SkColorPriv.h"
    1.16 +#include "SkStream.h"
    1.17 +#include "SkTemplates.h"
    1.18 +#include "SkUtils.h"
    1.19 +
    1.20 +#include "gif_lib.h"
    1.21 +
    1.22 +class SkGIFMovie : public SkMovie {
    1.23 +public:
    1.24 +    SkGIFMovie(SkStream* stream);
    1.25 +    virtual ~SkGIFMovie();
    1.26 +
    1.27 +protected:
    1.28 +    virtual bool onGetInfo(Info*);
    1.29 +    virtual bool onSetTime(SkMSec);
    1.30 +    virtual bool onGetBitmap(SkBitmap*);
    1.31 +
    1.32 +private:
    1.33 +    GifFileType* fGIF;
    1.34 +    int fCurrIndex;
    1.35 +    int fLastDrawIndex;
    1.36 +    SkBitmap fBackup;
    1.37 +};
    1.38 +
    1.39 +static int Decode(GifFileType* fileType, GifByteType* out, int size) {
    1.40 +    SkStream* stream = (SkStream*) fileType->UserData;
    1.41 +    return (int) stream->read(out, size);
    1.42 +}
    1.43 +
    1.44 +SkGIFMovie::SkGIFMovie(SkStream* stream)
    1.45 +{
    1.46 +#if GIFLIB_MAJOR < 5
    1.47 +    fGIF = DGifOpen( stream, Decode );
    1.48 +#else
    1.49 +    fGIF = DGifOpen( stream, Decode, NULL );
    1.50 +#endif
    1.51 +    if (NULL == fGIF)
    1.52 +        return;
    1.53 +
    1.54 +    if (DGifSlurp(fGIF) != GIF_OK)
    1.55 +    {
    1.56 +        DGifCloseFile(fGIF);
    1.57 +        fGIF = NULL;
    1.58 +    }
    1.59 +    fCurrIndex = -1;
    1.60 +    fLastDrawIndex = -1;
    1.61 +}
    1.62 +
    1.63 +SkGIFMovie::~SkGIFMovie()
    1.64 +{
    1.65 +    if (fGIF)
    1.66 +        DGifCloseFile(fGIF);
    1.67 +}
    1.68 +
    1.69 +static SkMSec savedimage_duration(const SavedImage* image)
    1.70 +{
    1.71 +    for (int j = 0; j < image->ExtensionBlockCount; j++)
    1.72 +    {
    1.73 +        if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
    1.74 +        {
    1.75 +            SkASSERT(image->ExtensionBlocks[j].ByteCount >= 4);
    1.76 +            const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
    1.77 +            return ((b[2] << 8) | b[1]) * 10;
    1.78 +        }
    1.79 +    }
    1.80 +    return 0;
    1.81 +}
    1.82 +
    1.83 +bool SkGIFMovie::onGetInfo(Info* info)
    1.84 +{
    1.85 +    if (NULL == fGIF)
    1.86 +        return false;
    1.87 +
    1.88 +    SkMSec dur = 0;
    1.89 +    for (int i = 0; i < fGIF->ImageCount; i++)
    1.90 +        dur += savedimage_duration(&fGIF->SavedImages[i]);
    1.91 +
    1.92 +    info->fDuration = dur;
    1.93 +    info->fWidth = fGIF->SWidth;
    1.94 +    info->fHeight = fGIF->SHeight;
    1.95 +    info->fIsOpaque = false;    // how to compute?
    1.96 +    return true;
    1.97 +}
    1.98 +
    1.99 +bool SkGIFMovie::onSetTime(SkMSec time)
   1.100 +{
   1.101 +    if (NULL == fGIF)
   1.102 +        return false;
   1.103 +
   1.104 +    SkMSec dur = 0;
   1.105 +    for (int i = 0; i < fGIF->ImageCount; i++)
   1.106 +    {
   1.107 +        dur += savedimage_duration(&fGIF->SavedImages[i]);
   1.108 +        if (dur >= time)
   1.109 +        {
   1.110 +            fCurrIndex = i;
   1.111 +            return fLastDrawIndex != fCurrIndex;
   1.112 +        }
   1.113 +    }
   1.114 +    fCurrIndex = fGIF->ImageCount - 1;
   1.115 +    return true;
   1.116 +}
   1.117 +
   1.118 +static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap,
   1.119 +                     int transparent, int width)
   1.120 +{
   1.121 +    for (; width > 0; width--, src++, dst++) {
   1.122 +        if (*src != transparent) {
   1.123 +            const GifColorType& col = cmap->Colors[*src];
   1.124 +            *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
   1.125 +        }
   1.126 +    }
   1.127 +}
   1.128 +
   1.129 +#if GIFLIB_MAJOR < 5
   1.130 +static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src,
   1.131 +                               const ColorMapObject* cmap, int transparent, int copyWidth,
   1.132 +                               int copyHeight, const GifImageDesc& imageDesc, int rowStep,
   1.133 +                               int startRow)
   1.134 +{
   1.135 +    int row;
   1.136 +    // every 'rowStep'th row, starting with row 'startRow'
   1.137 +    for (row = startRow; row < copyHeight; row += rowStep) {
   1.138 +        uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row);
   1.139 +        copyLine(dst, src, cmap, transparent, copyWidth);
   1.140 +        src += imageDesc.Width;
   1.141 +    }
   1.142 +
   1.143 +    // pad for rest height
   1.144 +    src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep);
   1.145 +}
   1.146 +
   1.147 +static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
   1.148 +                          int transparent)
   1.149 +{
   1.150 +    int width = bm->width();
   1.151 +    int height = bm->height();
   1.152 +    GifWord copyWidth = frame->ImageDesc.Width;
   1.153 +    if (frame->ImageDesc.Left + copyWidth > width) {
   1.154 +        copyWidth = width - frame->ImageDesc.Left;
   1.155 +    }
   1.156 +
   1.157 +    GifWord copyHeight = frame->ImageDesc.Height;
   1.158 +    if (frame->ImageDesc.Top + copyHeight > height) {
   1.159 +        copyHeight = height - frame->ImageDesc.Top;
   1.160 +    }
   1.161 +
   1.162 +    // deinterlace
   1.163 +    const unsigned char* src = (unsigned char*)frame->RasterBits;
   1.164 +
   1.165 +    // group 1 - every 8th row, starting with row 0
   1.166 +    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0);
   1.167 +
   1.168 +    // group 2 - every 8th row, starting with row 4
   1.169 +    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4);
   1.170 +
   1.171 +    // group 3 - every 4th row, starting with row 2
   1.172 +    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2);
   1.173 +
   1.174 +    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1);
   1.175 +}
   1.176 +#endif
   1.177 +
   1.178 +static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
   1.179 +                       int transparent)
   1.180 +{
   1.181 +    int width = bm->width();
   1.182 +    int height = bm->height();
   1.183 +    const unsigned char* src = (unsigned char*)frame->RasterBits;
   1.184 +    uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
   1.185 +    GifWord copyWidth = frame->ImageDesc.Width;
   1.186 +    if (frame->ImageDesc.Left + copyWidth > width) {
   1.187 +        copyWidth = width - frame->ImageDesc.Left;
   1.188 +    }
   1.189 +
   1.190 +    GifWord copyHeight = frame->ImageDesc.Height;
   1.191 +    if (frame->ImageDesc.Top + copyHeight > height) {
   1.192 +        copyHeight = height - frame->ImageDesc.Top;
   1.193 +    }
   1.194 +
   1.195 +    for (; copyHeight > 0; copyHeight--) {
   1.196 +        copyLine(dst, src, cmap, transparent, copyWidth);
   1.197 +        src += frame->ImageDesc.Width;
   1.198 +        dst += width;
   1.199 +    }
   1.200 +}
   1.201 +
   1.202 +static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
   1.203 +                     uint32_t col)
   1.204 +{
   1.205 +    int bmWidth = bm->width();
   1.206 +    int bmHeight = bm->height();
   1.207 +    uint32_t* dst = bm->getAddr32(left, top);
   1.208 +    GifWord copyWidth = width;
   1.209 +    if (left + copyWidth > bmWidth) {
   1.210 +        copyWidth = bmWidth - left;
   1.211 +    }
   1.212 +
   1.213 +    GifWord copyHeight = height;
   1.214 +    if (top + copyHeight > bmHeight) {
   1.215 +        copyHeight = bmHeight - top;
   1.216 +    }
   1.217 +
   1.218 +    for (; copyHeight > 0; copyHeight--) {
   1.219 +        sk_memset32(dst, col, copyWidth);
   1.220 +        dst += bmWidth;
   1.221 +    }
   1.222 +}
   1.223 +
   1.224 +static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
   1.225 +{
   1.226 +    int transparent = -1;
   1.227 +
   1.228 +    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
   1.229 +        ExtensionBlock* eb = frame->ExtensionBlocks + i;
   1.230 +        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
   1.231 +            eb->ByteCount == 4) {
   1.232 +            bool has_transparency = ((eb->Bytes[0] & 1) == 1);
   1.233 +            if (has_transparency) {
   1.234 +                transparent = (unsigned char)eb->Bytes[3];
   1.235 +            }
   1.236 +        }
   1.237 +    }
   1.238 +
   1.239 +    if (frame->ImageDesc.ColorMap != NULL) {
   1.240 +        // use local color table
   1.241 +        cmap = frame->ImageDesc.ColorMap;
   1.242 +    }
   1.243 +
   1.244 +    if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
   1.245 +        SkDEBUGFAIL("bad colortable setup");
   1.246 +        return;
   1.247 +    }
   1.248 +
   1.249 +#if GIFLIB_MAJOR < 5
   1.250 +    // before GIFLIB 5, de-interlacing wasn't done by library at load time
   1.251 +    if (frame->ImageDesc.Interlace) {
   1.252 +        blitInterlace(bm, frame, cmap, transparent);
   1.253 +        return;
   1.254 +    }
   1.255 +#endif
   1.256 +
   1.257 +    blitNormal(bm, frame, cmap, transparent);
   1.258 +}
   1.259 +
   1.260 +static bool checkIfWillBeCleared(const SavedImage* frame)
   1.261 +{
   1.262 +    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
   1.263 +        ExtensionBlock* eb = frame->ExtensionBlocks + i;
   1.264 +        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
   1.265 +            eb->ByteCount == 4) {
   1.266 +            // check disposal method
   1.267 +            int disposal = ((eb->Bytes[0] >> 2) & 7);
   1.268 +            if (disposal == 2 || disposal == 3) {
   1.269 +                return true;
   1.270 +            }
   1.271 +        }
   1.272 +    }
   1.273 +    return false;
   1.274 +}
   1.275 +
   1.276 +static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
   1.277 +{
   1.278 +    *trans = false;
   1.279 +    *disposal = 0;
   1.280 +    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
   1.281 +        ExtensionBlock* eb = frame->ExtensionBlocks + i;
   1.282 +        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
   1.283 +            eb->ByteCount == 4) {
   1.284 +            *trans = ((eb->Bytes[0] & 1) == 1);
   1.285 +            *disposal = ((eb->Bytes[0] >> 2) & 7);
   1.286 +        }
   1.287 +    }
   1.288 +}
   1.289 +
   1.290 +// return true if area of 'target' is completely covers area of 'covered'
   1.291 +static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
   1.292 +{
   1.293 +    if (target->ImageDesc.Left <= covered->ImageDesc.Left
   1.294 +        && covered->ImageDesc.Left + covered->ImageDesc.Width <=
   1.295 +               target->ImageDesc.Left + target->ImageDesc.Width
   1.296 +        && target->ImageDesc.Top <= covered->ImageDesc.Top
   1.297 +        && covered->ImageDesc.Top + covered->ImageDesc.Height <=
   1.298 +               target->ImageDesc.Top + target->ImageDesc.Height) {
   1.299 +        return true;
   1.300 +    }
   1.301 +    return false;
   1.302 +}
   1.303 +
   1.304 +static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
   1.305 +                                 SkBitmap* backup, SkColor color)
   1.306 +{
   1.307 +    // We can skip disposal process if next frame is not transparent
   1.308 +    // and completely covers current area
   1.309 +    bool curTrans;
   1.310 +    int curDisposal;
   1.311 +    getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
   1.312 +    bool nextTrans;
   1.313 +    int nextDisposal;
   1.314 +    getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
   1.315 +    if ((curDisposal == 2 || curDisposal == 3)
   1.316 +        && (nextTrans || !checkIfCover(next, cur))) {
   1.317 +        switch (curDisposal) {
   1.318 +        // restore to background color
   1.319 +        // -> 'background' means background under this image.
   1.320 +        case 2:
   1.321 +            fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
   1.322 +                     cur->ImageDesc.Width, cur->ImageDesc.Height,
   1.323 +                     color);
   1.324 +            break;
   1.325 +
   1.326 +        // restore to previous
   1.327 +        case 3:
   1.328 +            bm->swap(*backup);
   1.329 +            break;
   1.330 +        }
   1.331 +    }
   1.332 +
   1.333 +    // Save current image if next frame's disposal method == 3
   1.334 +    if (nextDisposal == 3) {
   1.335 +        const uint32_t* src = bm->getAddr32(0, 0);
   1.336 +        uint32_t* dst = backup->getAddr32(0, 0);
   1.337 +        int cnt = bm->width() * bm->height();
   1.338 +        memcpy(dst, src, cnt*sizeof(uint32_t));
   1.339 +    }
   1.340 +}
   1.341 +
   1.342 +bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
   1.343 +{
   1.344 +    const GifFileType* gif = fGIF;
   1.345 +    if (NULL == gif)
   1.346 +        return false;
   1.347 +
   1.348 +    if (gif->ImageCount < 1) {
   1.349 +        return false;
   1.350 +    }
   1.351 +
   1.352 +    const int width = gif->SWidth;
   1.353 +    const int height = gif->SHeight;
   1.354 +    if (width <= 0 || height <= 0) {
   1.355 +        return false;
   1.356 +    }
   1.357 +
   1.358 +    // no need to draw
   1.359 +    if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
   1.360 +        return true;
   1.361 +    }
   1.362 +
   1.363 +    int startIndex = fLastDrawIndex + 1;
   1.364 +    if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
   1.365 +        // first time
   1.366 +
   1.367 +        startIndex = 0;
   1.368 +
   1.369 +        // create bitmap
   1.370 +        if (!bm->allocPixels(SkImageInfo::MakeN32Premul(width, height))) {
   1.371 +            return false;
   1.372 +        }
   1.373 +        // create bitmap for backup
   1.374 +        if (!fBackup.allocPixels(SkImageInfo::MakeN32Premul(width, height))) {
   1.375 +            return false;
   1.376 +        }
   1.377 +    } else if (startIndex > fCurrIndex) {
   1.378 +        // rewind to 1st frame for repeat
   1.379 +        startIndex = 0;
   1.380 +    }
   1.381 +
   1.382 +    int lastIndex = fCurrIndex;
   1.383 +    if (lastIndex < 0) {
   1.384 +        // first time
   1.385 +        lastIndex = 0;
   1.386 +    } else if (lastIndex > fGIF->ImageCount - 1) {
   1.387 +        // this block must not be reached.
   1.388 +        lastIndex = fGIF->ImageCount - 1;
   1.389 +    }
   1.390 +
   1.391 +    SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
   1.392 +    if (gif->SColorMap != NULL) {
   1.393 +        const GifColorType& col = gif->SColorMap->Colors[fGIF->SBackGroundColor];
   1.394 +        bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
   1.395 +    }
   1.396 +
   1.397 +    static SkColor paintingColor = SkPackARGB32(0, 0, 0, 0);
   1.398 +    // draw each frames - not intelligent way
   1.399 +    for (int i = startIndex; i <= lastIndex; i++) {
   1.400 +        const SavedImage* cur = &fGIF->SavedImages[i];
   1.401 +        if (i == 0) {
   1.402 +            bool trans;
   1.403 +            int disposal;
   1.404 +            getTransparencyAndDisposalMethod(cur, &trans, &disposal);
   1.405 +            if (!trans && gif->SColorMap != NULL) {
   1.406 +                paintingColor = bgColor;
   1.407 +            } else {
   1.408 +                paintingColor = SkColorSetARGB(0, 0, 0, 0);
   1.409 +            }
   1.410 +
   1.411 +            bm->eraseColor(paintingColor);
   1.412 +            fBackup.eraseColor(paintingColor);
   1.413 +        } else {
   1.414 +            // Dispose previous frame before move to next frame.
   1.415 +            const SavedImage* prev = &fGIF->SavedImages[i-1];
   1.416 +            disposeFrameIfNeeded(bm, prev, cur, &fBackup, paintingColor);
   1.417 +        }
   1.418 +
   1.419 +        // Draw frame
   1.420 +        // We can skip this process if this index is not last and disposal
   1.421 +        // method == 2 or method == 3
   1.422 +        if (i == lastIndex || !checkIfWillBeCleared(cur)) {
   1.423 +            drawFrame(bm, cur, gif->SColorMap);
   1.424 +        }
   1.425 +    }
   1.426 +
   1.427 +    // save index
   1.428 +    fLastDrawIndex = lastIndex;
   1.429 +    return true;
   1.430 +}
   1.431 +
   1.432 +///////////////////////////////////////////////////////////////////////////////
   1.433 +
   1.434 +#include "SkTRegistry.h"
   1.435 +
   1.436 +SkMovie* Factory(SkStreamRewindable* stream) {
   1.437 +    char buf[GIF_STAMP_LEN];
   1.438 +    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
   1.439 +        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
   1.440 +                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
   1.441 +                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
   1.442 +            // must rewind here, since our construct wants to re-read the data
   1.443 +            stream->rewind();
   1.444 +            return SkNEW_ARGS(SkGIFMovie, (stream));
   1.445 +        }
   1.446 +    }
   1.447 +    return NULL;
   1.448 +}
   1.449 +
   1.450 +static SkTRegistry<SkMovie*(*)(SkStreamRewindable*)> gReg(Factory);

mercurial