Bug 717872 - Store a frame's raw image data pointer beside its imgFrame pointer so we can access it without having to lock the frame. r=seth

This patch makes us store imgFrames in FrameBlender with a new sort-of-tuple,
FrameDataPair, that is smart enough to be able to lock and unlock imgFrames,
and can be transparently cast to an imgFrame, but doesn't do too much else.
The alternative, storing a separate array of uint8_t pointers, seemed too
complicated.
This commit is contained in:
Joe Drew 2013-06-28 13:52:39 -04:00
parent 97e991daf1
commit 7406a5c6fb
2 changed files with 179 additions and 51 deletions

View File

@ -7,9 +7,7 @@
#include "mozilla/MemoryReporting.h"
#include "RasterImage.h"
#include "imgFrame.h"
#define PIXMAN_DONT_DEFINE_STDINT
#include "pixman.h"
using namespace mozilla;
@ -34,11 +32,11 @@ FrameBlender::GetFrame(uint32_t framenum) const
{
if (!mAnim) {
NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
return mFrames.SafeElementAt(0, nullptr);
return mFrames.SafeElementAt(0, FrameDataPair());
}
if (mAnim->lastCompositedFrameIndex == int32_t(framenum))
return mAnim->compositingFrame;
return mFrames.SafeElementAt(framenum, nullptr);
return mFrames.SafeElementAt(framenum, FrameDataPair());
}
imgFrame*
@ -46,10 +44,10 @@ FrameBlender::RawGetFrame(uint32_t framenum) const
{
if (!mAnim) {
NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
return mFrames.SafeElementAt(0, nullptr);
return mFrames.SafeElementAt(0, FrameDataPair());
}
return mFrames.SafeElementAt(framenum, nullptr);
return mFrames.SafeElementAt(framenum, FrameDataPair());
}
uint32_t
@ -63,17 +61,14 @@ FrameBlender::RemoveFrame(uint32_t framenum)
{
NS_ABORT_IF_FALSE(framenum < mFrames.Length(), "Deleting invalid frame!");
delete mFrames[framenum];
mFrames[framenum] = nullptr;
mFrames.RemoveElementAt(framenum);
}
void
FrameBlender::ClearFrames()
{
for (uint32_t i = 0; i < mFrames.Length(); ++i) {
delete mFrames[i];
}
// Since FrameDataPair holds an nsAutoPtr to its frame, clearing the mFrames
// array also deletes all the frames.
mFrames.Clear();
}
@ -84,6 +79,10 @@ FrameBlender::InsertFrame(uint32_t framenum, imgFrame* aFrame)
mFrames.InsertElementAt(framenum, aFrame);
if (GetNumFrames() > 1) {
EnsureAnimExists();
// Whenever we have more than one frame, we always lock *all* our frames
// so we have all the image data pointers.
mFrames[framenum].LockAndGetData();
}
}
@ -91,13 +90,40 @@ imgFrame*
FrameBlender::SwapFrame(uint32_t framenum, imgFrame* aFrame)
{
NS_ABORT_IF_FALSE(framenum < mFrames.Length(), "Swapping invalid frame!");
imgFrame* ret = mFrames.SafeElementAt(framenum, nullptr);
mFrames.RemoveElementAt(framenum);
if (aFrame) {
mFrames.InsertElementAt(framenum, aFrame);
FrameDataPair ret;
// Steal the imgFrame from wherever it's currently stored
if (mAnim && mAnim->lastCompositedFrameIndex == int32_t(framenum)) {
ret = mAnim->compositingFrame;
mAnim->lastCompositedFrameIndex = -1;
} else if (framenum < mFrames.Length()) {
ret = mFrames[framenum];
}
return ret;
mFrames.RemoveElementAt(framenum);
if (aFrame) {
InsertFrame(framenum, aFrame);
}
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(mFrames.Length() == 2);
// Whenever we have more than one frame, we always lock *all* our frames
// so we have all the image data pointers.
mFrames[0].LockAndGetData();
}
}
//******************************************************************************
@ -112,9 +138,9 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
return false;
}
imgFrame* prevFrame = mFrames[aPrevFrameIndex];
imgFrame* nextFrame = mFrames[aNextFrameIndex];
if (!prevFrame || !nextFrame) {
FrameDataPair& prevFrame = mFrames[aPrevFrameIndex];
FrameDataPair& nextFrame = mFrames[aNextFrameIndex];
if (!prevFrame.HasFrameData() || !nextFrame.HasFrameData()) {
return false;
}
@ -201,13 +227,14 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
// Create the Compositing Frame
if (!mAnim->compositingFrame) {
mAnim->compositingFrame = new imgFrame();
mAnim->compositingFrame.SetFrame(new imgFrame());
nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
gfxASurface::ImageFormatARGB32);
if (NS_FAILED(rv)) {
mAnim->compositingFrame = nullptr;
mAnim->compositingFrame.SetFrame(nullptr);
return false;
}
mAnim->compositingFrame.LockAndGetData();
needToBlankComposite = true;
} else if (int32_t(aNextFrameIndex) != mAnim->lastCompositedFrameIndex+1) {
@ -269,7 +296,7 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
// destroy only if we don't need it for this frame's disposal
if (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)
mAnim->compositingPrevFrame = nullptr;
mAnim->compositingPrevFrame.SetFrame(nullptr);
} else {
ClearFrame(mAnim->compositingFrame);
}
@ -312,13 +339,15 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
// It would be better if we just stored the area that nextFrame is going to
// overwrite.
if (!mAnim->compositingPrevFrame) {
mAnim->compositingPrevFrame = new imgFrame();
mAnim->compositingPrevFrame.SetFrame(new imgFrame());
nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
gfxASurface::ImageFormatARGB32);
if (NS_FAILED(rv)) {
mAnim->compositingPrevFrame = nullptr;
mAnim->compositingPrevFrame.SetFrame(nullptr);
return false;
}
mAnim->compositingPrevFrame.LockAndGetData();
}
CopyFrameImage(mAnim->compositingFrame, mAnim->compositingPrevFrame);
@ -352,7 +381,7 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
// frame next time around
if (CopyFrameImage(mAnim->compositingFrame, nextFrame)) {
prevFrame->SetFrameDisposalMethod(FrameBlender::kDisposeClearAll);
mAnim->compositingFrame = nullptr;
mAnim->compositingFrame.SetFrame(nullptr);
mAnim->lastCompositedFrameIndex = -1;
return true;
}
@ -567,9 +596,7 @@ FrameBlender::Discard()
NS_ABORT_IF_FALSE(!mAnim, "Asked to discard for animated image!");
// Delete all the decoded frames, then clear the array.
for (uint32_t i = 0; i < mFrames.Length(); ++i)
delete mFrames[i];
mFrames.Clear();
ClearFrames();
}
size_t
@ -578,7 +605,7 @@ FrameBlender::SizeOfDecodedWithComputedFallbackIfHeap(gfxASurface::MemoryLocatio
{
size_t n = 0;
for (uint32_t i = 0; i < mFrames.Length(); ++i) {
imgFrame* frame = mFrames.SafeElementAt(i, nullptr);
imgFrame* frame = mFrames.SafeElementAt(i, FrameDataPair());
NS_ABORT_IF_FALSE(frame, "Null frame in frame array!");
n += frame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
}

View File

@ -11,19 +11,134 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/TimeStamp.h"
#include "gfxASurface.h"
class imgFrame;
#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()
: mFrame(nullptr)
, mFrameData(nullptr)
{}
FrameDataPair(FrameDataPair& other)
{
mFrame = other.mFrame;
mFrameData = other.mFrameData;
// since mFrame is an nsAutoPtr, the assignment operator above actually
// nulls out other.mFrame. In order to fully assume ownership over the
// frame, we also null out the other's mFrameData.
other.mFrameData = nullptr;
}
~FrameDataPair()
{
if (mFrameData) {
mFrame->UnlockImageData();
}
}
// 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.
imgFrame* Forget()
{
if (mFrameData) {
mFrame->UnlockImageData();
}
imgFrame* frame = mFrame.forget();
mFrameData = nullptr;
return frame;
}
bool HasFrameData() const
{
if (mFrameData) {
MOZ_ASSERT(!!mFrame);
}
return !!mFrameData;
}
uint8_t* GetFrameData() const
{
return mFrameData;
}
imgFrame* GetFrame() const
{
return mFrame;
}
// 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;
}
operator imgFrame*() const
{
return GetFrame();
}
imgFrame* operator->() const
{
return GetFrame();
}
bool operator==(imgFrame* other) const
{
return mFrame == other;
}
private:
nsAutoPtr<imgFrame> mFrame;
uint8_t* mFrameData;
};
/**
* FrameBlender stores and gives access to imgFrames. It also knows how to
* blend frames from previous to next, looping if necessary.
*
* All logic about when and whether to blend are external to FrameBlender.
*/
class FrameBlender
{
public:
@ -95,7 +210,7 @@ private:
struct Anim
{
//! Track the last composited frame for Optimizations (See DoComposite code)
int32_t lastCompositedFrameIndex;
int32_t lastCompositedFrameIndex;
/** For managing blending of frames
*
@ -105,7 +220,7 @@ private:
* lastCompositedFrameIndex to -1. Code assume that if
* lastCompositedFrameIndex >= 0 then compositingFrame exists.
*/
nsAutoPtr<imgFrame> compositingFrame;
FrameDataPair compositingFrame;
/** the previous composited frame, for DISPOSE_RESTORE_PREVIOUS
*
@ -113,20 +228,14 @@ private:
* stored in cases where the image specifies it wants the last frame back
* when it's done with the current frame.
*/
nsAutoPtr<imgFrame> compositingPrevFrame;
FrameDataPair compositingPrevFrame;
Anim() :
lastCompositedFrameIndex(-1)
{}
};
inline void EnsureAnimExists()
{
if (!mAnim) {
// Create the animation context
mAnim = new Anim();
}
}
void EnsureAnimExists();
/** Clears an area of <aFrame> with transparent black.
*
@ -170,16 +279,8 @@ private:
private: // data
//! All the frames of the image
// IMPORTANT: if you use mFrames in a method, call EnsureImageIsDecoded() first
// to ensure that the frames actually exist (they may have been discarded to save
// memory, or we may be decoding on draw).
nsTArray<imgFrame*> mFrames;
nsTArray<FrameDataPair> mFrames;
nsIntSize mSize;
// IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure
// that the frames actually exist (they may have been discarded to save memory, or
// we maybe decoding on draw).
Anim* mAnim;
};