1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/src/FrameBlender.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,613 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "FrameBlender.h" 1.10 + 1.11 +#include "mozilla/MemoryReporting.h" 1.12 +#include "MainThreadUtils.h" 1.13 + 1.14 +#include "pixman.h" 1.15 + 1.16 +using namespace mozilla; 1.17 +using namespace mozilla::image; 1.18 + 1.19 +namespace mozilla { 1.20 +namespace image { 1.21 + 1.22 +FrameBlender::FrameBlender(FrameSequence* aSequenceToUse /* = nullptr */) 1.23 + : mFrames(aSequenceToUse) 1.24 + , mAnim(nullptr) 1.25 + , mLoopCount(-1) 1.26 +{ 1.27 + if (!mFrames) { 1.28 + mFrames = new FrameSequence(); 1.29 + } 1.30 +} 1.31 + 1.32 +FrameBlender::~FrameBlender() 1.33 +{ 1.34 + delete mAnim; 1.35 +} 1.36 + 1.37 +already_AddRefed<FrameSequence> 1.38 +FrameBlender::GetFrameSequence() 1.39 +{ 1.40 + nsRefPtr<FrameSequence> seq(mFrames); 1.41 + return seq.forget(); 1.42 +} 1.43 + 1.44 +imgFrame* 1.45 +FrameBlender::GetFrame(uint32_t framenum) const 1.46 +{ 1.47 + if (!mAnim) { 1.48 + NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!"); 1.49 + return mFrames->GetFrame(0); 1.50 + } 1.51 + if (mAnim->lastCompositedFrameIndex == int32_t(framenum)) 1.52 + return mAnim->compositingFrame; 1.53 + return mFrames->GetFrame(framenum); 1.54 +} 1.55 + 1.56 +imgFrame* 1.57 +FrameBlender::RawGetFrame(uint32_t framenum) const 1.58 +{ 1.59 + if (!mAnim) { 1.60 + NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!"); 1.61 + return mFrames->GetFrame(0); 1.62 + } 1.63 + 1.64 + return mFrames->GetFrame(framenum); 1.65 +} 1.66 + 1.67 +uint32_t 1.68 +FrameBlender::GetNumFrames() const 1.69 +{ 1.70 + return mFrames->GetNumFrames(); 1.71 +} 1.72 + 1.73 +int32_t 1.74 +FrameBlender::GetTimeoutForFrame(uint32_t framenum) const 1.75 +{ 1.76 + const int32_t timeout = RawGetFrame(framenum)->GetRawTimeout(); 1.77 + // Ensure a minimal time between updates so we don't throttle the UI thread. 1.78 + // consider 0 == unspecified and make it fast but not too fast. Unless we have 1.79 + // a single loop GIF. See bug 890743, bug 125137, bug 139677, and bug 207059. 1.80 + // The behavior of recent IE and Opera versions seems to be: 1.81 + // IE 6/Win: 1.82 + // 10 - 50ms go 100ms 1.83 + // >50ms go correct speed 1.84 + // Opera 7 final/Win: 1.85 + // 10ms goes 100ms 1.86 + // >10ms go correct speed 1.87 + // It seems that there are broken tools out there that set a 0ms or 10ms 1.88 + // timeout when they really want a "default" one. So munge values in that 1.89 + // range. 1.90 + if (timeout >= 0 && timeout <= 10 && mLoopCount != 0) 1.91 + return 100; 1.92 + return timeout; 1.93 +} 1.94 + 1.95 +void 1.96 +FrameBlender::SetLoopCount(int32_t aLoopCount) 1.97 +{ 1.98 + mLoopCount = aLoopCount; 1.99 +} 1.100 + 1.101 +int32_t 1.102 +FrameBlender::GetLoopCount() const 1.103 +{ 1.104 + return mLoopCount; 1.105 +} 1.106 + 1.107 +void 1.108 +FrameBlender::RemoveFrame(uint32_t framenum) 1.109 +{ 1.110 + NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Deleting invalid frame!"); 1.111 + 1.112 + mFrames->RemoveFrame(framenum); 1.113 +} 1.114 + 1.115 +void 1.116 +FrameBlender::ClearFrames() 1.117 +{ 1.118 + // Forget our old frame sequence, letting whoever else has it deal with it. 1.119 + mFrames = new FrameSequence(); 1.120 +} 1.121 + 1.122 +void 1.123 +FrameBlender::InsertFrame(uint32_t framenum, imgFrame* aFrame) 1.124 +{ 1.125 + NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Inserting invalid frame!"); 1.126 + mFrames->InsertFrame(framenum, aFrame); 1.127 + if (GetNumFrames() > 1) { 1.128 + EnsureAnimExists(); 1.129 + } 1.130 +} 1.131 + 1.132 +imgFrame* 1.133 +FrameBlender::SwapFrame(uint32_t framenum, imgFrame* aFrame) 1.134 +{ 1.135 + NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Swapping invalid frame!"); 1.136 + 1.137 + imgFrame* ret; 1.138 + 1.139 + // Steal the imgFrame from wherever it's currently stored 1.140 + if (mAnim && mAnim->lastCompositedFrameIndex == int32_t(framenum)) { 1.141 + ret = mAnim->compositingFrame.Forget(); 1.142 + mAnim->lastCompositedFrameIndex = -1; 1.143 + nsAutoPtr<imgFrame> toDelete(mFrames->SwapFrame(framenum, aFrame)); 1.144 + } else { 1.145 + ret = mFrames->SwapFrame(framenum, aFrame); 1.146 + } 1.147 + 1.148 + return ret; 1.149 +} 1.150 + 1.151 +void 1.152 +FrameBlender::EnsureAnimExists() 1.153 +{ 1.154 + if (!mAnim) { 1.155 + // Create the animation context 1.156 + mAnim = new Anim(); 1.157 + 1.158 + // We should only get into this code path directly after we've created our 1.159 + // second frame (hence we know we're animated). 1.160 + MOZ_ASSERT(GetNumFrames() == 2); 1.161 + } 1.162 +} 1.163 + 1.164 +//****************************************************************************** 1.165 +// DoBlend gets called when the timer for animation get fired and we have to 1.166 +// update the composited frame of the animation. 1.167 +bool 1.168 +FrameBlender::DoBlend(nsIntRect* aDirtyRect, 1.169 + uint32_t aPrevFrameIndex, 1.170 + uint32_t aNextFrameIndex) 1.171 +{ 1.172 + if (!aDirtyRect) { 1.173 + return false; 1.174 + } 1.175 + 1.176 + const FrameDataPair& prevFrame = mFrames->GetFrame(aPrevFrameIndex); 1.177 + const FrameDataPair& nextFrame = mFrames->GetFrame(aNextFrameIndex); 1.178 + if (!prevFrame.HasFrameData() || !nextFrame.HasFrameData()) { 1.179 + return false; 1.180 + } 1.181 + 1.182 + int32_t prevFrameDisposalMethod = prevFrame->GetFrameDisposalMethod(); 1.183 + if (prevFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious && 1.184 + !mAnim->compositingPrevFrame) 1.185 + prevFrameDisposalMethod = FrameBlender::kDisposeClear; 1.186 + 1.187 + nsIntRect prevFrameRect = prevFrame->GetRect(); 1.188 + bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 && 1.189 + prevFrameRect.width == mSize.width && 1.190 + prevFrameRect.height == mSize.height); 1.191 + 1.192 + // Optimization: DisposeClearAll if the previous frame is the same size as 1.193 + // container and it's clearing itself 1.194 + if (isFullPrevFrame && 1.195 + (prevFrameDisposalMethod == FrameBlender::kDisposeClear)) 1.196 + prevFrameDisposalMethod = FrameBlender::kDisposeClearAll; 1.197 + 1.198 + int32_t nextFrameDisposalMethod = nextFrame->GetFrameDisposalMethod(); 1.199 + nsIntRect nextFrameRect = nextFrame->GetRect(); 1.200 + bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 && 1.201 + nextFrameRect.width == mSize.width && 1.202 + nextFrameRect.height == mSize.height); 1.203 + 1.204 + if (!nextFrame->GetIsPaletted()) { 1.205 + // Optimization: Skip compositing if the previous frame wants to clear the 1.206 + // whole image 1.207 + if (prevFrameDisposalMethod == FrameBlender::kDisposeClearAll) { 1.208 + aDirtyRect->SetRect(0, 0, mSize.width, mSize.height); 1.209 + return true; 1.210 + } 1.211 + 1.212 + // Optimization: Skip compositing if this frame is the same size as the 1.213 + // container and it's fully drawing over prev frame (no alpha) 1.214 + if (isFullNextFrame && 1.215 + (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) && 1.216 + !nextFrame->GetHasAlpha()) { 1.217 + aDirtyRect->SetRect(0, 0, mSize.width, mSize.height); 1.218 + return true; 1.219 + } 1.220 + } 1.221 + 1.222 + // Calculate area that needs updating 1.223 + switch (prevFrameDisposalMethod) { 1.224 + default: 1.225 + case FrameBlender::kDisposeNotSpecified: 1.226 + case FrameBlender::kDisposeKeep: 1.227 + *aDirtyRect = nextFrameRect; 1.228 + break; 1.229 + 1.230 + case FrameBlender::kDisposeClearAll: 1.231 + // Whole image container is cleared 1.232 + aDirtyRect->SetRect(0, 0, mSize.width, mSize.height); 1.233 + break; 1.234 + 1.235 + case FrameBlender::kDisposeClear: 1.236 + // Calc area that needs to be redrawn (the combination of previous and 1.237 + // this frame) 1.238 + // XXX - This could be done with multiple framechanged calls 1.239 + // Having prevFrame way at the top of the image, and nextFrame 1.240 + // way at the bottom, and both frames being small, we'd be 1.241 + // telling framechanged to refresh the whole image when only two 1.242 + // small areas are needed. 1.243 + aDirtyRect->UnionRect(nextFrameRect, prevFrameRect); 1.244 + break; 1.245 + 1.246 + case FrameBlender::kDisposeRestorePrevious: 1.247 + aDirtyRect->SetRect(0, 0, mSize.width, mSize.height); 1.248 + break; 1.249 + } 1.250 + 1.251 + // Optimization: 1.252 + // Skip compositing if the last composited frame is this frame 1.253 + // (Only one composited frame was made for this animation. Example: 1.254 + // Only Frame 3 of a 10 frame image required us to build a composite frame 1.255 + // On the second loop, we do not need to rebuild the frame 1.256 + // since it's still sitting in compositingFrame) 1.257 + if (mAnim->lastCompositedFrameIndex == int32_t(aNextFrameIndex)) { 1.258 + return true; 1.259 + } 1.260 + 1.261 + bool needToBlankComposite = false; 1.262 + 1.263 + // Create the Compositing Frame 1.264 + if (!mAnim->compositingFrame) { 1.265 + mAnim->compositingFrame.SetFrame(new imgFrame()); 1.266 + nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height, 1.267 + gfxImageFormat::ARGB32); 1.268 + if (NS_FAILED(rv)) { 1.269 + mAnim->compositingFrame.SetFrame(nullptr); 1.270 + return false; 1.271 + } 1.272 + mAnim->compositingFrame.LockAndGetData(); 1.273 + needToBlankComposite = true; 1.274 + } else if (int32_t(aNextFrameIndex) != mAnim->lastCompositedFrameIndex+1) { 1.275 + 1.276 + // If we are not drawing on top of last composited frame, 1.277 + // then we are building a new composite frame, so let's clear it first. 1.278 + needToBlankComposite = true; 1.279 + } 1.280 + 1.281 + // More optimizations possible when next frame is not transparent 1.282 + // But if the next frame has FrameBlender::kDisposeRestorePrevious, 1.283 + // this "no disposal" optimization is not possible, 1.284 + // because the frame in "after disposal operation" state 1.285 + // needs to be stored in compositingFrame, so it can be 1.286 + // copied into compositingPrevFrame later. 1.287 + bool doDisposal = true; 1.288 + if (!nextFrame->GetHasAlpha() && 1.289 + nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) { 1.290 + if (isFullNextFrame) { 1.291 + // Optimization: No need to dispose prev.frame when 1.292 + // next frame is full frame and not transparent. 1.293 + doDisposal = false; 1.294 + // No need to blank the composite frame 1.295 + needToBlankComposite = false; 1.296 + } else { 1.297 + if ((prevFrameRect.x >= nextFrameRect.x) && 1.298 + (prevFrameRect.y >= nextFrameRect.y) && 1.299 + (prevFrameRect.x + prevFrameRect.width <= nextFrameRect.x + nextFrameRect.width) && 1.300 + (prevFrameRect.y + prevFrameRect.height <= nextFrameRect.y + nextFrameRect.height)) { 1.301 + // Optimization: No need to dispose prev.frame when 1.302 + // next frame fully overlaps previous frame. 1.303 + doDisposal = false; 1.304 + } 1.305 + } 1.306 + } 1.307 + 1.308 + if (doDisposal) { 1.309 + // Dispose of previous: clear, restore, or keep (copy) 1.310 + switch (prevFrameDisposalMethod) { 1.311 + case FrameBlender::kDisposeClear: 1.312 + if (needToBlankComposite) { 1.313 + // If we just created the composite, it could have anything in its 1.314 + // buffer. Clear whole frame 1.315 + ClearFrame(mAnim->compositingFrame.GetFrameData(), 1.316 + mAnim->compositingFrame.GetFrame()->GetRect()); 1.317 + } else { 1.318 + // Only blank out previous frame area (both color & Mask/Alpha) 1.319 + ClearFrame(mAnim->compositingFrame.GetFrameData(), 1.320 + mAnim->compositingFrame.GetFrame()->GetRect(), 1.321 + prevFrameRect); 1.322 + } 1.323 + break; 1.324 + 1.325 + case FrameBlender::kDisposeClearAll: 1.326 + ClearFrame(mAnim->compositingFrame.GetFrameData(), 1.327 + mAnim->compositingFrame.GetFrame()->GetRect()); 1.328 + break; 1.329 + 1.330 + case FrameBlender::kDisposeRestorePrevious: 1.331 + // It would be better to copy only the area changed back to 1.332 + // compositingFrame. 1.333 + if (mAnim->compositingPrevFrame) { 1.334 + CopyFrameImage(mAnim->compositingPrevFrame.GetFrameData(), 1.335 + mAnim->compositingPrevFrame.GetFrame()->GetRect(), 1.336 + mAnim->compositingFrame.GetFrameData(), 1.337 + mAnim->compositingFrame.GetFrame()->GetRect()); 1.338 + 1.339 + // destroy only if we don't need it for this frame's disposal 1.340 + if (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) 1.341 + mAnim->compositingPrevFrame.SetFrame(nullptr); 1.342 + } else { 1.343 + ClearFrame(mAnim->compositingFrame.GetFrameData(), 1.344 + mAnim->compositingFrame.GetFrame()->GetRect()); 1.345 + } 1.346 + break; 1.347 + 1.348 + default: 1.349 + // Copy previous frame into compositingFrame before we put the new frame on top 1.350 + // Assumes that the previous frame represents a full frame (it could be 1.351 + // smaller in size than the container, as long as the frame before it erased 1.352 + // itself) 1.353 + // Note: Frame 1 never gets into DoBlend(), so (aNextFrameIndex - 1) will 1.354 + // always be a valid frame number. 1.355 + if (mAnim->lastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) { 1.356 + if (isFullPrevFrame && !prevFrame->GetIsPaletted()) { 1.357 + // Just copy the bits 1.358 + CopyFrameImage(prevFrame.GetFrameData(), 1.359 + prevFrame.GetFrame()->GetRect(), 1.360 + mAnim->compositingFrame.GetFrameData(), 1.361 + mAnim->compositingFrame.GetFrame()->GetRect()); 1.362 + } else { 1.363 + if (needToBlankComposite) { 1.364 + // Only blank composite when prev is transparent or not full. 1.365 + if (prevFrame->GetHasAlpha() || !isFullPrevFrame) { 1.366 + ClearFrame(mAnim->compositingFrame.GetFrameData(), 1.367 + mAnim->compositingFrame.GetFrame()->GetRect()); 1.368 + } 1.369 + } 1.370 + DrawFrameTo(prevFrame.GetFrameData(), prevFrameRect, 1.371 + prevFrame.GetFrame()->PaletteDataLength(), 1.372 + prevFrame.GetFrame()->GetHasAlpha(), 1.373 + mAnim->compositingFrame.GetFrameData(), 1.374 + mAnim->compositingFrame.GetFrame()->GetRect(), 1.375 + FrameBlendMethod(prevFrame.GetFrame()->GetBlendMethod())); 1.376 + } 1.377 + } 1.378 + } 1.379 + } else if (needToBlankComposite) { 1.380 + // If we just created the composite, it could have anything in it's 1.381 + // buffers. Clear them 1.382 + ClearFrame(mAnim->compositingFrame.GetFrameData(), 1.383 + mAnim->compositingFrame.GetFrame()->GetRect()); 1.384 + } 1.385 + 1.386 + // Check if the frame we are composing wants the previous image restored afer 1.387 + // it is done. Don't store it (again) if last frame wanted its image restored 1.388 + // too 1.389 + if ((nextFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious) && 1.390 + (prevFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)) { 1.391 + // We are storing the whole image. 1.392 + // It would be better if we just stored the area that nextFrame is going to 1.393 + // overwrite. 1.394 + if (!mAnim->compositingPrevFrame) { 1.395 + mAnim->compositingPrevFrame.SetFrame(new imgFrame()); 1.396 + nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height, 1.397 + gfxImageFormat::ARGB32); 1.398 + if (NS_FAILED(rv)) { 1.399 + mAnim->compositingPrevFrame.SetFrame(nullptr); 1.400 + return false; 1.401 + } 1.402 + 1.403 + mAnim->compositingPrevFrame.LockAndGetData(); 1.404 + } 1.405 + 1.406 + CopyFrameImage(mAnim->compositingFrame.GetFrameData(), 1.407 + mAnim->compositingFrame.GetFrame()->GetRect(), 1.408 + mAnim->compositingPrevFrame.GetFrameData(), 1.409 + mAnim->compositingPrevFrame.GetFrame()->GetRect()); 1.410 + } 1.411 + 1.412 + // blit next frame into it's correct spot 1.413 + DrawFrameTo(nextFrame.GetFrameData(), nextFrameRect, 1.414 + nextFrame.GetFrame()->PaletteDataLength(), 1.415 + nextFrame.GetFrame()->GetHasAlpha(), 1.416 + mAnim->compositingFrame.GetFrameData(), 1.417 + mAnim->compositingFrame.GetFrame()->GetRect(), 1.418 + FrameBlendMethod(nextFrame.GetFrame()->GetBlendMethod())); 1.419 + 1.420 + // Set timeout of CompositeFrame to timeout of frame we just composed 1.421 + // Bug 177948 1.422 + int32_t timeout = nextFrame->GetRawTimeout(); 1.423 + mAnim->compositingFrame->SetRawTimeout(timeout); 1.424 + 1.425 + // Tell the image that it is fully 'downloaded'. 1.426 + nsresult rv = mAnim->compositingFrame->ImageUpdated(mAnim->compositingFrame->GetRect()); 1.427 + if (NS_FAILED(rv)) { 1.428 + return false; 1.429 + } 1.430 + 1.431 + mAnim->lastCompositedFrameIndex = int32_t(aNextFrameIndex); 1.432 + 1.433 + return true; 1.434 +} 1.435 + 1.436 +//****************************************************************************** 1.437 +// Fill aFrame with black. Does also clears the mask. 1.438 +void 1.439 +FrameBlender::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect) 1.440 +{ 1.441 + if (!aFrameData) 1.442 + return; 1.443 + 1.444 + memset(aFrameData, 0, aFrameRect.width * aFrameRect.height * 4); 1.445 +} 1.446 + 1.447 +//****************************************************************************** 1.448 +void 1.449 +FrameBlender::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect, const nsIntRect& aRectToClear) 1.450 +{ 1.451 + if (!aFrameData || aFrameRect.width <= 0 || aFrameRect.height <= 0 || 1.452 + aRectToClear.width <= 0 || aRectToClear.height <= 0) { 1.453 + return; 1.454 + } 1.455 + 1.456 + nsIntRect toClear = aFrameRect.Intersect(aRectToClear); 1.457 + if (toClear.IsEmpty()) { 1.458 + return; 1.459 + } 1.460 + 1.461 + uint32_t bytesPerRow = aFrameRect.width * 4; 1.462 + for (int row = toClear.y; row < toClear.y + toClear.height; ++row) { 1.463 + memset(aFrameData + toClear.x * 4 + row * bytesPerRow, 0, toClear.width * 4); 1.464 + } 1.465 +} 1.466 + 1.467 +//****************************************************************************** 1.468 +// Whether we succeed or fail will not cause a crash, and there's not much 1.469 +// we can do about a failure, so there we don't return a nsresult 1.470 +bool 1.471 +FrameBlender::CopyFrameImage(const uint8_t *aDataSrc, const nsIntRect& aRectSrc, 1.472 + uint8_t *aDataDest, const nsIntRect& aRectDest) 1.473 +{ 1.474 + uint32_t dataLengthSrc = aRectSrc.width * aRectSrc.height * 4; 1.475 + uint32_t dataLengthDest = aRectDest.width * aRectDest.height * 4; 1.476 + 1.477 + if (!aDataDest || !aDataSrc || dataLengthSrc != dataLengthDest) { 1.478 + return false; 1.479 + } 1.480 + 1.481 + memcpy(aDataDest, aDataSrc, dataLengthDest); 1.482 + 1.483 + return true; 1.484 +} 1.485 + 1.486 +nsresult 1.487 +FrameBlender::DrawFrameTo(const uint8_t *aSrcData, const nsIntRect& aSrcRect, 1.488 + uint32_t aSrcPaletteLength, bool aSrcHasAlpha, 1.489 + uint8_t *aDstPixels, const nsIntRect& aDstRect, 1.490 + FrameBlender::FrameBlendMethod aBlendMethod) 1.491 +{ 1.492 + NS_ENSURE_ARG_POINTER(aSrcData); 1.493 + NS_ENSURE_ARG_POINTER(aDstPixels); 1.494 + 1.495 + // According to both AGIF and APNG specs, offsets are unsigned 1.496 + if (aSrcRect.x < 0 || aSrcRect.y < 0) { 1.497 + NS_WARNING("FrameBlender::DrawFrameTo: negative offsets not allowed"); 1.498 + return NS_ERROR_FAILURE; 1.499 + } 1.500 + // Outside the destination frame, skip it 1.501 + if ((aSrcRect.x > aDstRect.width) || (aSrcRect.y > aDstRect.height)) { 1.502 + return NS_OK; 1.503 + } 1.504 + 1.505 + if (aSrcPaletteLength) { 1.506 + // Larger than the destination frame, clip it 1.507 + int32_t width = std::min(aSrcRect.width, aDstRect.width - aSrcRect.x); 1.508 + int32_t height = std::min(aSrcRect.height, aDstRect.height - aSrcRect.y); 1.509 + 1.510 + // The clipped image must now fully fit within destination image frame 1.511 + NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) && 1.512 + (aSrcRect.x + width <= aDstRect.width) && 1.513 + (aSrcRect.y + height <= aDstRect.height), 1.514 + "FrameBlender::DrawFrameTo: Invalid aSrcRect"); 1.515 + 1.516 + // clipped image size may be smaller than source, but not larger 1.517 + NS_ASSERTION((width <= aSrcRect.width) && (height <= aSrcRect.height), 1.518 + "FrameBlender::DrawFrameTo: source must be smaller than dest"); 1.519 + 1.520 + // Get pointers to image data 1.521 + const uint8_t *srcPixels = aSrcData + aSrcPaletteLength; 1.522 + uint32_t *dstPixels = reinterpret_cast<uint32_t*>(aDstPixels); 1.523 + const uint32_t *colormap = reinterpret_cast<const uint32_t*>(aSrcData); 1.524 + 1.525 + // Skip to the right offset 1.526 + dstPixels += aSrcRect.x + (aSrcRect.y * aDstRect.width); 1.527 + if (!aSrcHasAlpha) { 1.528 + for (int32_t r = height; r > 0; --r) { 1.529 + for (int32_t c = 0; c < width; c++) { 1.530 + dstPixels[c] = colormap[srcPixels[c]]; 1.531 + } 1.532 + // Go to the next row in the source resp. destination image 1.533 + srcPixels += aSrcRect.width; 1.534 + dstPixels += aDstRect.width; 1.535 + } 1.536 + } else { 1.537 + for (int32_t r = height; r > 0; --r) { 1.538 + for (int32_t c = 0; c < width; c++) { 1.539 + const uint32_t color = colormap[srcPixels[c]]; 1.540 + if (color) 1.541 + dstPixels[c] = color; 1.542 + } 1.543 + // Go to the next row in the source resp. destination image 1.544 + srcPixels += aSrcRect.width; 1.545 + dstPixels += aDstRect.width; 1.546 + } 1.547 + } 1.548 + } else { 1.549 + pixman_image_t* src = pixman_image_create_bits(aSrcHasAlpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8, 1.550 + aSrcRect.width, 1.551 + aSrcRect.height, 1.552 + reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(aSrcData)), 1.553 + aSrcRect.width * 4); 1.554 + pixman_image_t* dst = pixman_image_create_bits(PIXMAN_a8r8g8b8, 1.555 + aDstRect.width, 1.556 + aDstRect.height, 1.557 + reinterpret_cast<uint32_t*>(aDstPixels), 1.558 + aDstRect.width * 4); 1.559 + 1.560 + pixman_image_composite32(aBlendMethod == FrameBlender::kBlendSource ? PIXMAN_OP_SRC : PIXMAN_OP_OVER, 1.561 + src, 1.562 + nullptr, 1.563 + dst, 1.564 + 0, 0, 1.565 + 0, 0, 1.566 + aSrcRect.x, aSrcRect.y, 1.567 + aSrcRect.width, aSrcRect.height); 1.568 + 1.569 + pixman_image_unref(src); 1.570 + pixman_image_unref(dst); 1.571 + } 1.572 + 1.573 + return NS_OK; 1.574 +} 1.575 + 1.576 +void 1.577 +FrameBlender::Discard() 1.578 +{ 1.579 + MOZ_ASSERT(NS_IsMainThread()); 1.580 + 1.581 + // As soon as an image becomes animated, it becomes non-discardable and any 1.582 + // timers are cancelled. 1.583 + NS_ABORT_IF_FALSE(!mAnim, "Asked to discard for animated image!"); 1.584 + 1.585 + // Delete all the decoded frames, then clear the array. 1.586 + ClearFrames(); 1.587 +} 1.588 + 1.589 +size_t 1.590 +FrameBlender::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation, 1.591 + MallocSizeOf aMallocSizeOf) const 1.592 +{ 1.593 + size_t n = mFrames->SizeOfDecodedWithComputedFallbackIfHeap(aLocation, aMallocSizeOf); 1.594 + 1.595 + if (mAnim) { 1.596 + if (mAnim->compositingFrame) { 1.597 + n += mAnim->compositingFrame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf); 1.598 + } 1.599 + if (mAnim->compositingPrevFrame) { 1.600 + n += mAnim->compositingPrevFrame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf); 1.601 + } 1.602 + } 1.603 + 1.604 + return n; 1.605 +} 1.606 + 1.607 +void 1.608 +FrameBlender::ResetAnimation() 1.609 +{ 1.610 + if (mAnim) { 1.611 + mAnim->lastCompositedFrameIndex = -1; 1.612 + } 1.613 +} 1.614 + 1.615 +} // namespace image 1.616 +} // namespace mozilla