Back out bug 1057904, bug 1060869, bug 923302 for bustage

--HG--
extra : rebase_source : fe24318671f3fd9cccb822c65a9581f09612d8c6
This commit is contained in:
Seth Fowler 2014-10-01 17:33:49 -07:00
parent 8cf690729c
commit 66dd048dda
18 changed files with 971 additions and 932 deletions

View File

@ -9,7 +9,6 @@
#include <stdlib.h>
#include "mozilla/Endian.h"
#include "mozilla/Move.h"
#include "nsICODecoder.h"
#include "RasterImage.h"
@ -337,7 +336,7 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy
mContainedDecoder->SetSizeDecode(IsSizeDecode());
mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength,
mColormap, mColormapSize,
Move(mRefForContainedDecoder));
mCurrentFrame);
if (!WriteToContainedDecoder(mSignature, PNGSIGNATURESIZE, aStrategy)) {
return;
}
@ -414,7 +413,7 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy
mContainedDecoder->SetSizeDecode(IsSizeDecode());
mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength,
mColormap, mColormapSize,
Move(mRefForContainedDecoder));
mCurrentFrame);
// The ICO format when containing a BMP does not include the 14 byte
// bitmap file header. To use the code of the BMP decoder we need to
@ -614,19 +613,13 @@ nsICODecoder::NeedsNewFrame() const
nsresult
nsICODecoder::AllocateFrame()
{
nsresult rv;
if (mContainedDecoder) {
rv = mContainedDecoder->AllocateFrame();
mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
nsresult rv = mContainedDecoder->AllocateFrame();
mCurrentFrame = mContainedDecoder->GetCurrentFrame();
return rv;
}
// Grab a strong ref that we'll later hand over to the contained decoder. This
// lets us avoid creating a RawAccessFrameRef off-main-thread.
rv = Decoder::AllocateFrame();
mRefForContainedDecoder = GetCurrentFrameRef();
return rv;
return Decoder::AllocateFrame();
}
} // namespace image

View File

@ -9,7 +9,6 @@
#include "nsAutoPtr.h"
#include "Decoder.h"
#include "imgFrame.h"
#include "nsBMPDecoder.h"
#include "nsPNGDecoder.h"
#include "ICOFileHeaders.h"
@ -81,7 +80,6 @@ private:
uint32_t mRowBytes; // How many bytes of the row were already received
int32_t mOldLine; // Previous index of the line
nsRefPtr<Decoder> mContainedDecoder; // Contains either a BMP or PNG resource
RawAccessFrameRef mRefForContainedDecoder; // Avoid locking off-main-thread
char mDirEntryArray[ICODIRENTRYSIZE]; // Holds the current dir entry buffer
IconDirEntry mDirEntry; // Holds a decoded dir entry

View File

@ -16,6 +16,7 @@ namespace image {
Decoder::Decoder(RasterImage &aImage)
: mImage(aImage)
, mCurrentFrame(nullptr)
, mImageData(nullptr)
, mColormap(nullptr)
, mDecodeFlags(0)
@ -60,20 +61,19 @@ Decoder::Init()
// Initializes a decoder whose image and observer is already being used by a
// parent decoder
void
Decoder::InitSharedDecoder(uint8_t* aImageData, uint32_t aImageDataLength,
uint32_t* aColormap, uint32_t aColormapSize,
RawAccessFrameRef&& aFrameRef)
Decoder::InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength,
uint32_t* colormap, uint32_t colormapSize,
imgFrame* currentFrame)
{
// No re-initializing
NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!");
NS_ABORT_IF_FALSE(mObserver, "Need an observer!");
mImageData = aImageData;
mImageDataLength = aImageDataLength;
mColormap = aColormap;
mColormapSize = aColormapSize;
mCurrentFrame = Move(aFrameRef);
mImageData = imageData;
mImageDataLength = imageDataLength;
mColormap = colormap;
mColormapSize = colormapSize;
mCurrentFrame = currentFrame;
// We have all the frame data, so we've started the frame.
if (!IsSizeDecode()) {
PostFrameStart();
@ -179,13 +179,11 @@ Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent)
}
}
// Set image metadata before calling DecodingComplete, because
// DecodingComplete calls Optimize().
// Set image metadata before calling DecodingComplete, because DecodingComplete calls Optimize().
mImageMetadata.SetOnImage(&mImage);
if (mDecodeDone) {
MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
mImage.DecodingComplete(mCurrentFrame.get());
mImage.DecodingComplete();
}
}
@ -205,22 +203,34 @@ Decoder::AllocateFrame()
MOZ_ASSERT(mNeedsNewFrame);
MOZ_ASSERT(NS_IsMainThread());
mCurrentFrame = mImage.EnsureFrame(mNewFrameData.mFrameNum,
mNewFrameData.mFrameRect,
mDecodeFlags,
mNewFrameData.mFormat,
mNewFrameData.mPaletteDepth,
mCurrentFrame.get());
if (mCurrentFrame) {
// Gather the raw pointers the decoders will use.
mCurrentFrame->GetImageData(&mImageData, &mImageDataLength);
mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize);
if (mNewFrameData.mFrameNum == mFrameCount) {
PostFrameStart();
}
nsresult rv;
nsRefPtr<imgFrame> frame;
if (mNewFrameData.mPaletteDepth) {
rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
mNewFrameData.mOffsetY, mNewFrameData.mWidth,
mNewFrameData.mHeight, mNewFrameData.mFormat,
mNewFrameData.mPaletteDepth,
&mImageData, &mImageDataLength,
&mColormap, &mColormapSize,
getter_AddRefs(frame));
} else {
rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
mNewFrameData.mOffsetY, mNewFrameData.mWidth,
mNewFrameData.mHeight, mNewFrameData.mFormat,
&mImageData, &mImageDataLength,
getter_AddRefs(frame));
}
if (NS_SUCCEEDED(rv)) {
mCurrentFrame = frame;
} else {
mCurrentFrame = nullptr;
}
// Notify if appropriate
if (NS_SUCCEEDED(rv) && mNewFrameData.mFrameNum == mFrameCount) {
PostFrameStart();
} else if (NS_FAILED(rv)) {
PostDataError();
}
@ -228,7 +238,7 @@ Decoder::AllocateFrame()
// so they can tell us if they need yet another.
mNeedsNewFrame = false;
return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
return rv;
}
void
@ -318,8 +328,8 @@ Decoder::PostFrameStart()
// Decoder implementations should only call this method if they successfully
// appended the frame to the image. So mFrameCount should always match that
// reported by the Image.
MOZ_ASSERT(mFrameCount == mImage.GetNumFrames(),
"Decoder frame count doesn't match image's!");
NS_ABORT_IF_FALSE(mFrameCount == mImage.GetNumFrames(),
"Decoder frame count doesn't match image's!");
// Fire notifications
if (mObserver) {
@ -383,6 +393,7 @@ Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
mDecodeDone = true;
mImageMetadata.SetLoopCount(aLoopCount);
mImageMetadata.SetIsNonPremultiplied(GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA);
if (mObserver) {
mObserver->OnStopDecode(NS_OK);
@ -419,9 +430,7 @@ Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset,
// We don't want images going back in time or skipping frames.
MOZ_ASSERT(framenum == mFrameCount || framenum == (mFrameCount - 1));
mNewFrameData = NewFrameData(framenum,
nsIntRect(x_offset, y_offset, width, height),
format, palette_depth);
mNewFrameData = NewFrameData(framenum, x_offset, y_offset, width, height, format, palette_depth);
mNeedsNewFrame = true;
}

View File

@ -37,9 +37,9 @@ public:
*
* Notifications Sent: TODO
*/
void InitSharedDecoder(uint8_t* aImageData, uint32_t aImageDataLength,
uint32_t* aColormap, uint32_t aColormapSize,
RawAccessFrameRef&& aFrameRef);
void InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength,
uint32_t* colormap, uint32_t colormapSize,
imgFrame* currentFrame);
/**
* Writes data to the decoder.
@ -160,17 +160,12 @@ public:
// status code from that attempt. Clears mNewFrameData.
virtual nsresult AllocateFrame();
already_AddRefed<imgFrame> GetCurrentFrame()
already_AddRefed<imgFrame> GetCurrentFrame() const
{
nsRefPtr<imgFrame> frame = mCurrentFrame.get();
nsRefPtr<imgFrame> frame = mCurrentFrame;
return frame.forget();
}
RawAccessFrameRef GetCurrentFrameRef()
{
return mCurrentFrame->RawAccessRef();
}
protected:
virtual ~Decoder();
@ -229,7 +224,7 @@ protected:
*
*/
RasterImage &mImage;
RawAccessFrameRef mCurrentFrame;
nsRefPtr<imgFrame> mCurrentFrame;
RefPtr<imgDecoderObserver> mObserver;
ImageMetadata mImageMetadata;
@ -251,22 +246,28 @@ private:
struct NewFrameData
{
NewFrameData() { }
NewFrameData(uint32_t aFrameNum, const nsIntRect& aFrameRect,
gfx::SurfaceFormat aFormat, uint8_t aPaletteDepth)
: mFrameNum(aFrameNum)
, mFrameRect(aFrameRect)
, mFormat(aFormat)
, mPaletteDepth(aPaletteDepth)
{ }
NewFrameData()
{}
NewFrameData(uint32_t num, uint32_t offsetx, uint32_t offsety,
uint32_t width, uint32_t height,
gfx::SurfaceFormat format, uint8_t paletteDepth)
: mFrameNum(num)
, mOffsetX(offsetx)
, mOffsetY(offsety)
, mWidth(width)
, mHeight(height)
, mFormat(format)
, mPaletteDepth(paletteDepth)
{}
uint32_t mFrameNum;
nsIntRect mFrameRect;
uint32_t mOffsetX;
uint32_t mOffsetY;
uint32_t mWidth;
uint32_t mHeight;
gfx::SurfaceFormat mFormat;
uint8_t mPaletteDepth;
};
NewFrameData mNewFrameData;
bool mNeedsNewFrame;
bool mInitialized;

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,43 +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();
}
already_AddRefed<imgFrame>
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->GetFrame(0).GetFrame();
}
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.
@ -78,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;
}
@ -98,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);
}
}
//******************************************************************************
@ -135,10 +167,15 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
uint32_t aPrevFrameIndex,
uint32_t aNextFrameIndex)
{
nsRefPtr<imgFrame> prevFrame = GetFrame(aPrevFrameIndex);
nsRefPtr<imgFrame> nextFrame = GetFrame(aNextFrameIndex);
if (!aDirtyRect) {
return false;
}
MOZ_ASSERT(prevFrame && nextFrame, "Should have frames here");
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 &&
@ -223,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) {
@ -272,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;
@ -291,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;
@ -315,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()));
}
@ -339,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());
}
@ -352,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()));
@ -550,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.
*/
explicit 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.
@ -120,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
*
@ -128,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
@ -173,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

@ -27,6 +27,10 @@ ImageMetadata::SetOnImage(RasterImage* image)
}
image->SetLoopCount(mLoopCount);
for (uint32_t i = 0; i < image->GetNumFrames(); i++) {
image->SetFrameAsNonPremult(i, mIsNonPremultiplied);
}
}
} // namespace image

View File

@ -25,6 +25,7 @@ public:
: mHotspotX(-1)
, mHotspotY(-1)
, mLoopCount(-1)
, mIsNonPremultiplied(false)
{}
// Set the metadata this object represents on an image.
@ -40,6 +41,11 @@ public:
mLoopCount = loopcount;
}
void SetIsNonPremultiplied(bool nonPremult)
{
mIsNonPremultiplied = nonPremult;
}
void SetSize(int32_t width, int32_t height, Orientation orientation)
{
mSize.emplace(nsIntSize(width, height));
@ -62,7 +68,9 @@ private:
int32_t mLoopCount;
Maybe<nsIntSize> mSize;
Maybe<Orientation> mOrientation;
Maybe<Orientation> mOrientation;
bool mIsNonPremultiplied;
};
} // namespace image

View File

@ -211,8 +211,7 @@ public:
// Insert the new surface into the cache immediately. We need to do this so
// that we won't start multiple scaling jobs for the same size.
SurfaceCache::Insert(mDstRef.get(), ImageKey(mImage.get()),
RasterSurfaceKey(mDstSize.ToIntSize(), mImageFlags),
Lifetime::Transient);
RasterSurfaceKey(mDstSize.ToIntSize(), mImageFlags));
return true;
}
@ -239,6 +238,9 @@ public:
mDstRef->ImageUpdated(mDstRef->GetRect());
MOZ_ASSERT(mDstRef->ImageComplete(),
"Incomplete, but just updated the entire frame");
if (DiscardingEnabled()) {
mDstRef->SetDiscardable();
}
}
// We need to send notifications and release our references on the main
@ -263,9 +265,9 @@ public:
NS_WARNING("HQ scaling failed");
// Remove the frame from the cache since we know we don't need it.
SurfaceCache::RemoveSurface(ImageKey(mImage.get()),
RasterSurfaceKey(mDstSize.ToIntSize(),
mImageFlags));
SurfaceCache::RemoveIfPresent(ImageKey(mImage.get()),
RasterSurfaceKey(mDstSize.ToIntSize(),
mImageFlags));
// Release everything we're holding, too.
mSrcRef.reset();
@ -328,8 +330,6 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
mHasSourceData(false),
mDecoded(false),
mHasBeenDecoded(false),
mHasFirstFrame(false),
mFirstFrameIsOpaque(false),
mAnimationFinished(false),
mFinishing(false),
mInUpdateImageContainer(false),
@ -371,10 +371,19 @@ 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 all frames from the surface cache.
SurfaceCache::RemoveImage(ImageKey(this));
// Release any HQ scaled frames from the surface cache.
SurfaceCache::Discard(this);
mAnim = nullptr;
@ -428,11 +437,6 @@ RasterImage::Init(const char* aMimeType,
discardable_source_bytes += mSourceData.Length();
}
// Lock this image's surfaces in the SurfaceCache if we're not discardable.
if (!mDiscardable) {
SurfaceCache::LockImage(ImageKey(this));
}
// Instantiate the decoder
nsresult rv = InitDecoder(/* aDoSizeDecode = */ true);
CONTAINER_ENSURE_SUCCESS(rv);
@ -573,14 +577,21 @@ RasterImage::GetType()
return imgIContainer::TYPE_RASTER;
}
already_AddRefed<imgFrame>
RasterImage::LookupFrameNoDecode(uint32_t aFrameNum)
{
if (!mAnim) {
NS_ASSERTION(aFrameNum == 0, "Don't ask for a frame > 0 if we're not animated!");
return mFrameBlender.GetFrame(0);
}
return mFrameBlender.GetFrame(aFrameNum);
}
DrawableFrameRef
RasterImage::LookupFrame(uint32_t aFrameNum,
const nsIntSize& aSize,
uint32_t aFlags,
bool aShouldSyncNotify /* = true */)
{
MOZ_ASSERT(NS_IsMainThread());
if (mMultipart &&
aFrameNum == GetCurrentFrameIndex() &&
mMultipartDecodedFrame) {
@ -594,19 +605,12 @@ RasterImage::LookupFrame(uint32_t aFrameNum,
nsresult rv = WantDecodedFrames(aFlags, aShouldSyncNotify);
CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), DrawableFrameRef());
DrawableFrameRef ref;
if (mAnim) {
MOZ_ASSERT(mFrameBlender, "mAnim but no mFrameBlender?");
nsRefPtr<imgFrame> frame = mFrameBlender->GetFrame(aFrameNum);
ref = frame->DrawableRef();
} else {
NS_ASSERTION(aFrameNum == 0,
"Don't ask for a frame > 0 if we're not animated!");
ref = SurfaceCache::Lookup(ImageKey(this),
RasterSurfaceKey(aSize.ToIntSize(),
DecodeFlags(aFlags)));
nsRefPtr<imgFrame> frame = LookupFrameNoDecode(aFrameNum);
if (!frame) {
return DrawableFrameRef();
}
DrawableFrameRef ref = frame->DrawableRef();
if (!ref) {
// The OS threw this frame away. We need to discard and redecode.
MOZ_ASSERT(!mAnim, "Animated frames should be locked");
@ -615,9 +619,8 @@ RasterImage::LookupFrame(uint32_t aFrameNum,
WantDecodedFrames(aFlags, aShouldSyncNotify);
// See if we managed to entirely redecode the frame.
ref = SurfaceCache::Lookup(ImageKey(this),
RasterSurfaceKey(aSize.ToIntSize(),
DecodeFlags(aFlags)));
frame = LookupFrameNoDecode(aFrameNum);
ref = frame->DrawableRef();
}
if (!ref) {
@ -661,23 +664,12 @@ RasterImage::FrameIsOpaque(uint32_t aWhichFrame)
return false;
}
if (mError) {
if (mError)
return false;
}
// False is always correct if we are still decoding the first frame.
if (!mHasFirstFrame) {
return false;
}
if (GetNumFrames() == 1) {
return mFirstFrameIsOpaque;
}
// See if we can get an image frame.
MOZ_ASSERT(mFrameBlender, "We should be animated here");
nsRefPtr<imgFrame> frame =
mFrameBlender->RawGetFrame(GetRequestedFrameIndex(aWhichFrame));
LookupFrameNoDecode(GetRequestedFrameIndex(aWhichFrame));
// If we don't get a frame, the safe answer is "not opaque".
if (!frame)
@ -699,18 +691,9 @@ RasterImage::FrameRect(uint32_t aWhichFrame)
return nsIntRect();
}
if (!mHasFirstFrame) {
return nsIntRect();
}
if (GetNumFrames() == 1) {
return nsIntRect(0, 0, mSize.width, mSize.height);
}
// We must be animated, so get the requested frame from our FrameBlender.
MOZ_ASSERT(mFrameBlender, "We should be animated here");
// Get the requested frame.
nsRefPtr<imgFrame> frame =
mFrameBlender->RawGetFrame(GetRequestedFrameIndex(aWhichFrame));
LookupFrameNoDecode(GetRequestedFrameIndex(aWhichFrame));
// If we have the frame, use that rectangle.
if (frame) {
@ -728,10 +711,7 @@ RasterImage::FrameRect(uint32_t aWhichFrame)
uint32_t
RasterImage::GetNumFrames() const
{
if (mFrameBlender) {
return mFrameBlender->GetNumFrames();
}
return mHasFirstFrame ? 1 : 0;
return mFrameBlender.GetNumFrames();
}
//******************************************************************************
@ -773,8 +753,7 @@ RasterImage::GetFirstFrameDelay()
if (NS_FAILED(GetAnimated(&animated)) || !animated)
return -1;
MOZ_ASSERT(mFrameBlender, "Animated images should have a FrameBlender");
return mFrameBlender->GetTimeoutForFrame(0);
return mFrameBlender.GetTimeoutForFrame(0);
}
TemporaryRef<SourceSurface>
@ -799,7 +778,7 @@ RasterImage::CopyFrame(uint32_t aWhichFrame,
// not waiting for the data to be loaded from the network or not passing
// FLAG_SYNC_DECODE
DrawableFrameRef frameRef = LookupFrame(GetRequestedFrameIndex(aWhichFrame),
mSize, aFlags, aShouldSyncNotify);
aFlags, aShouldSyncNotify);
if (!frameRef) {
// The OS threw this frame away and we couldn't redecode it right now.
return nullptr;
@ -880,7 +859,7 @@ RasterImage::GetFrameInternal(uint32_t aWhichFrame,
// not waiting for the data to be loaded from the network or not passing
// FLAG_SYNC_DECODE
DrawableFrameRef frameRef = LookupFrame(GetRequestedFrameIndex(aWhichFrame),
mSize, aFlags, aShouldSyncNotify);
aFlags, aShouldSyncNotify);
if (!frameRef) {
// The OS threw this frame away and we couldn't redecode it.
return nullptr;
@ -1010,13 +989,7 @@ size_t
RasterImage::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf) const
{
size_t n = 0;
n += SurfaceCache::SizeOfSurfaces(ImageKey(this), aLocation, aMallocSizeOf);
if (mFrameBlender) {
n += mFrameBlender->SizeOfDecodedWithComputedFallbackIfHeap(aLocation,
aMallocSizeOf);
}
return n;
return mFrameBlender.SizeOfDecodedWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
}
size_t
@ -1040,66 +1013,13 @@ RasterImage::OutOfProcessSizeOfDecoded() const
nullptr);
}
RawAccessFrameRef
RasterImage::InternalAddFrame(uint32_t aFrameNum,
const nsIntRect& aFrameRect,
uint32_t aDecodeFlags,
SurfaceFormat aFormat,
uint8_t aPaletteDepth,
imgFrame* aPreviousFrame)
void
RasterImage::EnsureAnimExists()
{
// We assume that we're in the middle of decoding because we unlock the
// previous frame when we create a new frame, and only when decoding do we
// lock frames.
MOZ_ASSERT(mDecoder, "Only decoders may add frames!");
if (!mAnim) {
MOZ_ASSERT(aFrameNum <= GetNumFrames(), "Invalid frame index!");
if (aFrameNum > GetNumFrames()) {
return RawAccessFrameRef();
}
if (mSize.width <= 0 || mSize.height <= 0) {
NS_WARNING("Trying to add frame with zero or negative size");
return RawAccessFrameRef();
}
IntSize frameSize = aFrameRect.Size().ToIntSize();
if (!SurfaceCache::CanHold(frameSize)) {
NS_WARNING("Trying to add frame that's too large for the SurfaceCache");
return RawAccessFrameRef();
}
nsRefPtr<imgFrame> frame = new imgFrame();
if (NS_FAILED(frame->InitForDecoder(aFrameRect, aFormat, aPaletteDepth))) {
NS_WARNING("imgFrame::Init should succeed");
return RawAccessFrameRef();
}
frame->SetAsNonPremult(aDecodeFlags & FLAG_DECODE_NO_PREMULTIPLY_ALPHA);
RawAccessFrameRef ref = frame->RawAccessRef();
if (!ref) {
return RawAccessFrameRef();
}
if (GetNumFrames() == 0) {
bool succeeded =
SurfaceCache::Insert(frame, ImageKey(this),
RasterSurfaceKey(frameSize, aDecodeFlags),
Lifetime::Persistent);
if (!succeeded) {
return RawAccessFrameRef();
}
mHasFirstFrame = true;
return Move(ref);
}
if (GetNumFrames() == 1) {
// We're becoming animated, so initialize animation stuff.
MOZ_ASSERT(!mFrameBlender, "Already have a FrameBlender?");
MOZ_ASSERT(!mAnim, "Already have animation state?");
mFrameBlender.emplace();
mFrameBlender->SetSize(mSize);
mAnim = MakeUnique<FrameAnimator>(*mFrameBlender, mAnimationMode);
// Create the animation context
mAnim = MakeUnique<FrameAnimator>(mFrameBlender, mAnimationMode);
// We don't support discarding animated images (See bug 414259).
// Lock the image and throw away the key.
@ -1112,49 +1032,105 @@ RasterImage::InternalAddFrame(uint32_t aFrameNum,
// is acceptable for the moment.
LockImage();
// Insert the first frame into the FrameBlender.
MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
RawAccessFrameRef ref = aPreviousFrame->RawAccessRef();
if (!ref) {
return RawAccessFrameRef(); // Let's keep the FrameBlender consistent...
}
mFrameBlender->InsertFrame(0, Move(ref));
// Record whether the first frame is opaque.
mFirstFrameIsOpaque = !aPreviousFrame->GetNeedsBackground();
// Remove it from the SurfaceCache. (It's not really doing any harm there,
// but keeping it there could cause it to be counted twice in our memory
// statistics.)
SurfaceCache::RemoveSurface(ImageKey(this),
RasterSurfaceKey(frameSize, aDecodeFlags));
// Notify our observers that we are starting animation.
nsRefPtr<imgStatusTracker> statusTracker = CurrentStatusTracker();
statusTracker->RecordImageIsAnimated();
}
}
// If we dispose of the first frame by clearing it, then the first frame's
// refresh area is all of itself.
// RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR).
int32_t frameDisposalMethod = aPreviousFrame->GetFrameDisposalMethod();
if (frameDisposalMethod == FrameBlender::kDisposeClear ||
frameDisposalMethod == FrameBlender::kDisposeRestorePrevious) {
mAnim->SetFirstFrameRefreshArea(aPreviousFrame->GetRect());
}
nsresult
RasterImage::InternalAddFrameHelper(uint32_t framenum, imgFrame *aFrame,
uint8_t **imageData, uint32_t *imageLength,
uint32_t **paletteData, uint32_t *paletteLength,
imgFrame** aRetFrame)
{
NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Invalid frame index!");
if (framenum > GetNumFrames())
return NS_ERROR_INVALID_ARG;
if (mPendingAnimation && ShouldAnimate()) {
StartAnimation();
}
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, frame);
frame.forget(aRetFrame);
return NS_OK;
}
nsresult
RasterImage::InternalAddFrame(uint32_t framenum,
int32_t aX, int32_t aY,
int32_t aWidth, int32_t aHeight,
SurfaceFormat aFormat,
uint8_t aPaletteDepth,
uint8_t **imageData,
uint32_t *imageLength,
uint32_t **paletteData,
uint32_t *paletteLength,
imgFrame** aRetFrame)
{
// We assume that we're in the middle of decoding because we unlock the
// previous frame when we create a new frame, and only when decoding do we
// lock frames.
NS_ABORT_IF_FALSE(mDecoder, "Only decoders may add frames!");
NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Invalid frame index!");
if (framenum > GetNumFrames())
return NS_ERROR_INVALID_ARG;
nsRefPtr<imgFrame> frame(new imgFrame());
nsIntRect frameRect(aX, aY, aWidth, aHeight);
nsresult rv = frame->InitForDecoder(frameRect, aFormat, aPaletteDepth);
if (!(mSize.width > 0 && mSize.height > 0))
NS_WARNING("Shouldn't call InternalAddFrame with zero size");
if (!NS_SUCCEEDED(rv))
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();
}
// Some GIFs are huge but only have a small area that they animate. We only
// need to refresh that small area when frame 0 comes around again.
if (GetNumFrames() == 0) {
return InternalAddFrameHelper(framenum, frame, imageData, imageLength,
paletteData, paletteLength, aRetFrame);
}
if (GetNumFrames() == 1) {
// Since we're about to add our second frame, initialize animation stuff
EnsureAnimExists();
// If we dispose of the first frame by clearing it, then the
// First Frame's refresh area is all of itself.
// RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR)
nsRefPtr<imgFrame> firstFrame = mFrameBlender.RawGetFrame(0);
int32_t frameDisposalMethod = firstFrame->GetFrameDisposalMethod();
if (frameDisposalMethod == FrameBlender::kDisposeClear ||
frameDisposalMethod == FrameBlender::kDisposeRestorePrevious)
mAnim->SetFirstFrameRefreshArea(firstFrame->GetRect());
}
// Calculate firstFrameRefreshArea
// Some gifs are huge but only have a small area that they animate
// We only need to refresh that small area when Frame 0 comes around again
mAnim->UnionFirstFrameRefreshArea(frame->GetRect());
MOZ_ASSERT(mFrameBlender, "Should have a FrameBlender by now");
mFrameBlender->InsertFrame(aFrameNum, Move(ref));
rv = InternalAddFrameHelper(framenum, frame, imageData, imageLength,
paletteData, paletteLength, aRetFrame);
return ref;
return rv;
}
bool
@ -1224,70 +1200,126 @@ RasterImage::SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation)
mOrientation = aOrientation;
mHasSize = true;
mFrameBlender.SetSize(mSize);
return NS_OK;
}
RawAccessFrameRef
RasterImage::EnsureFrame(uint32_t aFrameNum,
const nsIntRect& aFrameRect,
uint32_t aDecodeFlags,
nsresult
RasterImage::EnsureFrame(uint32_t aFrameNum, int32_t aX, int32_t aY,
int32_t aWidth, int32_t aHeight,
SurfaceFormat aFormat,
uint8_t aPaletteDepth,
imgFrame* aPreviousFrame)
uint8_t **imageData, uint32_t *imageLength,
uint32_t **paletteData, uint32_t *paletteLength,
imgFrame** aRetFrame)
{
if (mError) {
return RawAccessFrameRef();
if (mError)
return NS_ERROR_FAILURE;
NS_ENSURE_ARG_POINTER(imageData);
NS_ENSURE_ARG_POINTER(imageLength);
NS_ENSURE_ARG_POINTER(aRetFrame);
NS_ABORT_IF_FALSE(aFrameNum <= GetNumFrames(), "Invalid frame index!");
if (aPaletteDepth > 0) {
NS_ENSURE_ARG_POINTER(paletteData);
NS_ENSURE_ARG_POINTER(paletteLength);
}
MOZ_ASSERT(aFrameNum <= GetNumFrames(), "Invalid frame index!");
if (aFrameNum > GetNumFrames()) {
return RawAccessFrameRef();
}
if (aFrameNum > GetNumFrames())
return NS_ERROR_INVALID_ARG;
// Adding a frame that doesn't already exist. This is the normal case.
// Adding a frame that doesn't already exist.
if (aFrameNum == GetNumFrames()) {
return InternalAddFrame(aFrameNum, aFrameRect, aDecodeFlags, aFormat,
aPaletteDepth, aPreviousFrame);
return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
aPaletteDepth, imageData, imageLength,
paletteData, paletteLength, aRetFrame);
}
// We're replacing a frame. It must be the first frame; there's no reason to
// ever replace any other frame, since the first frame is the only one we
// speculatively allocate without knowing what the decoder really needs.
// XXX(seth): I'm not convinced there's any reason to support this at all. We
// should figure out how to avoid triggering this and rip it out.
MOZ_ASSERT(mHasFirstFrame, "Should have the first frame");
MOZ_ASSERT(aFrameNum == 0, "Replacing a frame other than the first?");
MOZ_ASSERT(GetNumFrames() == 1, "Should have only one frame");
MOZ_ASSERT(aPreviousFrame, "Need the previous frame to replace");
MOZ_ASSERT(!mFrameBlender && !mAnim, "Shouldn't be animated");
if (aFrameNum != 0 || !aPreviousFrame || GetNumFrames() != 1) {
return RawAccessFrameRef();
nsRefPtr<imgFrame> frame = mFrameBlender.RawGetFrame(aFrameNum);
if (!frame) {
return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
aPaletteDepth, imageData, imageLength,
paletteData, paletteLength, aRetFrame);
}
MOZ_ASSERT(!aPreviousFrame->GetRect().IsEqualEdges(aFrameRect) ||
aPreviousFrame->GetFormat() != aFormat ||
aPreviousFrame->GetPaletteDepth() != aPaletteDepth,
"Replacing first frame with the same kind of frame?");
// See if we can re-use the frame that already exists.
nsIntRect rect = frame->GetRect();
if (rect.x == aX && rect.y == aY && rect.width == aWidth &&
rect.height == aHeight && frame->GetFormat() == aFormat &&
frame->GetPaletteDepth() == aPaletteDepth) {
frame->GetImageData(imageData, imageLength);
if (paletteData) {
frame->GetPaletteData(paletteData, paletteLength);
}
// Remove the old frame from the SurfaceCache.
IntSize prevFrameSize = aPreviousFrame->GetRect().Size().ToIntSize();
SurfaceCache::RemoveSurface(ImageKey(this),
RasterSurfaceKey(prevFrameSize, aDecodeFlags));
mHasFirstFrame = false;
// We can re-use the frame if it has image data.
if (*imageData && paletteData && *paletteData) {
frame.forget(aRetFrame);
return NS_OK;
}
if (*imageData && !paletteData) {
frame.forget(aRetFrame);
return NS_OK;
}
}
// Add the new frame as usual.
return InternalAddFrame(aFrameNum, aFrameRect, aDecodeFlags, aFormat,
aPaletteDepth, aPreviousFrame);
// 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);
nsresult rv = newFrame->InitForDecoder(frameRect, aFormat, aPaletteDepth);
NS_ENSURE_SUCCESS(rv, rv);
return InternalAddFrameHelper(aFrameNum, newFrame, imageData, imageLength,
paletteData, paletteLength, aRetFrame);
}
void
RasterImage::DecodingComplete(imgFrame* aFinalFrame)
nsresult
RasterImage::EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
int32_t aWidth, int32_t aHeight,
SurfaceFormat aFormat,
uint8_t** imageData, uint32_t* imageLength,
imgFrame** aFrame)
{
return EnsureFrame(aFramenum, aX, aY, aWidth, aHeight, aFormat,
/* aPaletteDepth = */ 0, imageData, imageLength,
/* aPaletteData = */ nullptr,
/* aPaletteLength = */ nullptr,
aFrame);
}
nsresult
RasterImage::SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult)
{
if (mError)
return NS_ERROR_FAILURE;
NS_ABORT_IF_FALSE(aFrameNum < GetNumFrames(), "Invalid frame index!");
if (aFrameNum >= GetNumFrames())
return NS_ERROR_INVALID_ARG;
nsRefPtr<imgFrame> frame = mFrameBlender.RawGetFrame(aFrameNum);
NS_ABORT_IF_FALSE(frame, "Calling SetFrameAsNonPremult on frame that doesn't exist!");
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
frame->SetAsNonPremult(aIsNonPremult);
return NS_OK;
}
nsresult
RasterImage::DecodingComplete()
{
MOZ_ASSERT(NS_IsMainThread());
if (mError) {
return;
}
if (mError)
return NS_ERROR_FAILURE;
// Flag that we're done decoding.
// XXX - these should probably be combined when we fix animated image
@ -1295,17 +1327,14 @@ RasterImage::DecodingComplete(imgFrame* aFinalFrame)
mDecoded = true;
mHasBeenDecoded = true;
nsresult rv;
// We now have one of the qualifications for discarding. Re-evaluate.
if (CanDiscard()) {
NS_ABORT_IF_FALSE(!DiscardingActive(),
"We shouldn't have been discardable before this");
DiscardTracker::Reset(&mDiscardTrackerNode);
}
bool singleFrame = GetNumFrames() == 1;
if (singleFrame && aFinalFrame) {
// Record whether the first frame is opaque.
mFirstFrameIsOpaque = !aFinalFrame->GetNeedsBackground();
rv = DiscardTracker::Reset(&mDiscardTrackerNode);
CONTAINER_ENSURE_SUCCESS(rv);
}
// If there's only 1 frame, mark it as optimizable. Optimizing animated images
@ -1313,28 +1342,34 @@ RasterImage::DecodingComplete(imgFrame* aFinalFrame)
//
// We don't optimize the frame for multipart images because we reuse
// the frame.
if (singleFrame && !mMultipart && aFinalFrame) {
aFinalFrame->SetOptimizable();
if ((GetNumFrames() == 1) && !mMultipart) {
nsRefPtr<imgFrame> firstFrame = mFrameBlender.RawGetFrame(0);
firstFrame->SetOptimizable();
if (DiscardingEnabled() && CanForciblyDiscard()) {
firstFrame->SetDiscardable();
}
}
// Double-buffer our frame in the multipart case, since we'll start decoding
// into the first frame again immediately and this produces severe tearing.
if (mMultipart) {
if (singleFrame) {
// aFinalFrame must be the first frame since we only have one.
mMultipartDecodedFrame = aFinalFrame->DrawableRef();
if (GetNumFrames() == 1) {
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
// not displaying the still-decoding frame of an animated image. We may
// have already stored an extra frame, though, so we'll release it here.
mMultipartDecodedFrame.reset();
mMultipartDecodedFrame = nullptr;
}
}
if (mAnim) {
mAnim->SetDoneDecoding(true);
}
return NS_OK;
}
NS_IMETHODIMP
@ -1356,17 +1391,12 @@ RasterImage::StartAnimation()
NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!");
// If we don't have mAnim yet, then we're not ready to animate. Setting
// mPendingAnimation will cause us to start animating as soon as we have a
// second frame, which causes mAnim to be constructed.
mPendingAnimation = !mAnim;
if (mPendingAnimation) {
return NS_OK;
}
EnsureAnimExists();
nsRefPtr<imgFrame> currentFrame = LookupFrameNoDecode(GetCurrentFrameIndex());
// A timeout of -1 means we should display this frame forever.
if (mDecoded &&
mFrameBlender->GetTimeoutForFrame(GetCurrentFrameIndex()) < 0) {
if (currentFrame &&
mFrameBlender.GetTimeoutForFrame(GetCurrentFrameIndex()) < 0) {
mAnimationFinished = true;
return NS_ERROR_ABORT;
}
@ -1406,20 +1436,16 @@ RasterImage::ResetAnimation()
if (mError)
return NS_ERROR_FAILURE;
mPendingAnimation = false;
if (mAnimationMode == kDontAnimMode || !mAnim ||
mAnim->GetCurrentAnimationFrameIndex() == 0) {
if (mAnimationMode == kDontAnimMode ||
!mAnim || mAnim->GetCurrentAnimationFrameIndex() == 0)
return NS_OK;
}
mAnimationFinished = false;
if (mAnimating)
StopAnimation();
MOZ_ASSERT(mFrameBlender, "Should have a FrameBlender");
mFrameBlender->ResetAnimation();
mFrameBlender.ResetAnimation();
mAnim->ResetAnimation();
UpdateImageContainer();
@ -1468,8 +1494,7 @@ RasterImage::SetLoopCount(int32_t aLoopCount)
if (mAnim) {
// No need to set this if we're not an animation
MOZ_ASSERT(mFrameBlender, "Should have a FrameBlender");
mFrameBlender->SetLoopCount(aLoopCount);
mFrameBlender.SetLoopCount(aLoopCount);
}
}
@ -1515,14 +1540,13 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
if (mAnimating)
StopAnimation();
mAnimationFinished = false;
mPendingAnimation = false;
if (mAnim) {
mAnim = nullptr;
}
if (mFrameBlender) {
nsRefPtr<imgFrame> firstFrame = mFrameBlender->RawGetFrame(0);
mMultipartDecodedFrame = firstFrame->DrawableRef();
mFrameBlender.reset();
// If there's only one frame, this could cause flickering
int old_frame_count = GetNumFrames();
if (old_frame_count > 1) {
mFrameBlender.ClearFrames();
}
}
@ -1740,7 +1764,6 @@ RasterImage::OnNewSourceData()
mDecoded = false;
mHasSourceData = false;
mHasSize = false;
mHasFirstFrame = false;
mWantFullDecode = true;
mDecodeRequest = nullptr;
@ -1831,15 +1854,13 @@ RasterImage::Discard(bool force)
int old_frame_count = GetNumFrames();
// Delete all the decoded frames
mFrameBlender.reset();
SurfaceCache::RemoveImage(this);
mFrameBlender.Discard();
// Clear the last decoded multipart frame.
mMultipartDecodedFrame.reset();
mMultipartDecodedFrame = nullptr;
// Flag that we no longer have decoded frames for this image
mDecoded = false;
mHasFirstFrame = false;
// Notify that we discarded
if (mStatusTracker)
@ -1956,6 +1977,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
if (!mDecodeRequest) {
mDecodeRequest = new DecodeRequest(this);
@ -2031,6 +2060,13 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
mInDecoder = false;
mFinishing = false;
// 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);
@ -2636,19 +2672,13 @@ RasterImage::Draw(gfxContext* aContext,
NS_ENSURE_SUCCESS(rv, rv);
}
// XXX(seth): For now, we deliberately don't look up a frame of size aSize
// (though DrawWithPreDownscaleIfNeeded will do so later). It doesn't make
// sense to do so until we support downscale-during-decode. Right now we need
// to make sure that we always touch an mSize-sized frame so that we have
// something to HQ scale.
DrawableFrameRef ref = LookupFrame(GetRequestedFrameIndex(aWhichFrame),
mSize, aFlags);
aFlags);
if (!ref) {
return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
}
DrawWithPreDownscaleIfNeeded(Move(ref), aContext, aSize,
aRegion, aFilter, aFlags);
DrawWithPreDownscaleIfNeeded(Move(ref), aContext, aSize, aRegion, aFilter, aFlags);
if (mDecoded && !mDrawStartTime.IsNull()) {
TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
@ -2676,11 +2706,6 @@ RasterImage::LockImage()
// Increment the lock count
mLockCount++;
// Lock this image's surfaces in the SurfaceCache.
if (mLockCount == 1) {
SurfaceCache::LockImage(ImageKey(this));
}
return NS_OK;
}
@ -2706,11 +2731,6 @@ RasterImage::UnlockImage()
// Decrement our lock count
mLockCount--;
// Unlock this image's surfaces in the SurfaceCache.
if (mLockCount == 0) {
SurfaceCache::UnlockImage(ImageKey(this));
}
// If we've decoded this image once before, we're currently decoding again,
// and our lock count is now zero (so nothing is forcing us to keep the
// decoded data around), try to cancel the decode and throw away whatever
@ -3574,8 +3594,7 @@ RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
}
if (!frameRef) {
// We could HQ scale to this size, but we haven't. Request a scale now.
frameRef = LookupFrame(GetRequestedFrameIndex(aWhichFrame),
mSize, aFlags);
frameRef = LookupFrame(GetRequestedFrameIndex(aWhichFrame), aFlags);
if (frameRef) {
RequestScale(frameRef.get(), aFlags, destSize);
}

View File

@ -29,7 +29,6 @@
#include "DiscardTracker.h"
#include "Orientation.h"
#include "nsIObserver.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Mutex.h"
#include "mozilla/ReentrantMonitor.h"
@ -187,6 +186,8 @@ public:
void ForceDiscard() { Discard(/* force = */ true); }
/* Callbacks for decoders */
nsresult SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult);
/** Sets the size and inherent orientation of the container. This should only
* be called by the decoder. This function may be called multiple times, but
* will throw an error if subsequent calls do not match the first.
@ -195,21 +196,33 @@ public:
/**
* Ensures that a given frame number exists with the given parameters, and
* returns a RawAccessFrameRef for that frame.
* returns pointers to the data storage for that frame.
* It is not possible to create sparse frame arrays; you can only append
* frames to the current frame array, or if there is only one frame in the
* array, replace that frame.
* If a non-paletted frame is desired, pass 0 for aPaletteDepth.
* frames to the current frame array.
*/
RawAccessFrameRef EnsureFrame(uint32_t aFrameNum,
const nsIntRect& aFrameRect,
uint32_t aDecodeFlags,
gfx::SurfaceFormat aFormat,
uint8_t aPaletteDepth,
imgFrame* aPreviousFrame);
nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
int32_t aWidth, int32_t aHeight,
gfx::SurfaceFormat aFormat,
uint8_t aPaletteDepth,
uint8_t** imageData,
uint32_t* imageLength,
uint32_t** paletteData,
uint32_t* paletteLength,
imgFrame** aFrame);
/**
* A shorthand for EnsureFrame, above, with aPaletteDepth = 0 and paletteData
* and paletteLength set to null.
*/
nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
int32_t aWidth, int32_t aHeight,
gfx::SurfaceFormat aFormat,
uint8_t** imageData,
uint32_t* imageLength,
imgFrame** aFrame);
/* notification that the entire image has been decoded */
void DecodingComplete(imgFrame* aFinalFrame);
nsresult DecodingComplete();
/**
* Number of times to loop the image.
@ -534,10 +547,8 @@ private:
uint32_t aFlags,
bool aShouldSyncNotify = true);
DrawableFrameRef LookupFrame(uint32_t aFrameNum,
const nsIntSize& aSize,
uint32_t aFlags,
bool aShouldSyncNotify = true);
already_AddRefed<imgFrame> LookupFrameNoDecode(uint32_t aFrameNum);
DrawableFrameRef LookupFrame(uint32_t aFrameNum, uint32_t aFlags, bool aShouldSyncNotify = true);
uint32_t GetCurrentFrameIndex() const;
uint32_t GetRequestedFrameIndex(uint32_t aWhichFrame) const;
@ -546,12 +557,16 @@ private:
void EnsureAnimExists();
RawAccessFrameRef InternalAddFrame(uint32_t aFrameNum,
const nsIntRect& aFrameRect,
uint32_t aDecodeFlags,
gfx::SurfaceFormat aFormat,
uint8_t aPaletteDepth,
imgFrame* aPreviousFrame);
nsresult InternalAddFrameHelper(uint32_t framenum, imgFrame *frame,
uint8_t **imageData, uint32_t *imageLength,
uint32_t **paletteData, uint32_t *paletteLength,
imgFrame** aRetFrame);
nsresult InternalAddFrame(uint32_t framenum, int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
gfx::SurfaceFormat aFormat, uint8_t aPaletteDepth,
uint8_t **imageData, uint32_t *imageLength,
uint32_t **paletteData, uint32_t *paletteLength,
imgFrame** aRetFrame);
nsresult DoImageDataComplete();
bool ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame);
@ -588,11 +603,11 @@ private: // data
// and imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION.
uint32_t mFrameDecodeFlags;
//! All the frames of the image.
Maybe<FrameBlender> mFrameBlender;
//! All the frames of the image
FrameBlender mFrameBlender;
//! The last frame we decoded for multipart images.
DrawableFrameRef mMultipartDecodedFrame;
// The last frame we decoded for multipart images.
nsRefPtr<imgFrame> mMultipartDecodedFrame;
nsCOMPtr<nsIProperties> mProperties;
@ -661,14 +676,6 @@ private: // data
bool mDecoded:1;
bool mHasBeenDecoded:1;
// Data about the first frame.
bool mHasFirstFrame:1;
bool mFirstFrameIsOpaque:1;
// Whether we're waiting to start animation. If we get a StartAnimation() call
// but we don't yet have more than one frame, mPendingAnimation is set so that
// we know to start animation later if/when we have more frames.
bool mPendingAnimation:1;
// Whether the animation can stop, due to running out
// of frames, or no more owning request

View File

@ -54,7 +54,7 @@ static StaticRefPtr<SurfaceCacheImpl> sInstance;
// SurfaceCache Implementation
///////////////////////////////////////////////////////////////////////////////
/**
/*
* Cost models the cost of storing a surface in the cache. Right now, this is
* simply an estimate of the size of the surface in bytes, but in the future it
* may be worth taking into account the cost of rematerializing the surface as
@ -67,7 +67,7 @@ static Cost ComputeCost(const IntSize& aSize)
return aSize.width * aSize.height * 4; // width * height * 4 bytes (32bpp)
}
/**
/*
* Since we want to be able to make eviction decisions based on cost, we need to
* be able to look up the CachedSurface which has a certain cost as well as the
* cost associated with a certain CachedSurface. To make this possible, in data
@ -108,7 +108,7 @@ private:
Cost mCost;
};
/**
/*
* A CachedSurface associates a surface with a key that uniquely identifies that
* surface.
*/
@ -118,18 +118,18 @@ class CachedSurface
public:
NS_INLINE_DECL_REFCOUNTING(CachedSurface)
CachedSurface(imgFrame* aSurface,
const Cost aCost,
const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey,
const Lifetime aLifetime)
CachedSurface(imgFrame* aSurface,
const IntSize aTargetSize,
const Cost aCost,
const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)
: mSurface(aSurface)
, mTargetSize(aTargetSize)
, mCost(aCost)
, mImageKey(aImageKey)
, mSurfaceKey(aSurfaceKey)
, mLifetime(aLifetime)
{
MOZ_ASSERT(mSurface, "Must have a valid surface");
MOZ_ASSERT(mSurface, "Must have a valid SourceSurface");
MOZ_ASSERT(mImageKey, "Must have a valid image key");
}
@ -138,62 +138,18 @@ public:
return mSurface->DrawableRef();
}
void SetLocked(bool aLocked)
{
if (aLocked && mLifetime == Lifetime::Persistent) {
// This may fail, and that's OK. We make no guarantees about whether
// locking is successful if you call SurfaceCache::LockImage() after
// SurfaceCache::Insert().
mDrawableRef = mSurface->DrawableRef();
} else {
mDrawableRef.reset();
}
}
bool IsLocked() const { return bool(mDrawableRef); }
ImageKey GetImageKey() const { return mImageKey; }
SurfaceKey GetSurfaceKey() const { return mSurfaceKey; }
CostEntry GetCostEntry() { return image::CostEntry(this, mCost); }
nsExpirationState* GetExpirationState() { return &mExpirationState; }
Lifetime GetLifetime() const { return mLifetime; }
// A helper type used by SurfaceCacheImpl::SizeOfSurfacesSum.
struct SizeOfSurfacesSum
{
SizeOfSurfacesSum(gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf)
: mLocation(aLocation)
, mMallocSizeOf(aMallocSizeOf)
, mSum(0)
{ }
void Add(CachedSurface* aCachedSurface)
{
if (!aCachedSurface || !aCachedSurface->mSurface) {
return;
}
mSum += aCachedSurface->mSurface->
SizeOfExcludingThisWithComputedFallbackIfHeap(mLocation, mMallocSizeOf);
}
size_t Result() const { return mSum; }
private:
gfxMemoryLocation mLocation;
MallocSizeOf mMallocSizeOf;
size_t mSum;
};
private:
nsExpirationState mExpirationState;
nsRefPtr<imgFrame> mSurface;
DrawableFrameRef mDrawableRef;
const IntSize mTargetSize;
const Cost mCost;
const ImageKey mImageKey;
const SurfaceKey mSurfaceKey;
const Lifetime mLifetime;
};
/*
@ -201,16 +157,11 @@ private:
* able to remove all surfaces associated with an image when the image is
* destroyed or invalidated. Since this will happen frequently, it makes sense
* to make it cheap by storing the surfaces for each image separately.
*
* ImageSurfaceCache also keeps track of whether its associated image is locked
* or unlocked.
*/
class ImageSurfaceCache
{
~ImageSurfaceCache() { }
~ImageSurfaceCache() {}
public:
ImageSurfaceCache() : mLocked(false) { }
NS_INLINE_DECL_REFCOUNTING(ImageSurfaceCache)
typedef nsRefPtrHashtable<nsGenericHashKey<SurfaceKey>, CachedSurface> SurfaceTable;
@ -220,9 +171,6 @@ public:
void Insert(const SurfaceKey& aKey, CachedSurface* aSurface)
{
MOZ_ASSERT(aSurface, "Should have a surface");
MOZ_ASSERT(!mLocked || aSurface->GetLifetime() != Lifetime::Persistent ||
aSurface->IsLocked(),
"Inserting an unlocked persistent surface for a locked image");
mSurfaces.Put(aKey, aSurface);
}
@ -247,12 +195,8 @@ public:
mSurfaces.EnumerateRead(aFunction, aData);
}
void SetLocked(bool aLocked) { mLocked = aLocked; }
bool IsLocked() const { return mLocked; }
private:
SurfaceTable mSurfaces;
bool mLocked;
};
/*
@ -274,7 +218,6 @@ public:
, mMemoryPressureObserver(new MemoryPressureObserver)
, mMaxCost(aSurfaceCacheSize)
, mAvailableCost(aSurfaceCacheSize)
, mLockedCost(0)
{
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os)
@ -296,22 +239,23 @@ public:
RegisterWeakMemoryReporter(this);
}
bool Insert(imgFrame* aSurface,
void Insert(imgFrame* aSurface,
IntSize aTargetSize,
const Cost aCost,
const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey,
Lifetime aLifetime)
const SurfaceKey& aSurfaceKey)
{
MOZ_ASSERT(!Lookup(aImageKey, aSurfaceKey),
"Inserting a duplicate surface into the SurfaceCache");
// If this is bigger than we can hold after discarding everything we can,
// refuse to cache it.
if (!CanHoldAfterDiscarding(aCost))
return false;
// If this is bigger than the maximum cache size, refuse to cache it.
if (!CanHold(aCost))
return;
// Remove elements in order of cost until we can fit this in the cache. Note
// that locked surfaces aren't in mCosts, so we never remove them here.
nsRefPtr<CachedSurface> surface =
new CachedSurface(aSurface, aTargetSize, aCost, aImageKey, aSurfaceKey);
// Remove elements in order of cost until we can fit this in the cache.
while (aCost > mAvailableCost) {
MOZ_ASSERT(!mCosts.IsEmpty(), "Removed everything and it still won't fit");
Remove(mCosts.LastElement().GetSurface());
@ -325,24 +269,10 @@ public:
mImageCaches.Put(aImageKey, cache);
}
nsRefPtr<CachedSurface> surface =
new CachedSurface(aSurface, aCost, aImageKey, aSurfaceKey, aLifetime);
// We require that locking succeed if the image is locked and the surface is
// persistent; the caller may need to know this to handle errors correctly.
if (cache->IsLocked() && aLifetime == Lifetime::Persistent) {
surface->SetLocked(true);
if (!surface->IsLocked()) {
return false;
}
}
// Insert.
MOZ_ASSERT(aCost <= mAvailableCost, "Inserting despite too large a cost");
cache->Insert(aSurfaceKey, surface);
StartTracking(surface);
return true;
}
void Remove(CachedSurface* aSurface)
@ -356,9 +286,8 @@ public:
StopTracking(aSurface);
cache->Remove(aSurface);
// Remove the per-image cache if it's unneeded now. (Keep it if the image is
// locked, since the per-image cache is where we store that state.)
if (cache->IsEmpty() && !cache->IsLocked()) {
// Remove the per-image cache if it's unneeded now.
if (cache->IsEmpty()) {
mImageCaches.Remove(imageKey);
}
}
@ -370,14 +299,8 @@ public:
"Cost too large and the caller didn't catch it");
mAvailableCost -= costEntry.GetCost();
if (aSurface->IsLocked()) {
mLockedCost += costEntry.GetCost();
MOZ_ASSERT(mLockedCost <= mMaxCost, "Locked more than we can hold?");
} else {
mCosts.InsertElementSorted(costEntry);
mExpirationTracker.AddObject(aSurface);
}
mCosts.InsertElementSorted(costEntry);
mExpirationTracker.AddObject(aSurface);
}
void StopTracking(CachedSurface* aSurface)
@ -385,21 +308,12 @@ public:
MOZ_ASSERT(aSurface, "Should have a surface");
CostEntry costEntry = aSurface->GetCostEntry();
if (aSurface->IsLocked()) {
MOZ_ASSERT(mLockedCost >= costEntry.GetCost(), "Costs don't balance");
mLockedCost -= costEntry.GetCost();
// XXX(seth): It'd be nice to use an O(log n) lookup here. This is O(n).
MOZ_ASSERT(!mCosts.Contains(costEntry),
"Shouldn't have a cost entry for a locked surface");
} else {
mExpirationTracker.RemoveObject(aSurface);
DebugOnly<bool> foundInCosts = mCosts.RemoveElementSorted(costEntry);
MOZ_ASSERT(foundInCosts, "Lost track of costs for this surface");
}
mExpirationTracker.RemoveObject(aSurface);
DebugOnly<bool> foundInCosts = mCosts.RemoveElementSorted(costEntry);
mAvailableCost += costEntry.GetCost();
MOZ_ASSERT(mAvailableCost <= mMaxCost,
"More available cost than we started with");
MOZ_ASSERT(foundInCosts, "Lost track of costs for this surface");
MOZ_ASSERT(mAvailableCost <= mMaxCost, "More available cost than we started with");
}
DrawableFrameRef Lookup(const ImageKey aImageKey,
@ -421,15 +335,12 @@ public:
return DrawableFrameRef();
}
if (!surface->IsLocked()) {
mExpirationTracker.MarkUsed(surface);
}
mExpirationTracker.MarkUsed(surface);
return ref;
}
void RemoveSurface(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)
void RemoveIfPresent(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)
{
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
if (!cache)
@ -447,33 +358,7 @@ public:
return aCost <= mMaxCost;
}
void LockImage(const ImageKey aImageKey)
{
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
if (!cache) {
cache = new ImageSurfaceCache;
mImageCaches.Put(aImageKey, cache);
}
cache->SetLocked(true);
// Try to lock all the surfaces the per-image cache is holding.
cache->ForEach(DoLockSurface, this);
}
void UnlockImage(const ImageKey aImageKey)
{
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
if (!cache)
return; // Already unlocked and removed.
cache->SetLocked(false);
// Unlock all the surfaces the per-image cache is holding.
cache->ForEach(DoUnlockSurface, this);
}
void RemoveImage(const ImageKey aImageKey)
void Discard(const ImageKey aImageKey)
{
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
if (!cache)
@ -487,16 +372,13 @@ public:
cache->ForEach(DoStopTracking, this);
// The per-image cache isn't needed anymore, so remove it as well.
// This implicitly unlocks the image if it was locked.
mImageCaches.Remove(aImageKey);
}
void DiscardAll()
{
// Remove in order of cost because mCosts is an array and the other data
// structures are all hash tables. Note that locked surfaces (persistent
// surfaces belonging to locked images) are not removed, since they aren't
// present in mCosts.
// structures are all hash tables.
while (!mCosts.IsEmpty()) {
Remove(mCosts.LastElement().GetSurface());
}
@ -510,93 +392,23 @@ public:
return PL_DHASH_NEXT;
}
static PLDHashOperator DoLockSurface(const SurfaceKey&,
CachedSurface* aSurface,
void* aCache)
{
if (aSurface->GetLifetime() == Lifetime::Transient ||
aSurface->IsLocked()) {
return PL_DHASH_NEXT;
}
auto cache = static_cast<SurfaceCacheImpl*>(aCache);
cache->StopTracking(aSurface);
// Lock the surface. This can fail.
aSurface->SetLocked(true);
cache->StartTracking(aSurface);
return PL_DHASH_NEXT;
}
static PLDHashOperator DoUnlockSurface(const SurfaceKey&,
CachedSurface* aSurface,
void* aCache)
{
if (aSurface->GetLifetime() == Lifetime::Transient ||
!aSurface->IsLocked()) {
return PL_DHASH_NEXT;
}
auto cache = static_cast<SurfaceCacheImpl*>(aCache);
cache->StopTracking(aSurface);
aSurface->SetLocked(false);
cache->StartTracking(aSurface);
return PL_DHASH_NEXT;
}
NS_IMETHOD
CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData,
bool aAnonymize) MOZ_OVERRIDE
CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
bool aAnonymize)
{
// We have explicit memory reporting for the surface cache which is more
// accurate than the cost metrics we report here, but these metrics are
// still useful to report, since they control the cache's behavior.
nsresult rv;
rv = MOZ_COLLECT_REPORT("imagelib-surface-cache-estimated-total",
KIND_OTHER, UNITS_BYTES,
(mMaxCost - mAvailableCost),
"Estimated total memory used by the imagelib "
"surface cache.");
NS_ENSURE_SUCCESS(rv, rv);
rv = MOZ_COLLECT_REPORT("imagelib-surface-cache-estimated-locked",
KIND_OTHER, UNITS_BYTES,
mLockedCost,
"Estimated memory used by locked surfaces in the "
"imagelib surface cache.");
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
return MOZ_COLLECT_REPORT(
"imagelib-surface-cache", KIND_OTHER, UNITS_BYTES,
SizeOfSurfacesEstimate(),
"Memory used by the imagelib temporary surface cache.");
}
size_t SizeOfSurfaces(const ImageKey aImageKey,
gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf)
// XXX(seth): This is currently only an estimate and, since we don't know
// which surfaces are in GPU memory and which aren't, it's reported as
// KIND_OTHER and will also show up in heap-unclassified. Bug 923302 will
// make this nicer.
Cost SizeOfSurfacesEstimate() const
{
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
if (!cache) {
return 0; // No surfaces for this image.
}
// Sum the size of all surfaces in the per-image cache.
CachedSurface::SizeOfSurfacesSum sum(aLocation, aMallocSizeOf);
cache->ForEach(DoSizeOfSurfacesSum, &sum);
return sum.Result();
}
static PLDHashOperator DoSizeOfSurfacesSum(const SurfaceKey&,
CachedSurface* aSurface,
void* aSum)
{
auto sum = static_cast<CachedSurface::SizeOfSurfacesSum*>(aSum);
sum->Add(aSurface);
return PL_DHASH_NEXT;
return mMaxCost - mAvailableCost;
}
private:
@ -607,16 +419,6 @@ private:
return imageCache.forget();
}
// This is similar to CanHold() except that it takes into account the costs of
// locked surfaces. It's used internally in Insert(), but it's not exposed
// publicly because if we start permitting multithreaded access to the surface
// cache, which seems likely, then the result would be meaningless: another
// thread could insert a persistent surface or lock an image at any time.
bool CanHoldAfterDiscarding(const Cost aCost) const
{
return aCost + mLockedCost <= mMaxCost;
}
struct SurfaceTracker : public nsExpirationTracker<CachedSurface, 2>
{
SurfaceTracker(SurfaceCacheImpl* aCache, uint32_t aSurfaceCacheExpirationTimeMS)
@ -640,9 +442,7 @@ private:
{
NS_DECL_ISUPPORTS
NS_IMETHOD Observe(nsISupports*,
const char* aTopic,
const char16_t*) MOZ_OVERRIDE
NS_IMETHOD Observe(nsISupports*, const char* aTopic, const char16_t*)
{
if (sInstance && strcmp(aTopic, "memory-pressure") == 0) {
sInstance->DiscardAll();
@ -661,7 +461,6 @@ private:
nsRefPtr<MemoryPressureObserver> mMemoryPressureObserver;
const Cost mMaxCost;
Cost mAvailableCost;
Cost mLockedCost;
};
NS_IMPL_ISUPPORTS(SurfaceCacheImpl, nsIMemoryReporter)
@ -728,19 +527,17 @@ SurfaceCache::Lookup(const ImageKey aImageKey,
return sInstance->Lookup(aImageKey, aSurfaceKey);
}
/* static */ bool
/* static */ void
SurfaceCache::Insert(imgFrame* aSurface,
const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey,
Lifetime aLifetime)
const SurfaceKey& aSurfaceKey)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sInstance) {
return false;
if (sInstance) {
Cost cost = ComputeCost(aSurfaceKey.Size());
sInstance->Insert(aSurface, aSurfaceKey.Size(), cost, aImageKey,
aSurfaceKey);
}
Cost cost = ComputeCost(aSurfaceKey.Size());
return sInstance->Insert(aSurface, cost, aImageKey, aSurfaceKey, aLifetime);
}
/* static */ bool
@ -756,39 +553,21 @@ SurfaceCache::CanHold(const IntSize& aSize)
}
/* static */ void
SurfaceCache::LockImage(Image* aImageKey)
SurfaceCache::RemoveIfPresent(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)
{
MOZ_ASSERT(NS_IsMainThread());
if (sInstance) {
return sInstance->LockImage(aImageKey);
sInstance->RemoveIfPresent(aImageKey, aSurfaceKey);
}
}
/* static */ void
SurfaceCache::UnlockImage(Image* aImageKey)
SurfaceCache::Discard(Image* aImageKey)
{
MOZ_ASSERT(NS_IsMainThread());
if (sInstance) {
return sInstance->UnlockImage(aImageKey);
}
}
/* static */ void
SurfaceCache::RemoveSurface(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)
{
MOZ_ASSERT(NS_IsMainThread());
if (sInstance) {
sInstance->RemoveSurface(aImageKey, aSurfaceKey);
}
}
/* static */ void
SurfaceCache::RemoveImage(Image* aImageKey)
{
MOZ_ASSERT(NS_IsMainThread());
if (sInstance) {
sInstance->RemoveImage(aImageKey);
sInstance->Discard(aImageKey);
}
}
@ -801,18 +580,5 @@ SurfaceCache::DiscardAll()
}
}
/* static */ size_t
SurfaceCache::SizeOfSurfaces(const ImageKey aImageKey,
gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sInstance) {
return 0;
}
return sInstance->SizeOfSurfaces(aImageKey, aLocation, aMallocSizeOf);
}
} // namespace image
} // namespace mozilla

View File

@ -11,15 +11,13 @@
#ifndef MOZILLA_IMAGELIB_SURFACECACHE_H_
#define MOZILLA_IMAGELIB_SURFACECACHE_H_
#include "mozilla/Maybe.h" // for Maybe
#include "mozilla/MemoryReporting.h" // for MallocSizeOf
#include "mozilla/HashFunctions.h" // for HashGeneric and AddToHash
#include "gfx2DGlue.h" // for gfxMemoryLocation
#include "gfxPoint.h" // for gfxSize
#include "nsCOMPtr.h" // for already_AddRefed
#include "mozilla/gfx/Point.h" // for mozilla::gfx::IntSize
#include "mozilla/gfx/2D.h" // for SourceSurface
#include "SVGImageContext.h" // for SVGImageContext
#include "mozilla/Maybe.h" // for Maybe
#include "mozilla/HashFunctions.h" // for HashGeneric and AddToHash
#include "gfxPoint.h" // for gfxSize
#include "nsCOMPtr.h" // for already_AddRefed
#include "mozilla/gfx/Point.h" // for mozilla::gfx::IntSize
#include "mozilla/gfx/2D.h" // for SourceSurface
#include "SVGImageContext.h" // for SVGImageContext
namespace mozilla {
namespace image {
@ -111,33 +109,16 @@ VectorSurfaceKey(const gfx::IntSize& aSize,
return SurfaceKey(aSize, aSVGContext, aAnimationTime, 0);
}
MOZ_BEGIN_ENUM_CLASS(Lifetime, uint8_t)
Transient,
Persistent
MOZ_END_ENUM_CLASS(Lifetime)
/**
* SurfaceCache is an imagelib-global service that allows caching of temporary
* surfaces. Surfaces normally expire from the cache automatically if they go
* too long without being accessed.
* surfaces. Surfaces expire from the cache automatically if they go too long
* without being accessed.
*
* SurfaceCache does not hold surfaces directly; instead, it holds imgFrame
* objects, which hold surfaces but also layer on additional features specific
* to imagelib's needs like animation, padding support, and transparent support
* for volatile buffers.
*
* Sometime it's useful to temporarily prevent surfaces from expiring from the
* cache. This is most often because losing the data could harm the user
* experience (for example, we often don't want to allow surfaces that are
* currently visible to expire) or because it's not possible to rematerialize
* the surface. SurfaceCache supports this through the use of image locking and
* surface lifetimes; see the comments for Insert() and LockImage() for more
* details.
*
* Any image which stores surfaces in the SurfaceCache *must* ensure that it
* calls RemoveImage() before it is destroyed. See the comments for
* RemoveImage() for more details.
*
* SurfaceCache is not thread-safe; it should only be accessed from the main
* thread.
*/
@ -145,25 +126,23 @@ struct SurfaceCache
{
typedef gfx::IntSize IntSize;
/**
/*
* Initialize static data. Called during imagelib module initialization.
*/
static void Initialize();
/**
/*
* Release static data. Called during imagelib module shutdown.
*/
static void Shutdown();
/**
/*
* Look up the imgFrame containing a surface in the cache and returns a
* drawable reference to that imgFrame.
*
* If the imgFrame was found in the cache, but had stored its surface in a
* volatile buffer which was discarded by the OS, then it is automatically
* removed from the cache and an empty DrawableFrameRef is returned. Note that
* this will never happen to persistent surfaces associated with a locked
* image; the cache keeps a strong reference to such surfaces internally.
* removed from the cache and an empty DrawableFrameRef is returned.
*
* @param aImageKey Key data identifying which image the surface belongs to.
* @param aSurfaceKey Key data which uniquely identifies the requested surface.
@ -174,46 +153,21 @@ struct SurfaceCache
static DrawableFrameRef Lookup(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey);
/**
/*
* Insert a surface into the cache. It is an error to call this function
* without first calling Lookup to verify that the surface is not already in
* the cache.
*
* Each surface in the cache has a lifetime, either Transient or Persistent.
* Transient surfaces can expire from the cache at any time. Persistent
* surfaces can ordinarily also expire from the cache at any time, but if the
* image they're associated with is locked, then these surfaces will never
* expire. This means that surfaces which cannot be rematerialized should be
* inserted with a persistent lifetime *after* the image is locked with
* LockImage(); if you use the other order, the surfaces might expire before
* LockImage() gets called.
*
* If a surface cannot be rematerialized, it may be important to know whether
* it was inserted into the cache successfully. Insert() returns false if it
* failed to insert the surface, which could happen because of capacity
* reasons, or because it was already freed by the OS. If you aren't inserting
* a surface with persistent lifetime, or if the surface isn't associated with
* a locked image, the return value is useless: the surface might expire
* immediately after being inserted, even though Insert() returned true. Thus,
* most callers do not need to check the return value.
*
* @param aTarget The new surface (wrapped in an imgFrame) to insert into
* the cache.
* @param aImageKey Key data identifying which image the surface belongs to.
* @param aSurfaceKey Key data which uniquely identifies the requested surface.
* @param aLifetime Whether this is a transient surface that can always be
* allowed to expire, or a persistent surface that
* shouldn't expire if the image is locked.
* @return false if the surface could not be inserted. Only check this if
* inserting a persistent surface associated with a locked image (see
* above for more information).
*/
static bool Insert(imgFrame* aSurface,
static void Insert(imgFrame* aSurface,
const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey,
Lifetime aLifetime);
const SurfaceKey& aSurfaceKey);
/**
/*
* Checks if a surface of a given size could possibly be stored in the cache.
* If CanHold() returns false, Insert() will always fail to insert the
* surface, but the inverse is not true: Insert() may take more information
@ -229,89 +183,33 @@ struct SurfaceCache
*/
static bool CanHold(const IntSize& aSize);
/**
* Locks an image, preventing any of that image's surfaces from expiring
* unless they have a transient lifetime.
*
* Regardless of locking, any of an image's surfaces may be removed using
* RemoveSurface(), and all of an image's surfaces are removed by
* RemoveImage(), whether the image is locked or not.
*
* It's safe to call LockImage() on an image that's already locked; this has
* no effect.
*
* You must always unlock any image you lock. You may do this explicitly by
* calling UnlockImage(), or implicitly by calling RemoveImage(). Since you're
* required to call RemoveImage() when you destroy an image, this doesn't
* impose any additional requirements, but it's preferable to call
* UnlockImage() earlier if it's possible.
*
* @param aImageKey The image to lock.
*/
static void LockImage(const ImageKey aImageKey);
/**
* Unlocks an image, allowing any of its surfaces to expire at any time.
*
* It's OK to call UnlockImage() on an image that's already unlocked; this has
* no effect.
*
* @param aImageKey The image to lock.
*/
static void UnlockImage(const ImageKey aImageKey);
/**
* Removes a surface from the cache, if it's present. If it's not present,
* RemoveSurface() has no effect.
/*
* Removes a surface from the cache, if it's present.
*
* Use this function to remove individual surfaces that have become invalid.
* Prefer RemoveImage() or DiscardAll() when they're applicable, as they have
* much better performance than calling this function repeatedly.
* Prefer Discard() or DiscardAll() when they're applicable, as they have much
* better performance than calling this function repeatedly.
*
* @param aImageKey Key data identifying which image the surface belongs to.
* @param aSurfaceKey Key data which uniquely identifies the requested surface.
*/
static void RemoveSurface(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey);
/**
* Removes all cached surfaces associated with the given image from the cache.
* If the image is locked, it is automatically unlocked.
*
* This MUST be called, at a minimum, when an Image which could be storing
* surfaces in the surface cache is destroyed. If another image were allocated
* at the same address it could result in subtle, difficult-to-reproduce bugs.
static void RemoveIfPresent(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey);
/*
* Evicts any cached surfaces associated with the given image from the cache.
* This MUST be called, at a minimum, when the image is destroyed. If
* another image were allocated at the same address it could result in
* subtle, difficult-to-reproduce bugs.
*
* @param aImageKey The image which should be removed from the cache.
*/
static void RemoveImage(const ImageKey aImageKey);
static void Discard(const ImageKey aImageKey);
/**
* Evicts all evictable surfaces from the cache.
*
* All surfaces are evictable except for persistent surfaces associated with
* locked images. Non-evictable surfaces can only be removed by
* RemoveSurface() or RemoveImage().
/*
* Evicts all caches surfaces from ths cache.
*/
static void DiscardAll();
/**
* Computes the size of the surfaces stored for the given image at the given
* memory location.
*
* This is intended for use with memory reporting.
*
* @param aImageKey The image to report memory usage for.
* @param aLocation The location (heap, nonheap, etc.) of the memory to
* report on.
* @param aMallocSizeOf A fallback malloc memory reporting function. This
* should be null unless we're reporting on in-process
* heap memory.
*/
static size_t SizeOfSurfaces(const ImageKey aImageKey,
gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf);
private:
virtual ~SurfaceCache() = 0; // Forbid instantiation.
};

View File

@ -338,7 +338,7 @@ VectorImage::VectorImage(imgStatusTracker* aStatusTracker,
VectorImage::~VectorImage()
{
CancelAllListeners();
SurfaceCache::RemoveImage(ImageKey(this));
SurfaceCache::Discard(this);
}
//------------------------------------------------------------------------------
@ -384,30 +384,25 @@ VectorImage::HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) c
// If implementing this, we'll need to restructure our callers to make sure
// any amount we return is attributed to the vector images measure (i.e.
// "explicit/images/{content,chrome}/vector/{used,unused}/...")
// XXX(seth): Same goes for the other *SizeOfDecoded() methods. We'll do this
// in bug 921300 or one of its blockers. For now it seems worthwhile to get
// this memory accounted for, even if it gets listed under 'raster'. It does
// make some perverse sense, since we are after all reporting on raster data
// here - it just happens to be computed from a vector document.
return SurfaceCache::SizeOfSurfaces(ImageKey(this),
gfxMemoryLocation::IN_PROCESS_HEAP,
aMallocSizeOf);
return 0;
}
size_t
VectorImage::NonHeapSizeOfDecoded() const
{
return SurfaceCache::SizeOfSurfaces(ImageKey(this),
gfxMemoryLocation::IN_PROCESS_NONHEAP,
nullptr);
// If implementing this, we'll need to restructure our callers to make sure
// any amount we return is attributed to the vector images measure (i.e.
// "explicit/images/{content,chrome}/vector/{used,unused}/...")
return 0;
}
size_t
VectorImage::OutOfProcessSizeOfDecoded() const
{
return SurfaceCache::SizeOfSurfaces(ImageKey(this),
gfxMemoryLocation::OUT_OF_PROCESS,
nullptr);
// If implementing this, we'll need to restructure our callers to make sure
// any amount we return is attributed to the vector images measure (i.e.
// "explicit/images/{content,chrome}/vector/{used,unused}/...")
return 0;
}
MOZ_DEFINE_MALLOC_SIZE_OF(WindowsMallocSizeOf);
@ -575,7 +570,7 @@ VectorImage::SendInvalidationNotifications()
// notifications directly in |InvalidateObservers...|.
if (mStatusTracker) {
SurfaceCache::RemoveImage(ImageKey(this));
SurfaceCache::Discard(this);
mStatusTracker->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
mStatusTracker->OnStopFrame();
}
@ -919,8 +914,7 @@ VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams)
SurfaceCache::Insert(frame, ImageKey(this),
VectorSurfaceKey(aParams.size,
aParams.svgContext,
aParams.animationTime),
Lifetime::Transient);
aParams.animationTime));
// Draw.
nsRefPtr<gfxDrawable> drawable =
@ -988,7 +982,7 @@ VectorImage::UnlockImage()
NS_IMETHODIMP
VectorImage::RequestDiscard()
{
SurfaceCache::RemoveImage(ImageKey(this));
SurfaceCache::Discard(this);
return NS_OK;
}

