mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Back out bug 1057904, bug 1060869, bug 923302 for bustage
--HG-- extra : rebase_source : fe24318671f3fd9cccb822c65a9581f09612d8c6
This commit is contained in:
parent
8cf690729c
commit
66dd048dda
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
103
image/src/FrameSequence.cpp
Normal 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
206
image/src/FrameSequence.h
Normal 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_ */
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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()? */
|
||||
|
@ -21,6 +21,7 @@ UNIFIED_SOURCES += [
|
||||
'DynamicImage.cpp',
|
||||
'FrameAnimator.cpp',
|
||||
'FrameBlender.cpp',
|
||||
'FrameSequence.cpp',
|
||||
'FrozenImage.cpp',
|
||||
'Image.cpp',
|
||||
'ImageFactory.cpp',
|
||||
|
Loading…
Reference in New Issue
Block a user