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

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial