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