Backed out changeset 2fd42e5e2920 (bug 1057904) for bc3 test failures on a CLOSED TREE

This commit is contained in:
Carsten "Tomcat" Book 2014-11-26 11:57:41 +01:00
parent bb82f27e3e
commit 77107c07e9
8 changed files with 483 additions and 161 deletions

View File

@ -6,7 +6,6 @@
#include "FrameBlender.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Move.h"
#include "MainThreadUtils.h"
#include "pixman.h"
@ -17,10 +16,14 @@ using namespace gfx;
namespace image {
FrameBlender::FrameBlender()
: mAnim(nullptr)
FrameBlender::FrameBlender(FrameSequence* aSequenceToUse /* = nullptr */)
: mFrames(aSequenceToUse)
, mAnim(nullptr)
, mLoopCount(-1)
{
if (!mFrames) {
mFrames = new FrameSequence();
}
}
FrameBlender::~FrameBlender()
@ -28,57 +31,47 @@ FrameBlender::~FrameBlender()
delete mAnim;
}
already_AddRefed<imgFrame>
FrameBlender::GetFrame(uint32_t aFrameNum)
already_AddRefed<FrameSequence>
FrameBlender::GetFrameSequence()
{
if (mAnim && mAnim->lastCompositedFrameIndex == int32_t(aFrameNum)) {
nsRefPtr<imgFrame> frame = mAnim->compositingFrame.get();
return frame.forget();
}
return RawGetFrame(aFrameNum);
nsRefPtr<FrameSequence> seq(mFrames);
return seq.forget();
}
already_AddRefed<imgFrame>
FrameBlender::RawGetFrame(uint32_t aFrameNum)
FrameBlender::GetFrame(uint32_t framenum) const
{
if (!mAnim) {
NS_ASSERTION(aFrameNum == 0,
"Don't ask for a frame > 0 if we're not animated!");
aFrameNum = 0;
NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
return mFrames->GetFrame(0).GetFrame();
}
if (aFrameNum >= mFrames.Length()) {
return nullptr;
if (mAnim->lastCompositedFrameIndex == int32_t(framenum)) {
return mAnim->compositingFrame.GetFrame();
}
nsRefPtr<imgFrame> frame = mFrames[aFrameNum].get();
return frame.forget();
return mFrames->GetFrame(framenum).GetFrame();
}
int32_t
FrameBlender::GetFrameDisposalMethod(uint32_t aFrameNum) const
already_AddRefed<imgFrame>
FrameBlender::RawGetFrame(uint32_t framenum) const
{
if (!mAnim) {
NS_ASSERTION(aFrameNum == 0,
"Don't ask for a frame > 0 if we're not animated!");
aFrameNum = 0;
NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
return mFrames->GetFrame(0).GetFrame();
}
if (aFrameNum >= mFrames.Length()) {
return FrameBlender::kDisposeNotSpecified;
}
return mFrames[aFrameNum]->GetFrameDisposalMethod();
return mFrames->GetFrame(framenum).GetFrame();
}
uint32_t
FrameBlender::GetNumFrames() const
{
return mFrames.Length();
return mFrames->GetNumFrames();
}
int32_t
FrameBlender::GetTimeoutForFrame(uint32_t aFrameNum)
FrameBlender::GetTimeoutForFrame(uint32_t framenum) const
{
nsRefPtr<imgFrame> frame = RawGetFrame(aFrameNum);
nsRefPtr<imgFrame> frame = RawGetFrame(framenum);
const int32_t timeout = frame->GetRawTimeout();
// Ensure a minimal time between updates so we don't throttle the UI thread.
// consider 0 == unspecified and make it fast but not too fast. Unless we have
// a single loop GIF. See bug 890743, bug 125137, bug 139677, and bug 207059.
@ -92,10 +85,8 @@ FrameBlender::GetTimeoutForFrame(uint32_t aFrameNum)
// It seems that there are broken tools out there that set a 0ms or 10ms
// timeout when they really want a "default" one. So munge values in that
// range.
if (timeout >= 0 && timeout <= 10 && mLoopCount != 0) {
if (timeout >= 0 && timeout <= 10 && mLoopCount != 0)
return 100;
}
return timeout;
}
@ -112,33 +103,60 @@ FrameBlender::GetLoopCount() const
}
void
FrameBlender::RemoveFrame(uint32_t aFrameNum)
FrameBlender::RemoveFrame(uint32_t framenum)
{
MOZ_ASSERT(aFrameNum < GetNumFrames(), "Deleting invalid frame!");
mFrames.RemoveElementAt(aFrameNum);
NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Deleting invalid frame!");
mFrames->RemoveFrame(framenum);
}
void
FrameBlender::ClearFrames()
{
mFrames.Clear();
mFrames.Compact();
// Forget our old frame sequence, letting whoever else has it deal with it.
mFrames = new FrameSequence();
}
void
FrameBlender::InsertFrame(uint32_t aFrameNum, RawAccessFrameRef&& aRef)
FrameBlender::InsertFrame(uint32_t framenum, imgFrame* aFrame)
{
MOZ_ASSERT(aRef, "Need a reference to a frame");
MOZ_ASSERT(aFrameNum <= GetNumFrames(), "Inserting invalid frame");
NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Inserting invalid frame!");
mFrames->InsertFrame(framenum, aFrame);
if (GetNumFrames() > 1) {
EnsureAnimExists();
}
}
mFrames.InsertElementAt(aFrameNum, Move(aRef));
if (GetNumFrames() == 2) {
MOZ_ASSERT(!mAnim, "Shouldn't have an animation context yet");
mAnim = new Anim();
already_AddRefed<imgFrame>
FrameBlender::SwapFrame(uint32_t framenum, imgFrame* aFrame)
{
NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Swapping invalid frame!");
nsRefPtr<imgFrame> ret;
// Steal the imgFrame from wherever it's currently stored
if (mAnim && mAnim->lastCompositedFrameIndex == int32_t(framenum)) {
ret = mAnim->compositingFrame.Forget();
mAnim->lastCompositedFrameIndex = -1;
nsRefPtr<imgFrame> toDelete(mFrames->SwapFrame(framenum, aFrame));
} else {
ret = mFrames->SwapFrame(framenum, aFrame);
}
MOZ_ASSERT(GetNumFrames() < 2 || mAnim,
"If we're animated we should have an animation context now");
return ret.forget();
}
void
FrameBlender::EnsureAnimExists()
{
if (!mAnim) {
// Create the animation context
mAnim = new Anim();
// We should only get into this code path directly after we've created our
// second frame (hence we know we're animated).
MOZ_ASSERT(GetNumFrames() == 2);
}
}
//******************************************************************************
@ -149,17 +167,21 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
uint32_t aPrevFrameIndex,
uint32_t aNextFrameIndex)
{
nsRefPtr<imgFrame> prevFrame = GetFrame(aPrevFrameIndex);
nsRefPtr<imgFrame> nextFrame = GetFrame(aNextFrameIndex);
MOZ_ASSERT(prevFrame && nextFrame, "Should have frames here");
int32_t prevFrameDisposalMethod = GetFrameDisposalMethod(aPrevFrameIndex);
if (prevFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious &&
!mAnim->compositingPrevFrame) {
prevFrameDisposalMethod = FrameBlender::kDisposeClear;
if (!aDirtyRect) {
return false;
}
const FrameDataPair& prevFrame = mFrames->GetFrame(aPrevFrameIndex);
const FrameDataPair& nextFrame = mFrames->GetFrame(aNextFrameIndex);
if (!prevFrame.HasFrameData() || !nextFrame.HasFrameData()) {
return false;
}
int32_t prevFrameDisposalMethod = prevFrame->GetFrameDisposalMethod();
if (prevFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious &&
!mAnim->compositingPrevFrame)
prevFrameDisposalMethod = FrameBlender::kDisposeClear;
nsIntRect prevFrameRect = prevFrame->GetRect();
bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
prevFrameRect.width == mSize.width &&
@ -168,11 +190,10 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
// Optimization: DisposeClearAll if the previous frame is the same size as
// container and it's clearing itself
if (isFullPrevFrame &&
(prevFrameDisposalMethod == FrameBlender::kDisposeClear)) {
(prevFrameDisposalMethod == FrameBlender::kDisposeClear))
prevFrameDisposalMethod = FrameBlender::kDisposeClearAll;
}
int32_t nextFrameDisposalMethod = GetFrameDisposalMethod(aNextFrameIndex);
int32_t nextFrameDisposalMethod = nextFrame->GetFrameDisposalMethod();
nsIntRect nextFrameRect = nextFrame->GetRect();
bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
nextFrameRect.width == mSize.width &&
@ -239,13 +260,14 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
// Create the Compositing Frame
if (!mAnim->compositingFrame) {
nsRefPtr<imgFrame> newFrame = new imgFrame;
nsresult rv = newFrame->InitForDecoder(mSize, SurfaceFormat::B8G8R8A8);
mAnim->compositingFrame.SetFrame(new imgFrame());
nsresult rv =
mAnim->compositingFrame->InitForDecoder(mSize, SurfaceFormat::B8G8R8A8);
if (NS_FAILED(rv)) {
mAnim->compositingFrame.reset();
mAnim->compositingFrame.SetFrame(nullptr);
return false;
}
mAnim->compositingFrame = newFrame->RawAccessRef();
mAnim->compositingFrame.LockAndGetData();
needToBlankComposite = true;
} else if (int32_t(aNextFrameIndex) != mAnim->lastCompositedFrameIndex+1) {
@ -288,18 +310,18 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
if (needToBlankComposite) {
// If we just created the composite, it could have anything in its
// buffer. Clear whole frame
ClearFrame(mAnim->compositingFrame->GetRawData(),
ClearFrame(mAnim->compositingFrame.GetFrameData(),
mAnim->compositingFrame->GetRect());
} else {
// Only blank out previous frame area (both color & Mask/Alpha)
ClearFrame(mAnim->compositingFrame->GetRawData(),
ClearFrame(mAnim->compositingFrame.GetFrameData(),
mAnim->compositingFrame->GetRect(),
prevFrameRect);
}
break;
case FrameBlender::kDisposeClearAll:
ClearFrame(mAnim->compositingFrame->GetRawData(),
ClearFrame(mAnim->compositingFrame.GetFrameData(),
mAnim->compositingFrame->GetRect());
break;
@ -307,16 +329,16 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
// It would be better to copy only the area changed back to
// compositingFrame.
if (mAnim->compositingPrevFrame) {
CopyFrameImage(mAnim->compositingPrevFrame->GetRawData(),
CopyFrameImage(mAnim->compositingPrevFrame.GetFrameData(),
mAnim->compositingPrevFrame->GetRect(),
mAnim->compositingFrame->GetRawData(),
mAnim->compositingFrame.GetFrameData(),
mAnim->compositingFrame->GetRect());
// destroy only if we don't need it for this frame's disposal
if (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)
mAnim->compositingPrevFrame.reset();
mAnim->compositingPrevFrame.SetFrame(nullptr);
} else {
ClearFrame(mAnim->compositingFrame->GetRawData(),
ClearFrame(mAnim->compositingFrame.GetFrameData(),
mAnim->compositingFrame->GetRect());
}
break;
@ -331,22 +353,22 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
if (mAnim->lastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) {
if (isFullPrevFrame && !prevFrame->GetIsPaletted()) {
// Just copy the bits
CopyFrameImage(prevFrame->GetRawData(),
CopyFrameImage(prevFrame.GetFrameData(),
prevFrame->GetRect(),
mAnim->compositingFrame->GetRawData(),
mAnim->compositingFrame.GetFrameData(),
mAnim->compositingFrame->GetRect());
} else {
if (needToBlankComposite) {
// Only blank composite when prev is transparent or not full.
if (prevFrame->GetHasAlpha() || !isFullPrevFrame) {
ClearFrame(mAnim->compositingFrame->GetRawData(),
ClearFrame(mAnim->compositingFrame.GetFrameData(),
mAnim->compositingFrame->GetRect());
}
}
DrawFrameTo(prevFrame->GetRawData(), prevFrameRect,
DrawFrameTo(prevFrame.GetFrameData(), prevFrameRect,
prevFrame->PaletteDataLength(),
prevFrame->GetHasAlpha(),
mAnim->compositingFrame->GetRawData(),
mAnim->compositingFrame.GetFrameData(),
mAnim->compositingFrame->GetRect(),
FrameBlendMethod(prevFrame->GetBlendMethod()));
}
@ -355,7 +377,7 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
} else if (needToBlankComposite) {
// If we just created the composite, it could have anything in it's
// buffers. Clear them
ClearFrame(mAnim->compositingFrame->GetRawData(),
ClearFrame(mAnim->compositingFrame.GetFrameData(),
mAnim->compositingFrame->GetRect());
}
@ -368,27 +390,29 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
// It would be better if we just stored the area that nextFrame is going to
// overwrite.
if (!mAnim->compositingPrevFrame) {
nsRefPtr<imgFrame> newFrame = new imgFrame;
nsresult rv = newFrame->InitForDecoder(mSize, SurfaceFormat::B8G8R8A8);
mAnim->compositingPrevFrame.SetFrame(new imgFrame());
nsresult rv =
mAnim->compositingPrevFrame->InitForDecoder(mSize,
SurfaceFormat::B8G8R8A8);
if (NS_FAILED(rv)) {
mAnim->compositingPrevFrame.reset();
mAnim->compositingPrevFrame.SetFrame(nullptr);
return false;
}
mAnim->compositingPrevFrame = newFrame->RawAccessRef();
mAnim->compositingPrevFrame.LockAndGetData();
}
CopyFrameImage(mAnim->compositingFrame->GetRawData(),
CopyFrameImage(mAnim->compositingFrame.GetFrameData(),
mAnim->compositingFrame->GetRect(),
mAnim->compositingPrevFrame->GetRawData(),
mAnim->compositingPrevFrame.GetFrameData(),
mAnim->compositingPrevFrame->GetRect());
}
// blit next frame into it's correct spot
DrawFrameTo(nextFrame->GetRawData(), nextFrameRect,
DrawFrameTo(nextFrame.GetFrameData(), nextFrameRect,
nextFrame->PaletteDataLength(),
nextFrame->GetHasAlpha(),
mAnim->compositingFrame->GetRawData(),
mAnim->compositingFrame.GetFrameData(),
mAnim->compositingFrame->GetRect(),
FrameBlendMethod(nextFrame->GetBlendMethod()));
@ -566,23 +590,14 @@ size_t
FrameBlender::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf) const
{
size_t n = 0;
for (uint32_t i = 0; i < mFrames.Length(); ++i) {
n += mFrames[i]->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation,
aMallocSizeOf);
}
size_t n = mFrames->SizeOfDecodedWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
if (mAnim) {
if (mAnim->compositingFrame) {
n += mAnim->compositingFrame
->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation,
aMallocSizeOf);
n += mAnim->compositingFrame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
}
if (mAnim->compositingPrevFrame) {
n += mAnim->compositingPrevFrame
->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation,
aMallocSizeOf);
n += mAnim->compositingPrevFrame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
}
}

View File

@ -9,12 +9,14 @@
#include "mozilla/MemoryReporting.h"
#include "gfxTypes.h"
#include "imgFrame.h"
#include "FrameSequence.h"
#include "nsCOMPtr.h"
namespace mozilla {
namespace image {
class imgFrame;
/**
* FrameBlender stores and gives access to imgFrames. It also knows how to
* blend frames from previous to next, looping if necessary.
@ -30,25 +32,28 @@ public:
*
* If aSequenceToUse is not specified, it will be allocated automatically.
*/
FrameBlender();
explicit FrameBlender(FrameSequence* aSequenceToUse = nullptr);
~FrameBlender();
bool DoBlend(nsIntRect* aDirtyRect, uint32_t aPrevFrameIndex,
uint32_t aNextFrameIndex);
already_AddRefed<FrameSequence> GetFrameSequence();
/**
* Get the @aIndex-th frame, including (if applicable) any results of
* blending.
*/
already_AddRefed<imgFrame> GetFrame(uint32_t aIndex);
already_AddRefed<imgFrame> GetFrame(uint32_t aIndex) const;
/**
* Get the @aIndex-th frame in the frame index, ignoring results of blending.
*/
already_AddRefed<imgFrame> RawGetFrame(uint32_t aIndex);
already_AddRefed<imgFrame> RawGetFrame(uint32_t aIndex) const;
void InsertFrame(uint32_t aFrameNum, RawAccessFrameRef&& aRef);
void RemoveFrame(uint32_t aFrameNum);
void InsertFrame(uint32_t framenum, imgFrame* aFrame);
void RemoveFrame(uint32_t framenum);
already_AddRefed<imgFrame> SwapFrame(uint32_t framenum, imgFrame* aFrame);
void ClearFrames();
/* The total number of frames in this image. */
@ -59,7 +64,7 @@ public:
* falls in between a certain range then the timeout is adjusted so that
* it's never 0. If the animation does not loop then no adjustments are made.
*/
int32_t GetTimeoutForFrame(uint32_t aFrameNum);
int32_t GetTimeoutForFrame(uint32_t framenum) const;
/*
* Set number of times to loop the image.
@ -107,15 +112,6 @@ public:
private:
/**
* Get the disposal method of the @aIndex-th frame.
*
* Note that it's not safe to use GetFrame(aIndex)->GetFrameDisposalMethod()
* instead, because the frame GetFrame() returns may be a blended frame which
* does not have the correct disposal method set.
*/
int32_t GetFrameDisposalMethod(uint32_t aIndex) const;
struct Anim
{
//! Track the last composited frame for Optimizations (See DoComposite code)
@ -129,7 +125,7 @@ private:
* lastCompositedFrameIndex to -1. Code assume that if
* lastCompositedFrameIndex >= 0 then compositingFrame exists.
*/
RawAccessFrameRef compositingFrame;
FrameDataPair compositingFrame;
/** the previous composited frame, for DISPOSE_RESTORE_PREVIOUS
*
@ -137,13 +133,15 @@ private:
* stored in cases where the image specifies it wants the last frame back
* when it's done with the current frame.
*/
RawAccessFrameRef compositingPrevFrame;
FrameDataPair compositingPrevFrame;
Anim() :
lastCompositedFrameIndex(-1)
{}
};
void EnsureAnimExists();
/** Clears an area of <aFrame> with transparent black.
*
* @param aFrameData Target Frame data
@ -182,7 +180,7 @@ private:
private: // data
//! All the frames of the image
nsTArray<RawAccessFrameRef> mFrames;
nsRefPtr<FrameSequence> mFrames;
nsIntSize mSize;
Anim* mAnim;
int32_t mLoopCount;

103
image/src/FrameSequence.cpp Normal file
View File

@ -0,0 +1,103 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FrameSequence.h"
namespace mozilla {
namespace image {
FrameSequence::~FrameSequence()
{
ClearFrames();
}
const FrameDataPair&
FrameSequence::GetFrame(uint32_t framenum) const
{
if (framenum >= mFrames.Length()) {
static FrameDataPair empty;
return empty;
}
return mFrames[framenum];
}
uint32_t
FrameSequence::GetNumFrames() const
{
return mFrames.Length();
}
void
FrameSequence::RemoveFrame(uint32_t framenum)
{
NS_ABORT_IF_FALSE(framenum < mFrames.Length(), "Deleting invalid frame!");
mFrames.RemoveElementAt(framenum);
}
void
FrameSequence::ClearFrames()
{
// Since FrameDataPair holds an nsAutoPtr to its frame, clearing the mFrames
// array also deletes all the frames.
mFrames.Clear();
}
void
FrameSequence::InsertFrame(uint32_t framenum, imgFrame* aFrame)
{
NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Inserting invalid frame!");
mFrames.InsertElementAt(framenum, aFrame);
if (GetNumFrames() > 1) {
// If we're creating our second element, we now know we're animated.
// Therefore, we need to lock the first frame too.
if (GetNumFrames() == 2) {
mFrames[0].LockAndGetData();
}
// Whenever we have more than one frame, we always lock *all* our frames
// so we have all the image data pointers.
mFrames[framenum].LockAndGetData();
}
}
already_AddRefed<imgFrame>
FrameSequence::SwapFrame(uint32_t framenum, imgFrame* aFrame)
{
NS_ABORT_IF_FALSE(framenum < mFrames.Length(), "Swapping invalid frame!");
FrameDataPair ret;
// Steal the imgFrame.
if (framenum < mFrames.Length()) {
ret = mFrames[framenum];
}
if (aFrame) {
mFrames.ReplaceElementAt(framenum, aFrame);
} else {
mFrames.RemoveElementAt(framenum);
}
return ret.GetFrame();
}
size_t
FrameSequence::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf) const
{
size_t n = 0;
for (uint32_t i = 0; i < mFrames.Length(); ++i) {
FrameDataPair fdp = mFrames.SafeElementAt(i, FrameDataPair());
NS_ABORT_IF_FALSE(fdp, "Null frame in frame array!");
n += fdp->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
}
return n;
}
} // namespace image
} // namespace mozilla

206
image/src/FrameSequence.h Normal file
View File

@ -0,0 +1,206 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_imagelib_FrameSequence_h_
#define mozilla_imagelib_FrameSequence_h_
#include "nsTArray.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Move.h"
#include "gfxTypes.h"
#include "imgFrame.h"
namespace mozilla {
namespace image {
/**
* FrameDataPair is a slightly-smart tuple of (frame, raw frame data) where the
* raw frame data is allowed to be (and is, initially) null.
*
* If you call LockAndGetData, you will be able to call GetFrameData() on that
* instance, and when the FrameDataPair is destructed, the imgFrame lock will
* be unlocked.
*/
class FrameDataPair
{
public:
explicit FrameDataPair(imgFrame* frame)
: mFrame(frame)
, mFrameData(nullptr)
{}
FrameDataPair()
: mFrameData(nullptr)
{}
FrameDataPair(const FrameDataPair& aOther)
: mFrame(aOther.mFrame)
, mFrameData(nullptr)
{}
FrameDataPair(FrameDataPair&& aOther)
: mFrame(Move(aOther.mFrame))
, mFrameData(aOther.mFrameData)
{
aOther.mFrameData = nullptr;
}
~FrameDataPair()
{
if (mFrameData) {
mFrame->UnlockImageData();
}
}
FrameDataPair& operator=(const FrameDataPair& aOther)
{
if (&aOther != this) {
mFrame = aOther.mFrame;
mFrameData = nullptr;
}
return *this;
}
FrameDataPair& operator=(FrameDataPair&& aOther)
{
MOZ_ASSERT(&aOther != this, "Moving to self");
mFrame = Move(aOther.mFrame);
mFrameData = aOther.mFrameData;
aOther.mFrameData = nullptr;
return *this;
}
// Lock the frame and store its mFrameData. The frame will be unlocked (and
// deleted) when this FrameDataPair is deleted.
void LockAndGetData()
{
if (mFrame) {
if (NS_SUCCEEDED(mFrame->LockImageData())) {
if (mFrame->GetIsPaletted()) {
mFrameData = reinterpret_cast<uint8_t*>(mFrame->GetPaletteData());
} else {
mFrameData = mFrame->GetImageData();
}
}
}
}
// Null out this FrameDataPair and return its frame. You must ensure the
// frame will be deleted separately.
already_AddRefed<imgFrame> Forget()
{
if (mFrameData) {
mFrame->UnlockImageData();
}
mFrameData = nullptr;
return mFrame.forget();
}
bool HasFrameData() const
{
if (mFrameData) {
MOZ_ASSERT(!!mFrame);
}
return !!mFrameData;
}
uint8_t* GetFrameData() const
{
return mFrameData;
}
already_AddRefed<imgFrame> GetFrame() const
{
nsRefPtr<imgFrame> frame = mFrame;
return frame.forget();
}
// Resets this FrameDataPair to work with a different frame. Takes ownership
// of the frame, deleting the old frame (if any).
void SetFrame(imgFrame* frame)
{
if (mFrameData) {
mFrame->UnlockImageData();
}
mFrame = frame;
mFrameData = nullptr;
}
imgFrame* operator->() const
{
return mFrame.get();
}
bool operator==(imgFrame* other) const
{
return mFrame == other;
}
operator bool() const
{
return mFrame != nullptr;
}
private:
nsRefPtr<imgFrame> mFrame;
uint8_t* mFrameData;
};
/**
* FrameSequence stores image frames (and their associated raw data pointers).
* It is little more than a smart array.
*/
class FrameSequence
{
~FrameSequence();
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FrameSequence)
/**
* Get the read-only (frame, data) pair at index aIndex.
*/
const FrameDataPair& GetFrame(uint32_t aIndex) const;
/**
* Insert a frame into the array. FrameSequence takes ownership of the frame.
*/
void InsertFrame(uint32_t framenum, imgFrame* aFrame);
/**
* Remove (and delete) the frame at index framenum.
*/
void RemoveFrame(uint32_t framenum);
/**
* Swap aFrame with the frame at sequence framenum, and return that frame.
* You take ownership over the frame returned.
*/
already_AddRefed<imgFrame> SwapFrame(uint32_t framenum, imgFrame* aFrame);
/**
* Remove (and delete) all frames.
*/
void ClearFrames();
/* The total number of frames in this image. */
uint32_t GetNumFrames() const;
size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf) const;
private: // data
//! All the frames of the image
nsTArray<FrameDataPair> mFrames;
};
} // namespace image
} // namespace mozilla
#endif /* mozilla_imagelib_FrameSequence_h_ */

View File

@ -361,6 +361,15 @@ RasterImage::~RasterImage()
ReentrantMonitorAutoEnter lock(mDecodingMonitor);
DecodePool::StopDecoding(this);
mDecoder = nullptr;
// Unlock the last frame (if we have any). Our invariant is that, while we
// have a decoder open, the last frame is always locked.
// This would be done in ShutdownDecoder, but since mDecoder is non-null,
// we didn't call ShutdownDecoder and we need to do it manually.
if (GetNumFrames() > 0) {
nsRefPtr<imgFrame> curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
curframe->UnlockImageData();
}
}
// Release any HQ scaled frames from the surface cache.
@ -1014,20 +1023,18 @@ RasterImage::InternalAddFrameHelper(uint32_t framenum, imgFrame *aFrame,
if (framenum > GetNumFrames())
return NS_ERROR_INVALID_ARG;
nsRefPtr<imgFrame> frame = aFrame;
RawAccessFrameRef ref = frame->RawAccessRef();
if (!ref) {
// Probably the OS discarded the frame. Exceedingly unlikely since we just
// created it, but it could happen.
return NS_ERROR_FAILURE;
}
nsRefPtr<imgFrame> frame(aFrame);
// We are in the middle of decoding. This will be unlocked when we finish
// decoding or switch to another frame.
frame->LockImageData();
if (paletteData && paletteLength)
frame->GetPaletteData(paletteData, paletteLength);
frame->GetImageData(imageData, imageLength);
mFrameBlender.InsertFrame(framenum, Move(ref));
mFrameBlender.InsertFrame(framenum, frame);
frame.forget(aRetFrame);
return NS_OK;
@ -1064,6 +1071,13 @@ RasterImage::InternalAddFrame(uint32_t framenum,
NS_WARNING("imgFrame::Init should succeed");
NS_ENSURE_SUCCESS(rv, rv);
// We know we are in a decoder. Therefore, we must unlock the previous frame
// when we move on to decoding into the next frame.
if (GetNumFrames() > 0) {
nsRefPtr<imgFrame> prevframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
prevframe->UnlockImageData();
}
if (GetNumFrames() == 0) {
return InternalAddFrameHelper(framenum, frame, imageData, imageLength,
paletteData, paletteLength, aRetFrame);
@ -1227,6 +1241,11 @@ RasterImage::EnsureFrame(uint32_t aFrameNum, int32_t aX, int32_t aY,
}
// Not reusable, so replace the frame directly.
// We know this frame is already locked, because it's the one we're currently
// writing to.
frame->UnlockImageData();
mFrameBlender.RemoveFrame(aFrameNum);
nsRefPtr<imgFrame> newFrame(new imgFrame());
nsIntRect frameRect(aX, aY, aWidth, aHeight);
@ -1310,7 +1329,8 @@ RasterImage::DecodingComplete()
// into the first frame again immediately and this produces severe tearing.
if (mMultipart) {
if (GetNumFrames() == 1) {
mMultipartDecodedFrame = mFrameBlender.GetFrame(GetCurrentFrameIndex());
mMultipartDecodedFrame = mFrameBlender.SwapFrame(GetCurrentFrameIndex(),
mMultipartDecodedFrame);
} else {
// Don't double buffer for animated multipart images. It entails more
// complexity and it's not really needed since we already are smart about
@ -1911,6 +1931,14 @@ RasterImage::InitDecoder(bool aDoSizeDecode)
NS_ABORT_IF_FALSE(0, "Shouldn't get here!");
}
// If we already have frames, we're probably in the multipart/x-mixed-replace
// case. Regardless, we need to lock the last frame. Our invariant is that,
// while we have a decoder open, the last frame is always locked.
if (GetNumFrames() > 0) {
nsRefPtr<imgFrame> curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
curframe->LockImageData();
}
// Initialize the decoder
mDecoder->SetSizeDecode(aDoSizeDecode);
mDecoder->SetDecodeFlags(mFrameDecodeFlags);
@ -1973,6 +2001,13 @@ RasterImage::ShutdownDecoder(ShutdownReason aReason)
decoder->Finish(aReason);
// Unlock the last frame (if we have any). Our invariant is that, while we
// have a decoder open, the last frame is always locked.
if (GetNumFrames() > 0) {
nsRefPtr<imgFrame> curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
curframe->UnlockImageData();
}
// Kill off our decode request, if it's pending. (If not, this call is
// harmless.)
DecodePool::StopDecoding(this);

View File

@ -579,34 +579,9 @@ imgFrame::GetStride() const
return VolatileSurfaceStride(mSize, mFormat);
}
<<<<<<< found
SurfaceFormat imgFrame::GetFormat() const
{
||||||| expected
bool imgFrame::GetNeedsBackground() const
{
// We need a background painted if we have alpha or we're incomplete.
=======
bool imgFrame::GetNeedsBackground() const
{
// We need a background painted if we're incomplete.
if (!ImageComplete()) {
return true;
}
// We need a background painted if we might not be opaque.
>>>>>>> replacement
<<<<<<< found
return mFormat;
||||||| expected
return (mFormat == SurfaceFormat::B8G8R8A8 || !ImageComplete());
=======
if (mFormat == SurfaceFormat::B8G8R8A8 && !mHasNoAlpha) {
return true;
}
return false;
>>>>>>> replacement
}
uint32_t imgFrame::GetImageBytesPerRow() const
@ -678,16 +653,6 @@ uint32_t* imgFrame::GetPaletteData() const
return data;
}
uint8_t*
imgFrame::GetRawData() const
{
MOZ_ASSERT(mLockCount, "Should be locked to call GetRawData()");
if (mPalettedImageData) {
return mPalettedImageData;
}
return GetImageData();
}
nsresult imgFrame::LockImageData()
{
MOZ_ASSERT(NS_IsMainThread());

View File

@ -96,7 +96,6 @@ public:
uint8_t* GetImageData() const;
void GetPaletteData(uint32_t **aPalette, uint32_t *length) const;
uint32_t* GetPaletteData() const;
uint8_t* GetRawData() const;
int32_t GetRawTimeout() const;
void SetRawTimeout(int32_t aTimeout);

View File

@ -22,6 +22,7 @@ UNIFIED_SOURCES += [
'DynamicImage.cpp',
'FrameAnimator.cpp',
'FrameBlender.cpp',
'FrameSequence.cpp',
'FrozenImage.cpp',
'Image.cpp',
'ImageFactory.cpp',