michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "FrameBlender.h" michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "MainThreadUtils.h" michael@0: michael@0: #include "pixman.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::image; michael@0: michael@0: namespace mozilla { michael@0: namespace image { michael@0: michael@0: FrameBlender::FrameBlender(FrameSequence* aSequenceToUse /* = nullptr */) michael@0: : mFrames(aSequenceToUse) michael@0: , mAnim(nullptr) michael@0: , mLoopCount(-1) michael@0: { michael@0: if (!mFrames) { michael@0: mFrames = new FrameSequence(); michael@0: } michael@0: } michael@0: michael@0: FrameBlender::~FrameBlender() michael@0: { michael@0: delete mAnim; michael@0: } michael@0: michael@0: already_AddRefed michael@0: FrameBlender::GetFrameSequence() michael@0: { michael@0: nsRefPtr seq(mFrames); michael@0: return seq.forget(); michael@0: } michael@0: michael@0: imgFrame* michael@0: FrameBlender::GetFrame(uint32_t framenum) const michael@0: { michael@0: if (!mAnim) { michael@0: NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!"); michael@0: return mFrames->GetFrame(0); michael@0: } michael@0: if (mAnim->lastCompositedFrameIndex == int32_t(framenum)) michael@0: return mAnim->compositingFrame; michael@0: return mFrames->GetFrame(framenum); michael@0: } michael@0: michael@0: imgFrame* michael@0: FrameBlender::RawGetFrame(uint32_t framenum) const michael@0: { michael@0: if (!mAnim) { michael@0: NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!"); michael@0: return mFrames->GetFrame(0); michael@0: } michael@0: michael@0: return mFrames->GetFrame(framenum); michael@0: } michael@0: michael@0: uint32_t michael@0: FrameBlender::GetNumFrames() const michael@0: { michael@0: return mFrames->GetNumFrames(); michael@0: } michael@0: michael@0: int32_t michael@0: FrameBlender::GetTimeoutForFrame(uint32_t framenum) const michael@0: { michael@0: const int32_t timeout = RawGetFrame(framenum)->GetRawTimeout(); michael@0: // Ensure a minimal time between updates so we don't throttle the UI thread. michael@0: // consider 0 == unspecified and make it fast but not too fast. Unless we have michael@0: // a single loop GIF. See bug 890743, bug 125137, bug 139677, and bug 207059. michael@0: // The behavior of recent IE and Opera versions seems to be: michael@0: // IE 6/Win: michael@0: // 10 - 50ms go 100ms michael@0: // >50ms go correct speed michael@0: // Opera 7 final/Win: michael@0: // 10ms goes 100ms michael@0: // >10ms go correct speed michael@0: // It seems that there are broken tools out there that set a 0ms or 10ms michael@0: // timeout when they really want a "default" one. So munge values in that michael@0: // range. michael@0: if (timeout >= 0 && timeout <= 10 && mLoopCount != 0) michael@0: return 100; michael@0: return timeout; michael@0: } michael@0: michael@0: void michael@0: FrameBlender::SetLoopCount(int32_t aLoopCount) michael@0: { michael@0: mLoopCount = aLoopCount; michael@0: } michael@0: michael@0: int32_t michael@0: FrameBlender::GetLoopCount() const michael@0: { michael@0: return mLoopCount; michael@0: } michael@0: michael@0: void michael@0: FrameBlender::RemoveFrame(uint32_t framenum) michael@0: { michael@0: NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Deleting invalid frame!"); michael@0: michael@0: mFrames->RemoveFrame(framenum); michael@0: } michael@0: michael@0: void michael@0: FrameBlender::ClearFrames() michael@0: { michael@0: // Forget our old frame sequence, letting whoever else has it deal with it. michael@0: mFrames = new FrameSequence(); michael@0: } michael@0: michael@0: void michael@0: FrameBlender::InsertFrame(uint32_t framenum, imgFrame* aFrame) michael@0: { michael@0: NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Inserting invalid frame!"); michael@0: mFrames->InsertFrame(framenum, aFrame); michael@0: if (GetNumFrames() > 1) { michael@0: EnsureAnimExists(); michael@0: } michael@0: } michael@0: michael@0: imgFrame* michael@0: FrameBlender::SwapFrame(uint32_t framenum, imgFrame* aFrame) michael@0: { michael@0: NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Swapping invalid frame!"); michael@0: michael@0: imgFrame* ret; michael@0: michael@0: // Steal the imgFrame from wherever it's currently stored michael@0: if (mAnim && mAnim->lastCompositedFrameIndex == int32_t(framenum)) { michael@0: ret = mAnim->compositingFrame.Forget(); michael@0: mAnim->lastCompositedFrameIndex = -1; michael@0: nsAutoPtr toDelete(mFrames->SwapFrame(framenum, aFrame)); michael@0: } else { michael@0: ret = mFrames->SwapFrame(framenum, aFrame); michael@0: } michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: void michael@0: FrameBlender::EnsureAnimExists() michael@0: { michael@0: if (!mAnim) { michael@0: // Create the animation context michael@0: mAnim = new Anim(); michael@0: michael@0: // We should only get into this code path directly after we've created our michael@0: // second frame (hence we know we're animated). michael@0: MOZ_ASSERT(GetNumFrames() == 2); michael@0: } michael@0: } michael@0: michael@0: //****************************************************************************** michael@0: // DoBlend gets called when the timer for animation get fired and we have to michael@0: // update the composited frame of the animation. michael@0: bool michael@0: FrameBlender::DoBlend(nsIntRect* aDirtyRect, michael@0: uint32_t aPrevFrameIndex, michael@0: uint32_t aNextFrameIndex) michael@0: { michael@0: if (!aDirtyRect) { michael@0: return false; michael@0: } michael@0: michael@0: const FrameDataPair& prevFrame = mFrames->GetFrame(aPrevFrameIndex); michael@0: const FrameDataPair& nextFrame = mFrames->GetFrame(aNextFrameIndex); michael@0: if (!prevFrame.HasFrameData() || !nextFrame.HasFrameData()) { michael@0: return false; michael@0: } michael@0: michael@0: int32_t prevFrameDisposalMethod = prevFrame->GetFrameDisposalMethod(); michael@0: if (prevFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious && michael@0: !mAnim->compositingPrevFrame) michael@0: prevFrameDisposalMethod = FrameBlender::kDisposeClear; michael@0: michael@0: nsIntRect prevFrameRect = prevFrame->GetRect(); michael@0: bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 && michael@0: prevFrameRect.width == mSize.width && michael@0: prevFrameRect.height == mSize.height); michael@0: michael@0: // Optimization: DisposeClearAll if the previous frame is the same size as michael@0: // container and it's clearing itself michael@0: if (isFullPrevFrame && michael@0: (prevFrameDisposalMethod == FrameBlender::kDisposeClear)) michael@0: prevFrameDisposalMethod = FrameBlender::kDisposeClearAll; michael@0: michael@0: int32_t nextFrameDisposalMethod = nextFrame->GetFrameDisposalMethod(); michael@0: nsIntRect nextFrameRect = nextFrame->GetRect(); michael@0: bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 && michael@0: nextFrameRect.width == mSize.width && michael@0: nextFrameRect.height == mSize.height); michael@0: michael@0: if (!nextFrame->GetIsPaletted()) { michael@0: // Optimization: Skip compositing if the previous frame wants to clear the michael@0: // whole image michael@0: if (prevFrameDisposalMethod == FrameBlender::kDisposeClearAll) { michael@0: aDirtyRect->SetRect(0, 0, mSize.width, mSize.height); michael@0: return true; michael@0: } michael@0: michael@0: // Optimization: Skip compositing if this frame is the same size as the michael@0: // container and it's fully drawing over prev frame (no alpha) michael@0: if (isFullNextFrame && michael@0: (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) && michael@0: !nextFrame->GetHasAlpha()) { michael@0: aDirtyRect->SetRect(0, 0, mSize.width, mSize.height); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // Calculate area that needs updating michael@0: switch (prevFrameDisposalMethod) { michael@0: default: michael@0: case FrameBlender::kDisposeNotSpecified: michael@0: case FrameBlender::kDisposeKeep: michael@0: *aDirtyRect = nextFrameRect; michael@0: break; michael@0: michael@0: case FrameBlender::kDisposeClearAll: michael@0: // Whole image container is cleared michael@0: aDirtyRect->SetRect(0, 0, mSize.width, mSize.height); michael@0: break; michael@0: michael@0: case FrameBlender::kDisposeClear: michael@0: // Calc area that needs to be redrawn (the combination of previous and michael@0: // this frame) michael@0: // XXX - This could be done with multiple framechanged calls michael@0: // Having prevFrame way at the top of the image, and nextFrame michael@0: // way at the bottom, and both frames being small, we'd be michael@0: // telling framechanged to refresh the whole image when only two michael@0: // small areas are needed. michael@0: aDirtyRect->UnionRect(nextFrameRect, prevFrameRect); michael@0: break; michael@0: michael@0: case FrameBlender::kDisposeRestorePrevious: michael@0: aDirtyRect->SetRect(0, 0, mSize.width, mSize.height); michael@0: break; michael@0: } michael@0: michael@0: // Optimization: michael@0: // Skip compositing if the last composited frame is this frame michael@0: // (Only one composited frame was made for this animation. Example: michael@0: // Only Frame 3 of a 10 frame image required us to build a composite frame michael@0: // On the second loop, we do not need to rebuild the frame michael@0: // since it's still sitting in compositingFrame) michael@0: if (mAnim->lastCompositedFrameIndex == int32_t(aNextFrameIndex)) { michael@0: return true; michael@0: } michael@0: michael@0: bool needToBlankComposite = false; michael@0: michael@0: // Create the Compositing Frame michael@0: if (!mAnim->compositingFrame) { michael@0: mAnim->compositingFrame.SetFrame(new imgFrame()); michael@0: nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height, michael@0: gfxImageFormat::ARGB32); michael@0: if (NS_FAILED(rv)) { michael@0: mAnim->compositingFrame.SetFrame(nullptr); michael@0: return false; michael@0: } michael@0: mAnim->compositingFrame.LockAndGetData(); michael@0: needToBlankComposite = true; michael@0: } else if (int32_t(aNextFrameIndex) != mAnim->lastCompositedFrameIndex+1) { michael@0: michael@0: // If we are not drawing on top of last composited frame, michael@0: // then we are building a new composite frame, so let's clear it first. michael@0: needToBlankComposite = true; michael@0: } michael@0: michael@0: // More optimizations possible when next frame is not transparent michael@0: // But if the next frame has FrameBlender::kDisposeRestorePrevious, michael@0: // this "no disposal" optimization is not possible, michael@0: // because the frame in "after disposal operation" state michael@0: // needs to be stored in compositingFrame, so it can be michael@0: // copied into compositingPrevFrame later. michael@0: bool doDisposal = true; michael@0: if (!nextFrame->GetHasAlpha() && michael@0: nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) { michael@0: if (isFullNextFrame) { michael@0: // Optimization: No need to dispose prev.frame when michael@0: // next frame is full frame and not transparent. michael@0: doDisposal = false; michael@0: // No need to blank the composite frame michael@0: needToBlankComposite = false; michael@0: } else { michael@0: if ((prevFrameRect.x >= nextFrameRect.x) && michael@0: (prevFrameRect.y >= nextFrameRect.y) && michael@0: (prevFrameRect.x + prevFrameRect.width <= nextFrameRect.x + nextFrameRect.width) && michael@0: (prevFrameRect.y + prevFrameRect.height <= nextFrameRect.y + nextFrameRect.height)) { michael@0: // Optimization: No need to dispose prev.frame when michael@0: // next frame fully overlaps previous frame. michael@0: doDisposal = false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (doDisposal) { michael@0: // Dispose of previous: clear, restore, or keep (copy) michael@0: switch (prevFrameDisposalMethod) { michael@0: case FrameBlender::kDisposeClear: michael@0: if (needToBlankComposite) { michael@0: // If we just created the composite, it could have anything in its michael@0: // buffer. Clear whole frame michael@0: ClearFrame(mAnim->compositingFrame.GetFrameData(), michael@0: mAnim->compositingFrame.GetFrame()->GetRect()); michael@0: } else { michael@0: // Only blank out previous frame area (both color & Mask/Alpha) michael@0: ClearFrame(mAnim->compositingFrame.GetFrameData(), michael@0: mAnim->compositingFrame.GetFrame()->GetRect(), michael@0: prevFrameRect); michael@0: } michael@0: break; michael@0: michael@0: case FrameBlender::kDisposeClearAll: michael@0: ClearFrame(mAnim->compositingFrame.GetFrameData(), michael@0: mAnim->compositingFrame.GetFrame()->GetRect()); michael@0: break; michael@0: michael@0: case FrameBlender::kDisposeRestorePrevious: michael@0: // It would be better to copy only the area changed back to michael@0: // compositingFrame. michael@0: if (mAnim->compositingPrevFrame) { michael@0: CopyFrameImage(mAnim->compositingPrevFrame.GetFrameData(), michael@0: mAnim->compositingPrevFrame.GetFrame()->GetRect(), michael@0: mAnim->compositingFrame.GetFrameData(), michael@0: mAnim->compositingFrame.GetFrame()->GetRect()); michael@0: michael@0: // destroy only if we don't need it for this frame's disposal michael@0: if (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) michael@0: mAnim->compositingPrevFrame.SetFrame(nullptr); michael@0: } else { michael@0: ClearFrame(mAnim->compositingFrame.GetFrameData(), michael@0: mAnim->compositingFrame.GetFrame()->GetRect()); michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: // Copy previous frame into compositingFrame before we put the new frame on top michael@0: // Assumes that the previous frame represents a full frame (it could be michael@0: // smaller in size than the container, as long as the frame before it erased michael@0: // itself) michael@0: // Note: Frame 1 never gets into DoBlend(), so (aNextFrameIndex - 1) will michael@0: // always be a valid frame number. michael@0: if (mAnim->lastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) { michael@0: if (isFullPrevFrame && !prevFrame->GetIsPaletted()) { michael@0: // Just copy the bits michael@0: CopyFrameImage(prevFrame.GetFrameData(), michael@0: prevFrame.GetFrame()->GetRect(), michael@0: mAnim->compositingFrame.GetFrameData(), michael@0: mAnim->compositingFrame.GetFrame()->GetRect()); michael@0: } else { michael@0: if (needToBlankComposite) { michael@0: // Only blank composite when prev is transparent or not full. michael@0: if (prevFrame->GetHasAlpha() || !isFullPrevFrame) { michael@0: ClearFrame(mAnim->compositingFrame.GetFrameData(), michael@0: mAnim->compositingFrame.GetFrame()->GetRect()); michael@0: } michael@0: } michael@0: DrawFrameTo(prevFrame.GetFrameData(), prevFrameRect, michael@0: prevFrame.GetFrame()->PaletteDataLength(), michael@0: prevFrame.GetFrame()->GetHasAlpha(), michael@0: mAnim->compositingFrame.GetFrameData(), michael@0: mAnim->compositingFrame.GetFrame()->GetRect(), michael@0: FrameBlendMethod(prevFrame.GetFrame()->GetBlendMethod())); michael@0: } michael@0: } michael@0: } michael@0: } else if (needToBlankComposite) { michael@0: // If we just created the composite, it could have anything in it's michael@0: // buffers. Clear them michael@0: ClearFrame(mAnim->compositingFrame.GetFrameData(), michael@0: mAnim->compositingFrame.GetFrame()->GetRect()); michael@0: } michael@0: michael@0: // Check if the frame we are composing wants the previous image restored afer michael@0: // it is done. Don't store it (again) if last frame wanted its image restored michael@0: // too michael@0: if ((nextFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious) && michael@0: (prevFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)) { michael@0: // We are storing the whole image. michael@0: // It would be better if we just stored the area that nextFrame is going to michael@0: // overwrite. michael@0: if (!mAnim->compositingPrevFrame) { michael@0: mAnim->compositingPrevFrame.SetFrame(new imgFrame()); michael@0: nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height, michael@0: gfxImageFormat::ARGB32); michael@0: if (NS_FAILED(rv)) { michael@0: mAnim->compositingPrevFrame.SetFrame(nullptr); michael@0: return false; michael@0: } michael@0: michael@0: mAnim->compositingPrevFrame.LockAndGetData(); michael@0: } michael@0: michael@0: CopyFrameImage(mAnim->compositingFrame.GetFrameData(), michael@0: mAnim->compositingFrame.GetFrame()->GetRect(), michael@0: mAnim->compositingPrevFrame.GetFrameData(), michael@0: mAnim->compositingPrevFrame.GetFrame()->GetRect()); michael@0: } michael@0: michael@0: // blit next frame into it's correct spot michael@0: DrawFrameTo(nextFrame.GetFrameData(), nextFrameRect, michael@0: nextFrame.GetFrame()->PaletteDataLength(), michael@0: nextFrame.GetFrame()->GetHasAlpha(), michael@0: mAnim->compositingFrame.GetFrameData(), michael@0: mAnim->compositingFrame.GetFrame()->GetRect(), michael@0: FrameBlendMethod(nextFrame.GetFrame()->GetBlendMethod())); michael@0: michael@0: // Set timeout of CompositeFrame to timeout of frame we just composed michael@0: // Bug 177948 michael@0: int32_t timeout = nextFrame->GetRawTimeout(); michael@0: mAnim->compositingFrame->SetRawTimeout(timeout); michael@0: michael@0: // Tell the image that it is fully 'downloaded'. michael@0: nsresult rv = mAnim->compositingFrame->ImageUpdated(mAnim->compositingFrame->GetRect()); michael@0: if (NS_FAILED(rv)) { michael@0: return false; michael@0: } michael@0: michael@0: mAnim->lastCompositedFrameIndex = int32_t(aNextFrameIndex); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: //****************************************************************************** michael@0: // Fill aFrame with black. Does also clears the mask. michael@0: void michael@0: FrameBlender::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect) michael@0: { michael@0: if (!aFrameData) michael@0: return; michael@0: michael@0: memset(aFrameData, 0, aFrameRect.width * aFrameRect.height * 4); michael@0: } michael@0: michael@0: //****************************************************************************** michael@0: void michael@0: FrameBlender::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect, const nsIntRect& aRectToClear) michael@0: { michael@0: if (!aFrameData || aFrameRect.width <= 0 || aFrameRect.height <= 0 || michael@0: aRectToClear.width <= 0 || aRectToClear.height <= 0) { michael@0: return; michael@0: } michael@0: michael@0: nsIntRect toClear = aFrameRect.Intersect(aRectToClear); michael@0: if (toClear.IsEmpty()) { michael@0: return; michael@0: } michael@0: michael@0: uint32_t bytesPerRow = aFrameRect.width * 4; michael@0: for (int row = toClear.y; row < toClear.y + toClear.height; ++row) { michael@0: memset(aFrameData + toClear.x * 4 + row * bytesPerRow, 0, toClear.width * 4); michael@0: } michael@0: } michael@0: michael@0: //****************************************************************************** michael@0: // Whether we succeed or fail will not cause a crash, and there's not much michael@0: // we can do about a failure, so there we don't return a nsresult michael@0: bool michael@0: FrameBlender::CopyFrameImage(const uint8_t *aDataSrc, const nsIntRect& aRectSrc, michael@0: uint8_t *aDataDest, const nsIntRect& aRectDest) michael@0: { michael@0: uint32_t dataLengthSrc = aRectSrc.width * aRectSrc.height * 4; michael@0: uint32_t dataLengthDest = aRectDest.width * aRectDest.height * 4; michael@0: michael@0: if (!aDataDest || !aDataSrc || dataLengthSrc != dataLengthDest) { michael@0: return false; michael@0: } michael@0: michael@0: memcpy(aDataDest, aDataSrc, dataLengthDest); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: nsresult michael@0: FrameBlender::DrawFrameTo(const uint8_t *aSrcData, const nsIntRect& aSrcRect, michael@0: uint32_t aSrcPaletteLength, bool aSrcHasAlpha, michael@0: uint8_t *aDstPixels, const nsIntRect& aDstRect, michael@0: FrameBlender::FrameBlendMethod aBlendMethod) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aSrcData); michael@0: NS_ENSURE_ARG_POINTER(aDstPixels); michael@0: michael@0: // According to both AGIF and APNG specs, offsets are unsigned michael@0: if (aSrcRect.x < 0 || aSrcRect.y < 0) { michael@0: NS_WARNING("FrameBlender::DrawFrameTo: negative offsets not allowed"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: // Outside the destination frame, skip it michael@0: if ((aSrcRect.x > aDstRect.width) || (aSrcRect.y > aDstRect.height)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (aSrcPaletteLength) { michael@0: // Larger than the destination frame, clip it michael@0: int32_t width = std::min(aSrcRect.width, aDstRect.width - aSrcRect.x); michael@0: int32_t height = std::min(aSrcRect.height, aDstRect.height - aSrcRect.y); michael@0: michael@0: // The clipped image must now fully fit within destination image frame michael@0: NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) && michael@0: (aSrcRect.x + width <= aDstRect.width) && michael@0: (aSrcRect.y + height <= aDstRect.height), michael@0: "FrameBlender::DrawFrameTo: Invalid aSrcRect"); michael@0: michael@0: // clipped image size may be smaller than source, but not larger michael@0: NS_ASSERTION((width <= aSrcRect.width) && (height <= aSrcRect.height), michael@0: "FrameBlender::DrawFrameTo: source must be smaller than dest"); michael@0: michael@0: // Get pointers to image data michael@0: const uint8_t *srcPixels = aSrcData + aSrcPaletteLength; michael@0: uint32_t *dstPixels = reinterpret_cast(aDstPixels); michael@0: const uint32_t *colormap = reinterpret_cast(aSrcData); michael@0: michael@0: // Skip to the right offset michael@0: dstPixels += aSrcRect.x + (aSrcRect.y * aDstRect.width); michael@0: if (!aSrcHasAlpha) { michael@0: for (int32_t r = height; r > 0; --r) { michael@0: for (int32_t c = 0; c < width; c++) { michael@0: dstPixels[c] = colormap[srcPixels[c]]; michael@0: } michael@0: // Go to the next row in the source resp. destination image michael@0: srcPixels += aSrcRect.width; michael@0: dstPixels += aDstRect.width; michael@0: } michael@0: } else { michael@0: for (int32_t r = height; r > 0; --r) { michael@0: for (int32_t c = 0; c < width; c++) { michael@0: const uint32_t color = colormap[srcPixels[c]]; michael@0: if (color) michael@0: dstPixels[c] = color; michael@0: } michael@0: // Go to the next row in the source resp. destination image michael@0: srcPixels += aSrcRect.width; michael@0: dstPixels += aDstRect.width; michael@0: } michael@0: } michael@0: } else { michael@0: pixman_image_t* src = pixman_image_create_bits(aSrcHasAlpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8, michael@0: aSrcRect.width, michael@0: aSrcRect.height, michael@0: reinterpret_cast(const_cast(aSrcData)), michael@0: aSrcRect.width * 4); michael@0: pixman_image_t* dst = pixman_image_create_bits(PIXMAN_a8r8g8b8, michael@0: aDstRect.width, michael@0: aDstRect.height, michael@0: reinterpret_cast(aDstPixels), michael@0: aDstRect.width * 4); michael@0: michael@0: pixman_image_composite32(aBlendMethod == FrameBlender::kBlendSource ? PIXMAN_OP_SRC : PIXMAN_OP_OVER, michael@0: src, michael@0: nullptr, michael@0: dst, michael@0: 0, 0, michael@0: 0, 0, michael@0: aSrcRect.x, aSrcRect.y, michael@0: aSrcRect.width, aSrcRect.height); michael@0: michael@0: pixman_image_unref(src); michael@0: pixman_image_unref(dst); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: FrameBlender::Discard() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // As soon as an image becomes animated, it becomes non-discardable and any michael@0: // timers are cancelled. michael@0: NS_ABORT_IF_FALSE(!mAnim, "Asked to discard for animated image!"); michael@0: michael@0: // Delete all the decoded frames, then clear the array. michael@0: ClearFrames(); michael@0: } michael@0: michael@0: size_t michael@0: FrameBlender::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation, michael@0: MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = mFrames->SizeOfDecodedWithComputedFallbackIfHeap(aLocation, aMallocSizeOf); michael@0: michael@0: if (mAnim) { michael@0: if (mAnim->compositingFrame) { michael@0: n += mAnim->compositingFrame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf); michael@0: } michael@0: if (mAnim->compositingPrevFrame) { michael@0: n += mAnim->compositingPrevFrame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf); michael@0: } michael@0: } michael@0: michael@0: return n; michael@0: } michael@0: michael@0: void michael@0: FrameBlender::ResetAnimation() michael@0: { michael@0: if (mAnim) { michael@0: mAnim->lastCompositedFrameIndex = -1; michael@0: } michael@0: } michael@0: michael@0: } // namespace image michael@0: } // namespace mozilla