image/src/FrameBlender.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "FrameBlender.h"
michael@0 7
michael@0 8 #include "mozilla/MemoryReporting.h"
michael@0 9 #include "MainThreadUtils.h"
michael@0 10
michael@0 11 #include "pixman.h"
michael@0 12
michael@0 13 using namespace mozilla;
michael@0 14 using namespace mozilla::image;
michael@0 15
michael@0 16 namespace mozilla {
michael@0 17 namespace image {
michael@0 18
michael@0 19 FrameBlender::FrameBlender(FrameSequence* aSequenceToUse /* = nullptr */)
michael@0 20 : mFrames(aSequenceToUse)
michael@0 21 , mAnim(nullptr)
michael@0 22 , mLoopCount(-1)
michael@0 23 {
michael@0 24 if (!mFrames) {
michael@0 25 mFrames = new FrameSequence();
michael@0 26 }
michael@0 27 }
michael@0 28
michael@0 29 FrameBlender::~FrameBlender()
michael@0 30 {
michael@0 31 delete mAnim;
michael@0 32 }
michael@0 33
michael@0 34 already_AddRefed<FrameSequence>
michael@0 35 FrameBlender::GetFrameSequence()
michael@0 36 {
michael@0 37 nsRefPtr<FrameSequence> seq(mFrames);
michael@0 38 return seq.forget();
michael@0 39 }
michael@0 40
michael@0 41 imgFrame*
michael@0 42 FrameBlender::GetFrame(uint32_t framenum) const
michael@0 43 {
michael@0 44 if (!mAnim) {
michael@0 45 NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
michael@0 46 return mFrames->GetFrame(0);
michael@0 47 }
michael@0 48 if (mAnim->lastCompositedFrameIndex == int32_t(framenum))
michael@0 49 return mAnim->compositingFrame;
michael@0 50 return mFrames->GetFrame(framenum);
michael@0 51 }
michael@0 52
michael@0 53 imgFrame*
michael@0 54 FrameBlender::RawGetFrame(uint32_t framenum) const
michael@0 55 {
michael@0 56 if (!mAnim) {
michael@0 57 NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
michael@0 58 return mFrames->GetFrame(0);
michael@0 59 }
michael@0 60
michael@0 61 return mFrames->GetFrame(framenum);
michael@0 62 }
michael@0 63
michael@0 64 uint32_t
michael@0 65 FrameBlender::GetNumFrames() const
michael@0 66 {
michael@0 67 return mFrames->GetNumFrames();
michael@0 68 }
michael@0 69
michael@0 70 int32_t
michael@0 71 FrameBlender::GetTimeoutForFrame(uint32_t framenum) const
michael@0 72 {
michael@0 73 const int32_t timeout = RawGetFrame(framenum)->GetRawTimeout();
michael@0 74 // Ensure a minimal time between updates so we don't throttle the UI thread.
michael@0 75 // consider 0 == unspecified and make it fast but not too fast. Unless we have
michael@0 76 // a single loop GIF. See bug 890743, bug 125137, bug 139677, and bug 207059.
michael@0 77 // The behavior of recent IE and Opera versions seems to be:
michael@0 78 // IE 6/Win:
michael@0 79 // 10 - 50ms go 100ms
michael@0 80 // >50ms go correct speed
michael@0 81 // Opera 7 final/Win:
michael@0 82 // 10ms goes 100ms
michael@0 83 // >10ms go correct speed
michael@0 84 // It seems that there are broken tools out there that set a 0ms or 10ms
michael@0 85 // timeout when they really want a "default" one. So munge values in that
michael@0 86 // range.
michael@0 87 if (timeout >= 0 && timeout <= 10 && mLoopCount != 0)
michael@0 88 return 100;
michael@0 89 return timeout;
michael@0 90 }
michael@0 91
michael@0 92 void
michael@0 93 FrameBlender::SetLoopCount(int32_t aLoopCount)
michael@0 94 {
michael@0 95 mLoopCount = aLoopCount;
michael@0 96 }
michael@0 97
michael@0 98 int32_t
michael@0 99 FrameBlender::GetLoopCount() const
michael@0 100 {
michael@0 101 return mLoopCount;
michael@0 102 }
michael@0 103
michael@0 104 void
michael@0 105 FrameBlender::RemoveFrame(uint32_t framenum)
michael@0 106 {
michael@0 107 NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Deleting invalid frame!");
michael@0 108
michael@0 109 mFrames->RemoveFrame(framenum);
michael@0 110 }
michael@0 111
michael@0 112 void
michael@0 113 FrameBlender::ClearFrames()
michael@0 114 {
michael@0 115 // Forget our old frame sequence, letting whoever else has it deal with it.
michael@0 116 mFrames = new FrameSequence();
michael@0 117 }
michael@0 118
michael@0 119 void
michael@0 120 FrameBlender::InsertFrame(uint32_t framenum, imgFrame* aFrame)
michael@0 121 {
michael@0 122 NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Inserting invalid frame!");
michael@0 123 mFrames->InsertFrame(framenum, aFrame);
michael@0 124 if (GetNumFrames() > 1) {
michael@0 125 EnsureAnimExists();
michael@0 126 }
michael@0 127 }
michael@0 128
michael@0 129 imgFrame*
michael@0 130 FrameBlender::SwapFrame(uint32_t framenum, imgFrame* aFrame)
michael@0 131 {
michael@0 132 NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Swapping invalid frame!");
michael@0 133
michael@0 134 imgFrame* ret;
michael@0 135
michael@0 136 // Steal the imgFrame from wherever it's currently stored
michael@0 137 if (mAnim && mAnim->lastCompositedFrameIndex == int32_t(framenum)) {
michael@0 138 ret = mAnim->compositingFrame.Forget();
michael@0 139 mAnim->lastCompositedFrameIndex = -1;
michael@0 140 nsAutoPtr<imgFrame> toDelete(mFrames->SwapFrame(framenum, aFrame));
michael@0 141 } else {
michael@0 142 ret = mFrames->SwapFrame(framenum, aFrame);
michael@0 143 }
michael@0 144
michael@0 145 return ret;
michael@0 146 }
michael@0 147
michael@0 148 void
michael@0 149 FrameBlender::EnsureAnimExists()
michael@0 150 {
michael@0 151 if (!mAnim) {
michael@0 152 // Create the animation context
michael@0 153 mAnim = new Anim();
michael@0 154
michael@0 155 // We should only get into this code path directly after we've created our
michael@0 156 // second frame (hence we know we're animated).
michael@0 157 MOZ_ASSERT(GetNumFrames() == 2);
michael@0 158 }
michael@0 159 }
michael@0 160
michael@0 161 //******************************************************************************
michael@0 162 // DoBlend gets called when the timer for animation get fired and we have to
michael@0 163 // update the composited frame of the animation.
michael@0 164 bool
michael@0 165 FrameBlender::DoBlend(nsIntRect* aDirtyRect,
michael@0 166 uint32_t aPrevFrameIndex,
michael@0 167 uint32_t aNextFrameIndex)
michael@0 168 {
michael@0 169 if (!aDirtyRect) {
michael@0 170 return false;
michael@0 171 }
michael@0 172
michael@0 173 const FrameDataPair& prevFrame = mFrames->GetFrame(aPrevFrameIndex);
michael@0 174 const FrameDataPair& nextFrame = mFrames->GetFrame(aNextFrameIndex);
michael@0 175 if (!prevFrame.HasFrameData() || !nextFrame.HasFrameData()) {
michael@0 176 return false;
michael@0 177 }
michael@0 178
michael@0 179 int32_t prevFrameDisposalMethod = prevFrame->GetFrameDisposalMethod();
michael@0 180 if (prevFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious &&
michael@0 181 !mAnim->compositingPrevFrame)
michael@0 182 prevFrameDisposalMethod = FrameBlender::kDisposeClear;
michael@0 183
michael@0 184 nsIntRect prevFrameRect = prevFrame->GetRect();
michael@0 185 bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
michael@0 186 prevFrameRect.width == mSize.width &&
michael@0 187 prevFrameRect.height == mSize.height);
michael@0 188
michael@0 189 // Optimization: DisposeClearAll if the previous frame is the same size as
michael@0 190 // container and it's clearing itself
michael@0 191 if (isFullPrevFrame &&
michael@0 192 (prevFrameDisposalMethod == FrameBlender::kDisposeClear))
michael@0 193 prevFrameDisposalMethod = FrameBlender::kDisposeClearAll;
michael@0 194
michael@0 195 int32_t nextFrameDisposalMethod = nextFrame->GetFrameDisposalMethod();
michael@0 196 nsIntRect nextFrameRect = nextFrame->GetRect();
michael@0 197 bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
michael@0 198 nextFrameRect.width == mSize.width &&
michael@0 199 nextFrameRect.height == mSize.height);
michael@0 200
michael@0 201 if (!nextFrame->GetIsPaletted()) {
michael@0 202 // Optimization: Skip compositing if the previous frame wants to clear the
michael@0 203 // whole image
michael@0 204 if (prevFrameDisposalMethod == FrameBlender::kDisposeClearAll) {
michael@0 205 aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
michael@0 206 return true;
michael@0 207 }
michael@0 208
michael@0 209 // Optimization: Skip compositing if this frame is the same size as the
michael@0 210 // container and it's fully drawing over prev frame (no alpha)
michael@0 211 if (isFullNextFrame &&
michael@0 212 (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) &&
michael@0 213 !nextFrame->GetHasAlpha()) {
michael@0 214 aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
michael@0 215 return true;
michael@0 216 }
michael@0 217 }
michael@0 218
michael@0 219 // Calculate area that needs updating
michael@0 220 switch (prevFrameDisposalMethod) {
michael@0 221 default:
michael@0 222 case FrameBlender::kDisposeNotSpecified:
michael@0 223 case FrameBlender::kDisposeKeep:
michael@0 224 *aDirtyRect = nextFrameRect;
michael@0 225 break;
michael@0 226
michael@0 227 case FrameBlender::kDisposeClearAll:
michael@0 228 // Whole image container is cleared
michael@0 229 aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
michael@0 230 break;
michael@0 231
michael@0 232 case FrameBlender::kDisposeClear:
michael@0 233 // Calc area that needs to be redrawn (the combination of previous and
michael@0 234 // this frame)
michael@0 235 // XXX - This could be done with multiple framechanged calls
michael@0 236 // Having prevFrame way at the top of the image, and nextFrame
michael@0 237 // way at the bottom, and both frames being small, we'd be
michael@0 238 // telling framechanged to refresh the whole image when only two
michael@0 239 // small areas are needed.
michael@0 240 aDirtyRect->UnionRect(nextFrameRect, prevFrameRect);
michael@0 241 break;
michael@0 242
michael@0 243 case FrameBlender::kDisposeRestorePrevious:
michael@0 244 aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
michael@0 245 break;
michael@0 246 }
michael@0 247
michael@0 248 // Optimization:
michael@0 249 // Skip compositing if the last composited frame is this frame
michael@0 250 // (Only one composited frame was made for this animation. Example:
michael@0 251 // Only Frame 3 of a 10 frame image required us to build a composite frame
michael@0 252 // On the second loop, we do not need to rebuild the frame
michael@0 253 // since it's still sitting in compositingFrame)
michael@0 254 if (mAnim->lastCompositedFrameIndex == int32_t(aNextFrameIndex)) {
michael@0 255 return true;
michael@0 256 }
michael@0 257
michael@0 258 bool needToBlankComposite = false;
michael@0 259
michael@0 260 // Create the Compositing Frame
michael@0 261 if (!mAnim->compositingFrame) {
michael@0 262 mAnim->compositingFrame.SetFrame(new imgFrame());
michael@0 263 nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
michael@0 264 gfxImageFormat::ARGB32);
michael@0 265 if (NS_FAILED(rv)) {
michael@0 266 mAnim->compositingFrame.SetFrame(nullptr);
michael@0 267 return false;
michael@0 268 }
michael@0 269 mAnim->compositingFrame.LockAndGetData();
michael@0 270 needToBlankComposite = true;
michael@0 271 } else if (int32_t(aNextFrameIndex) != mAnim->lastCompositedFrameIndex+1) {
michael@0 272
michael@0 273 // If we are not drawing on top of last composited frame,
michael@0 274 // then we are building a new composite frame, so let's clear it first.
michael@0 275 needToBlankComposite = true;
michael@0 276 }
michael@0 277
michael@0 278 // More optimizations possible when next frame is not transparent
michael@0 279 // But if the next frame has FrameBlender::kDisposeRestorePrevious,
michael@0 280 // this "no disposal" optimization is not possible,
michael@0 281 // because the frame in "after disposal operation" state
michael@0 282 // needs to be stored in compositingFrame, so it can be
michael@0 283 // copied into compositingPrevFrame later.
michael@0 284 bool doDisposal = true;
michael@0 285 if (!nextFrame->GetHasAlpha() &&
michael@0 286 nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) {
michael@0 287 if (isFullNextFrame) {
michael@0 288 // Optimization: No need to dispose prev.frame when
michael@0 289 // next frame is full frame and not transparent.
michael@0 290 doDisposal = false;
michael@0 291 // No need to blank the composite frame
michael@0 292 needToBlankComposite = false;
michael@0 293 } else {
michael@0 294 if ((prevFrameRect.x >= nextFrameRect.x) &&
michael@0 295 (prevFrameRect.y >= nextFrameRect.y) &&
michael@0 296 (prevFrameRect.x + prevFrameRect.width <= nextFrameRect.x + nextFrameRect.width) &&
michael@0 297 (prevFrameRect.y + prevFrameRect.height <= nextFrameRect.y + nextFrameRect.height)) {
michael@0 298 // Optimization: No need to dispose prev.frame when
michael@0 299 // next frame fully overlaps previous frame.
michael@0 300 doDisposal = false;
michael@0 301 }
michael@0 302 }
michael@0 303 }
michael@0 304
michael@0 305 if (doDisposal) {
michael@0 306 // Dispose of previous: clear, restore, or keep (copy)
michael@0 307 switch (prevFrameDisposalMethod) {
michael@0 308 case FrameBlender::kDisposeClear:
michael@0 309 if (needToBlankComposite) {
michael@0 310 // If we just created the composite, it could have anything in its
michael@0 311 // buffer. Clear whole frame
michael@0 312 ClearFrame(mAnim->compositingFrame.GetFrameData(),
michael@0 313 mAnim->compositingFrame.GetFrame()->GetRect());
michael@0 314 } else {
michael@0 315 // Only blank out previous frame area (both color & Mask/Alpha)
michael@0 316 ClearFrame(mAnim->compositingFrame.GetFrameData(),
michael@0 317 mAnim->compositingFrame.GetFrame()->GetRect(),
michael@0 318 prevFrameRect);
michael@0 319 }
michael@0 320 break;
michael@0 321
michael@0 322 case FrameBlender::kDisposeClearAll:
michael@0 323 ClearFrame(mAnim->compositingFrame.GetFrameData(),
michael@0 324 mAnim->compositingFrame.GetFrame()->GetRect());
michael@0 325 break;
michael@0 326
michael@0 327 case FrameBlender::kDisposeRestorePrevious:
michael@0 328 // It would be better to copy only the area changed back to
michael@0 329 // compositingFrame.
michael@0 330 if (mAnim->compositingPrevFrame) {
michael@0 331 CopyFrameImage(mAnim->compositingPrevFrame.GetFrameData(),
michael@0 332 mAnim->compositingPrevFrame.GetFrame()->GetRect(),
michael@0 333 mAnim->compositingFrame.GetFrameData(),
michael@0 334 mAnim->compositingFrame.GetFrame()->GetRect());
michael@0 335
michael@0 336 // destroy only if we don't need it for this frame's disposal
michael@0 337 if (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)
michael@0 338 mAnim->compositingPrevFrame.SetFrame(nullptr);
michael@0 339 } else {
michael@0 340 ClearFrame(mAnim->compositingFrame.GetFrameData(),
michael@0 341 mAnim->compositingFrame.GetFrame()->GetRect());
michael@0 342 }
michael@0 343 break;
michael@0 344
michael@0 345 default:
michael@0 346 // Copy previous frame into compositingFrame before we put the new frame on top
michael@0 347 // Assumes that the previous frame represents a full frame (it could be
michael@0 348 // smaller in size than the container, as long as the frame before it erased
michael@0 349 // itself)
michael@0 350 // Note: Frame 1 never gets into DoBlend(), so (aNextFrameIndex - 1) will
michael@0 351 // always be a valid frame number.
michael@0 352 if (mAnim->lastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) {
michael@0 353 if (isFullPrevFrame && !prevFrame->GetIsPaletted()) {
michael@0 354 // Just copy the bits
michael@0 355 CopyFrameImage(prevFrame.GetFrameData(),
michael@0 356 prevFrame.GetFrame()->GetRect(),
michael@0 357 mAnim->compositingFrame.GetFrameData(),
michael@0 358 mAnim->compositingFrame.GetFrame()->GetRect());
michael@0 359 } else {
michael@0 360 if (needToBlankComposite) {
michael@0 361 // Only blank composite when prev is transparent or not full.
michael@0 362 if (prevFrame->GetHasAlpha() || !isFullPrevFrame) {
michael@0 363 ClearFrame(mAnim->compositingFrame.GetFrameData(),
michael@0 364 mAnim->compositingFrame.GetFrame()->GetRect());
michael@0 365 }
michael@0 366 }
michael@0 367 DrawFrameTo(prevFrame.GetFrameData(), prevFrameRect,
michael@0 368 prevFrame.GetFrame()->PaletteDataLength(),
michael@0 369 prevFrame.GetFrame()->GetHasAlpha(),
michael@0 370 mAnim->compositingFrame.GetFrameData(),
michael@0 371 mAnim->compositingFrame.GetFrame()->GetRect(),
michael@0 372 FrameBlendMethod(prevFrame.GetFrame()->GetBlendMethod()));
michael@0 373 }
michael@0 374 }
michael@0 375 }
michael@0 376 } else if (needToBlankComposite) {
michael@0 377 // If we just created the composite, it could have anything in it's
michael@0 378 // buffers. Clear them
michael@0 379 ClearFrame(mAnim->compositingFrame.GetFrameData(),
michael@0 380 mAnim->compositingFrame.GetFrame()->GetRect());
michael@0 381 }
michael@0 382
michael@0 383 // Check if the frame we are composing wants the previous image restored afer
michael@0 384 // it is done. Don't store it (again) if last frame wanted its image restored
michael@0 385 // too
michael@0 386 if ((nextFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious) &&
michael@0 387 (prevFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)) {
michael@0 388 // We are storing the whole image.
michael@0 389 // It would be better if we just stored the area that nextFrame is going to
michael@0 390 // overwrite.
michael@0 391 if (!mAnim->compositingPrevFrame) {
michael@0 392 mAnim->compositingPrevFrame.SetFrame(new imgFrame());
michael@0 393 nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
michael@0 394 gfxImageFormat::ARGB32);
michael@0 395 if (NS_FAILED(rv)) {
michael@0 396 mAnim->compositingPrevFrame.SetFrame(nullptr);
michael@0 397 return false;
michael@0 398 }
michael@0 399
michael@0 400 mAnim->compositingPrevFrame.LockAndGetData();
michael@0 401 }
michael@0 402
michael@0 403 CopyFrameImage(mAnim->compositingFrame.GetFrameData(),
michael@0 404 mAnim->compositingFrame.GetFrame()->GetRect(),
michael@0 405 mAnim->compositingPrevFrame.GetFrameData(),
michael@0 406 mAnim->compositingPrevFrame.GetFrame()->GetRect());
michael@0 407 }
michael@0 408
michael@0 409 // blit next frame into it's correct spot
michael@0 410 DrawFrameTo(nextFrame.GetFrameData(), nextFrameRect,
michael@0 411 nextFrame.GetFrame()->PaletteDataLength(),
michael@0 412 nextFrame.GetFrame()->GetHasAlpha(),
michael@0 413 mAnim->compositingFrame.GetFrameData(),
michael@0 414 mAnim->compositingFrame.GetFrame()->GetRect(),
michael@0 415 FrameBlendMethod(nextFrame.GetFrame()->GetBlendMethod()));
michael@0 416
michael@0 417 // Set timeout of CompositeFrame to timeout of frame we just composed
michael@0 418 // Bug 177948
michael@0 419 int32_t timeout = nextFrame->GetRawTimeout();
michael@0 420 mAnim->compositingFrame->SetRawTimeout(timeout);
michael@0 421
michael@0 422 // Tell the image that it is fully 'downloaded'.
michael@0 423 nsresult rv = mAnim->compositingFrame->ImageUpdated(mAnim->compositingFrame->GetRect());
michael@0 424 if (NS_FAILED(rv)) {
michael@0 425 return false;
michael@0 426 }
michael@0 427
michael@0 428 mAnim->lastCompositedFrameIndex = int32_t(aNextFrameIndex);
michael@0 429
michael@0 430 return true;
michael@0 431 }
michael@0 432
michael@0 433 //******************************************************************************
michael@0 434 // Fill aFrame with black. Does also clears the mask.
michael@0 435 void
michael@0 436 FrameBlender::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect)
michael@0 437 {
michael@0 438 if (!aFrameData)
michael@0 439 return;
michael@0 440
michael@0 441 memset(aFrameData, 0, aFrameRect.width * aFrameRect.height * 4);
michael@0 442 }
michael@0 443
michael@0 444 //******************************************************************************
michael@0 445 void
michael@0 446 FrameBlender::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect, const nsIntRect& aRectToClear)
michael@0 447 {
michael@0 448 if (!aFrameData || aFrameRect.width <= 0 || aFrameRect.height <= 0 ||
michael@0 449 aRectToClear.width <= 0 || aRectToClear.height <= 0) {
michael@0 450 return;
michael@0 451 }
michael@0 452
michael@0 453 nsIntRect toClear = aFrameRect.Intersect(aRectToClear);
michael@0 454 if (toClear.IsEmpty()) {
michael@0 455 return;
michael@0 456 }
michael@0 457
michael@0 458 uint32_t bytesPerRow = aFrameRect.width * 4;
michael@0 459 for (int row = toClear.y; row < toClear.y + toClear.height; ++row) {
michael@0 460 memset(aFrameData + toClear.x * 4 + row * bytesPerRow, 0, toClear.width * 4);
michael@0 461 }
michael@0 462 }
michael@0 463
michael@0 464 //******************************************************************************
michael@0 465 // Whether we succeed or fail will not cause a crash, and there's not much
michael@0 466 // we can do about a failure, so there we don't return a nsresult
michael@0 467 bool
michael@0 468 FrameBlender::CopyFrameImage(const uint8_t *aDataSrc, const nsIntRect& aRectSrc,
michael@0 469 uint8_t *aDataDest, const nsIntRect& aRectDest)
michael@0 470 {
michael@0 471 uint32_t dataLengthSrc = aRectSrc.width * aRectSrc.height * 4;
michael@0 472 uint32_t dataLengthDest = aRectDest.width * aRectDest.height * 4;
michael@0 473
michael@0 474 if (!aDataDest || !aDataSrc || dataLengthSrc != dataLengthDest) {
michael@0 475 return false;
michael@0 476 }
michael@0 477
michael@0 478 memcpy(aDataDest, aDataSrc, dataLengthDest);
michael@0 479
michael@0 480 return true;
michael@0 481 }
michael@0 482
michael@0 483 nsresult
michael@0 484 FrameBlender::DrawFrameTo(const uint8_t *aSrcData, const nsIntRect& aSrcRect,
michael@0 485 uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
michael@0 486 uint8_t *aDstPixels, const nsIntRect& aDstRect,
michael@0 487 FrameBlender::FrameBlendMethod aBlendMethod)
michael@0 488 {
michael@0 489 NS_ENSURE_ARG_POINTER(aSrcData);
michael@0 490 NS_ENSURE_ARG_POINTER(aDstPixels);
michael@0 491
michael@0 492 // According to both AGIF and APNG specs, offsets are unsigned
michael@0 493 if (aSrcRect.x < 0 || aSrcRect.y < 0) {
michael@0 494 NS_WARNING("FrameBlender::DrawFrameTo: negative offsets not allowed");
michael@0 495 return NS_ERROR_FAILURE;
michael@0 496 }
michael@0 497 // Outside the destination frame, skip it
michael@0 498 if ((aSrcRect.x > aDstRect.width) || (aSrcRect.y > aDstRect.height)) {
michael@0 499 return NS_OK;
michael@0 500 }
michael@0 501
michael@0 502 if (aSrcPaletteLength) {
michael@0 503 // Larger than the destination frame, clip it
michael@0 504 int32_t width = std::min(aSrcRect.width, aDstRect.width - aSrcRect.x);
michael@0 505 int32_t height = std::min(aSrcRect.height, aDstRect.height - aSrcRect.y);
michael@0 506
michael@0 507 // The clipped image must now fully fit within destination image frame
michael@0 508 NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) &&
michael@0 509 (aSrcRect.x + width <= aDstRect.width) &&
michael@0 510 (aSrcRect.y + height <= aDstRect.height),
michael@0 511 "FrameBlender::DrawFrameTo: Invalid aSrcRect");
michael@0 512
michael@0 513 // clipped image size may be smaller than source, but not larger
michael@0 514 NS_ASSERTION((width <= aSrcRect.width) && (height <= aSrcRect.height),
michael@0 515 "FrameBlender::DrawFrameTo: source must be smaller than dest");
michael@0 516
michael@0 517 // Get pointers to image data
michael@0 518 const uint8_t *srcPixels = aSrcData + aSrcPaletteLength;
michael@0 519 uint32_t *dstPixels = reinterpret_cast<uint32_t*>(aDstPixels);
michael@0 520 const uint32_t *colormap = reinterpret_cast<const uint32_t*>(aSrcData);
michael@0 521
michael@0 522 // Skip to the right offset
michael@0 523 dstPixels += aSrcRect.x + (aSrcRect.y * aDstRect.width);
michael@0 524 if (!aSrcHasAlpha) {
michael@0 525 for (int32_t r = height; r > 0; --r) {
michael@0 526 for (int32_t c = 0; c < width; c++) {
michael@0 527 dstPixels[c] = colormap[srcPixels[c]];
michael@0 528 }
michael@0 529 // Go to the next row in the source resp. destination image
michael@0 530 srcPixels += aSrcRect.width;
michael@0 531 dstPixels += aDstRect.width;
michael@0 532 }
michael@0 533 } else {
michael@0 534 for (int32_t r = height; r > 0; --r) {
michael@0 535 for (int32_t c = 0; c < width; c++) {
michael@0 536 const uint32_t color = colormap[srcPixels[c]];
michael@0 537 if (color)
michael@0 538 dstPixels[c] = color;
michael@0 539 }
michael@0 540 // Go to the next row in the source resp. destination image
michael@0 541 srcPixels += aSrcRect.width;
michael@0 542 dstPixels += aDstRect.width;
michael@0 543 }
michael@0 544 }
michael@0 545 } else {
michael@0 546 pixman_image_t* src = pixman_image_create_bits(aSrcHasAlpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
michael@0 547 aSrcRect.width,
michael@0 548 aSrcRect.height,
michael@0 549 reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(aSrcData)),
michael@0 550 aSrcRect.width * 4);
michael@0 551 pixman_image_t* dst = pixman_image_create_bits(PIXMAN_a8r8g8b8,
michael@0 552 aDstRect.width,
michael@0 553 aDstRect.height,
michael@0 554 reinterpret_cast<uint32_t*>(aDstPixels),
michael@0 555 aDstRect.width * 4);
michael@0 556
michael@0 557 pixman_image_composite32(aBlendMethod == FrameBlender::kBlendSource ? PIXMAN_OP_SRC : PIXMAN_OP_OVER,
michael@0 558 src,
michael@0 559 nullptr,
michael@0 560 dst,
michael@0 561 0, 0,
michael@0 562 0, 0,
michael@0 563 aSrcRect.x, aSrcRect.y,
michael@0 564 aSrcRect.width, aSrcRect.height);
michael@0 565
michael@0 566 pixman_image_unref(src);
michael@0 567 pixman_image_unref(dst);
michael@0 568 }
michael@0 569
michael@0 570 return NS_OK;
michael@0 571 }
michael@0 572
michael@0 573 void
michael@0 574 FrameBlender::Discard()
michael@0 575 {
michael@0 576 MOZ_ASSERT(NS_IsMainThread());
michael@0 577
michael@0 578 // As soon as an image becomes animated, it becomes non-discardable and any
michael@0 579 // timers are cancelled.
michael@0 580 NS_ABORT_IF_FALSE(!mAnim, "Asked to discard for animated image!");
michael@0 581
michael@0 582 // Delete all the decoded frames, then clear the array.
michael@0 583 ClearFrames();
michael@0 584 }
michael@0 585
michael@0 586 size_t
michael@0 587 FrameBlender::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
michael@0 588 MallocSizeOf aMallocSizeOf) const
michael@0 589 {
michael@0 590 size_t n = mFrames->SizeOfDecodedWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
michael@0 591
michael@0 592 if (mAnim) {
michael@0 593 if (mAnim->compositingFrame) {
michael@0 594 n += mAnim->compositingFrame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
michael@0 595 }
michael@0 596 if (mAnim->compositingPrevFrame) {
michael@0 597 n += mAnim->compositingPrevFrame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
michael@0 598 }
michael@0 599 }
michael@0 600
michael@0 601 return n;
michael@0 602 }
michael@0 603
michael@0 604 void
michael@0 605 FrameBlender::ResetAnimation()
michael@0 606 {
michael@0 607 if (mAnim) {
michael@0 608 mAnim->lastCompositedFrameIndex = -1;
michael@0 609 }
michael@0 610 }
michael@0 611
michael@0 612 } // namespace image
michael@0 613 } // namespace mozilla

mercurial