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);