View File

@ -123,6 +123,7 @@ imgFrame::imgFrame() :
mCompositingFailed(false),
mHasNoAlpha(false),
mNonPremult(false),
mDiscardable(false),
mOptimizable(false),
mInformedDiscardTracker(false)
{
@ -414,7 +415,9 @@ nsresult imgFrame::Optimize()
// allows the operating system to free our volatile buffer.
// XXX(seth): We'd eventually like to do this on all platforms, but right now
// converting raw memory to a SourceSurface is expensive on some backends.
mImageSurface = nullptr;
if (mDiscardable) {
mImageSurface = nullptr;
}
#endif
return NS_OK;
@ -583,17 +586,8 @@ SurfaceFormat imgFrame::GetFormat() const
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.
if (mFormat == SurfaceFormat::B8G8R8A8 && !mHasNoAlpha) {
return true;
}
return false;
// We need a background painted if we have alpha or we're incomplete.
return (mFormat == SurfaceFormat::B8G8R8A8 || !ImageComplete());
}
uint32_t imgFrame::GetImageBytesPerRow() const
@ -665,16 +659,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());
@ -791,6 +775,13 @@ nsresult imgFrame::UnlockImageData()
return NS_OK;
}
void
imgFrame::SetDiscardable()
{
MOZ_ASSERT(mLockCount, "Expected to be locked when SetDiscardable is called");
mDiscardable = true;
}
void
imgFrame::SetOptimizable()
{

View File

@ -64,8 +64,9 @@ public:
* This is appropriate to use when drawing content into an imgFrame, as it
* uses the same graphics backend as normal content drawing. The downside is
* that the underlying surface may not be stored in a volatile buffer on all
* platforms, and raw access to the surface (using RawAccessRef()) may be much
* more expensive than in the InitForDecoder() case.
* platforms, and raw access to the surface (using RawAccessRef() or
* LockImageData()) may be much more expensive than in the InitForDecoder()
* case.
*/
nsresult InitWithDrawable(gfxDrawable* aDrawable,
const nsIntSize& aSize,
@ -96,7 +97,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);
@ -113,6 +113,10 @@ public:
bool GetCompositingFailed() const;
void SetCompositingFailed(bool val);
nsresult LockImageData();
nsresult UnlockImageData();
void SetDiscardable();
void SetOptimizable();
TemporaryRef<SourceSurface> GetSurface();
@ -147,8 +151,6 @@ private: // methods
~imgFrame();
nsresult LockImageData();
nsresult UnlockImageData();
nsresult Optimize();
struct SurfaceWithFormat {
@ -205,6 +207,7 @@ private: // data
bool mCompositingFailed;
bool mHasNoAlpha;
bool mNonPremult;
bool mDiscardable;
bool mOptimizable;
/** Have we called DiscardTracker::InformAllocation()? */

View File

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