Bug 962670 - Store decoded images in VolatileBuffers, r=seth,jrmuizel
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -21,16 +21,17 @@
#include "nsAutoRef.h" // for nsCountedRef
#include "nsCOMPtr.h" // for already_AddRefed
#include "nsDebug.h" // for NS_ASSERTION
#include "nsISupportsImpl.h" // for Image::Release, etc
#include "nsRect.h" // for nsIntRect
#include "nsSize.h" // for nsIntSize
#include "nsTArray.h" // for nsTArray
#include "mozilla/Atomics.h"
+#include "mozilla/WeakPtr.h"
#include "nsThreadUtils.h"
#include "mozilla/gfx/2D.h"
#include "nsDataHashtable.h"
#ifndef XPCOM_GLUE_AVOID_NSPR
/**
* We need to be able to hold a reference to a gfxASurface from Image
* subclasses. This is potentially a problem since Images can be addrefed
@@ -375,17 +376,17 @@ struct RemoteImageData {
* a layer transaction.
* 3) Remote. Initiated by calling SetRemoteImageData on the ImageContainer
* before any other activity.
* The ImageContainer uses a shared memory block containing a cross-process mutex
* to communicate with the compositor thread. SetCurrentImage synchronously
* updates the shared state to point to the new image and the old image
* is immediately released (not true in Normal or Asynchronous modes).
*/
-class ImageContainer {
+class ImageContainer : public SupportsWeakPtr<ImageContainer> {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
public:
enum { DISABLE_ASYNC = 0x0, ENABLE_ASYNC = 0x01 };
ImageContainer(int flag = 0);
~ImageContainer();
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -917,18 +917,34 @@ RasterImage::GetFrame(uint32_t aWhichFra
nsRefPtr<gfxASurface> framesurf;
// If this frame covers the entire image, we can just reuse its existing
// surface.
nsIntRect framerect = frame->GetRect();
if (framerect.x == 0 && framerect.y == 0 &&
framerect.width == mSize.width &&
- framerect.height == mSize.height)
+ framerect.height == mSize.height) {
frame->GetSurface(getter_AddRefs(framesurf));
+ if (!framesurf && !frame->IsSinglePixel()) {
+ // No reason to be optimized away here - the OS threw out the data
+ if (!(aFlags & FLAG_SYNC_DECODE))
+ return nullptr;
+
+ // Unconditionally call ForceDiscard() here because GetSurface can only
+ // return null when we can forcibly discard and redecode. There are two
+ // other cases where GetSurface() can return null - when it is a single
+ // pixel image, which we check before getting here, or when this is an
+ // indexed image, in which case we shouldn't be in this function at all.
+ // The only remaining possibility is that SetDiscardable() was called on
+ // this imgFrame, which implies the image can be redecoded.
+ ForceDiscard();
+ return GetFrame(aWhichFrame, aFlags);
+ }
+ }
// The image doesn't have a surface because it's been optimized away. Create
// one.
if (!framesurf) {
nsRefPtr<gfxImageSurface> imgsurf;
CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf));
framesurf = imgsurf;
}
@@ -942,17 +958,25 @@ RasterImage::GetCurrentImage()
if (!mDecoded) {
// We can't call StartDecoding because that can synchronously notify
// which can cause DOM modification
RequestDecodeCore(ASYNCHRONOUS);
return nullptr;
}
nsRefPtr<gfxASurface> imageSurface = GetFrame(FRAME_CURRENT, FLAG_NONE);
- NS_ENSURE_TRUE(imageSurface, nullptr);
+ if (!imageSurface) {
+ // The OS threw out some or all of our buffer. Start decoding again.
+ // GetFrame will only return null in the case that the image was
+ // discarded. We already checked that the image is decoded, so other
+ // error paths are not possible.
+ ForceDiscard();
+ RequestDecodeCore(ASYNCHRONOUS);
+ return nullptr;
+ }
if (!mImageContainer) {
mImageContainer = LayerManager::CreateImageContainer();
}
CairoImage::Data cairoData;
cairoData.mDeprecatedSurface = imageSurface;
GetWidth(&cairoData.mSize.width);
@@ -976,30 +1000,41 @@ RasterImage::GetImageContainer(LayerMana
*_retval = nullptr;
return NS_OK;
}
if (IsUnlocked() && mStatusTracker) {
mStatusTracker->OnUnlockedDraw();
}
+ if (!mImageContainer) {
+ mImageContainer = mImageContainerCache;
+ }
+
if (mImageContainer) {
*_retval = mImageContainer;
NS_ADDREF(*_retval);
return NS_OK;
}
nsRefPtr<layers::Image> image = GetCurrentImage();
if (!image) {
return NS_ERROR_NOT_AVAILABLE;
}
mImageContainer->SetCurrentImageInTransaction(image);
*_retval = mImageContainer;
NS_ADDREF(*_retval);
+ // We only need to be careful about holding on to the image when it is
+ // discardable by the OS.
+ if (CanForciblyDiscardAndRedecode()) {
+ mImageContainerCache = mImageContainer->asWeakPtr();
+ mImageContainer = nullptr;
+ }
+
return NS_OK;
}
void
RasterImage::UpdateImageContainer()
{
if (!mImageContainer || IsInUpdateImageContainer()) {
return;
@@ -1387,16 +1422,22 @@ RasterImage::DecodingComplete()
}
// If there's only 1 frame, optimize it. Optimizing animated images
// is not supported.
//
// We don't optimize the frame for multipart images because we reuse
// the frame.
if ((GetNumFrames() == 1) && !mMultipart) {
+ // CanForciblyDiscard is used instead of CanForciblyDiscardAndRedecode
+ // because we know decoding is complete at this point and this is not
+ // an animation
+ if (DiscardingEnabled() && CanForciblyDiscard()) {
+ mFrameBlender.RawGetFrame(0)->SetDiscardable();
+ }
rv = mFrameBlender.RawGetFrame(0)->Optimize();
NS_ENSURE_SUCCESS(rv, rv);
}
// 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 (GetNumFrames() == 1) {
@@ -2088,35 +2129,35 @@ RasterImage::ShutdownDecoder(eShutdownIn
"Invalid shutdown intent");
// Ensure that the decoder is initialized
NS_ABORT_IF_FALSE(mDecoder, "Calling ShutdownDecoder() with no active decoder!");
// Figure out what kind of decode we were doing before we get rid of our decoder
bool wasSizeDecode = mDecoder->IsSizeDecode();
- // 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) {
- imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
- curframe->UnlockImageData();
- }
-
// Finalize the decoder
// null out mDecoder, _then_ check for errors on the close (otherwise the
// error routine might re-invoke ShutdownDecoder)
nsRefPtr<Decoder> decoder = mDecoder;
mDecoder = nullptr;
mFinishing = true;
mInDecoder = true;
decoder->Finish(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) {
+ 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);
nsresult decoderStatus = decoder->GetDecoderError();
if (NS_FAILED(decoderStatus)) {
DoError();
return decoderStatus;
@@ -2672,16 +2713,27 @@ RasterImage::Draw(gfxContext *aContext,
uint32_t frameIndex = aWhichFrame == FRAME_FIRST ? 0
: GetCurrentImgFrameIndex();
imgFrame* frame = GetDrawableImgFrame(frameIndex);
if (!frame) {
return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
}
+ nsRefPtr<gfxASurface> surf;
+ if (!frame->IsSinglePixel()) {
+ frame->GetSurface(getter_AddRefs(surf));
+ if (!surf) {
+ // The OS threw out some or all of our buffer. Start decoding again.
+ ForceDiscard();
+ WantDecodedFrames();
+ return NS_OK;
+ }
+ }
+
DrawWithPreDownscaleIfNeeded(frame, aContext, aFilter, aUserSpaceToImageSpace, aFill, aSubimage, aFlags);
if (mDecoded && !mDrawStartTime.IsNull()) {
TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY, int32_t(drawLatency.ToMicroseconds()));
// clear the value of mDrawStartTime
mDrawStartTime = TimeStamp();
}
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -654,16 +654,19 @@ private: // data
nsIntSize mRequestedResolution;
// A hint for image decoder that directly scale the image to smaller buffer
int mRequestedSampleSize;
// Cached value for GetImageContainer.
nsRefPtr<mozilla::layers::ImageContainer> mImageContainer;
+ // If not cached in mImageContainer, this might have our image container
+ WeakPtr<mozilla::layers::ImageContainer> mImageContainerCache;
+
#ifdef DEBUG
uint32_t mFramesNotified;
#endif
// Below are the pieces of data that can be accessed on more than one thread
// at once, and hence need to be locked by mDecodingMonitor.
// BEGIN LOCKED MEMBER VARIABLES
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -7,16 +7,17 @@
#include "imgFrame.h"
#include "DiscardTracker.h"
#include "prenv.h"
#include "gfx2DGlue.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
+#include "gfxAlphaRecovery.h"
static bool gDisableOptimize = false;
#include "cairo.h"
#include "GeckoProfiler.h"
#include "mozilla/Likely.h"
#include "mozilla/MemoryReporting.h"
#include "nsMargin.h"
@@ -30,16 +31,58 @@ static bool gDisableOptimize = false;
#define USE_WIN_SURFACE 1
#endif
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::image;
+static cairo_user_data_key_t kVolatileBuffer;
+
+static void
+VolatileBufferRelease(void *vbuf)
+{
+ delete static_cast<VolatileBufferPtr<unsigned char>*>(vbuf);
+}
+
+gfxImageSurface *
+LockedImageSurface::CreateSurface(VolatileBuffer *vbuf,
+ const gfxIntSize& size,
+ gfxImageFormat format)
+{
+ VolatileBufferPtr<unsigned char> *vbufptr =
+ new VolatileBufferPtr<unsigned char>(vbuf);
+ MOZ_ASSERT(!vbufptr->WasBufferPurged(), "Expected image data!");
+
+ long stride = gfxImageSurface::ComputeStride(size, format);
+ gfxImageSurface *img = new gfxImageSurface(*vbufptr, size, stride, format);
+ if (!img || img->CairoStatus()) {
+ delete img;
+ delete vbufptr;
+ return nullptr;
+ }
+
+ img->SetData(&kVolatileBuffer, vbufptr, VolatileBufferRelease);
+ return img;
+}
+
+TemporaryRef<VolatileBuffer>
+LockedImageSurface::AllocateBuffer(const gfxIntSize& size,
+ gfxImageFormat format)
+{
+ long stride = gfxImageSurface::ComputeStride(size, format);
+ RefPtr<VolatileBuffer> buf = new VolatileBuffer();
+ if (buf->Init(stride * size.height,
+ 1 << gfxAlphaRecovery::GoodAlignmentLog2()))
+ return buf;
+
+ return nullptr;
+}
+
// Returns true if an image of aWidth x aHeight is allowed and legal.
static bool AllowedImageSize(int32_t aWidth, int32_t aHeight)
{
// reject over-wide or over-tall images
const int32_t k64KLimit = 0x0000FFFF;
if (MOZ_UNLIKELY(aWidth > k64KLimit || aHeight > k64KLimit )) {
NS_WARNING("image too big");
return false;
@@ -103,16 +146,17 @@ imgFrame::imgFrame() :
mTimeout(100),
mDisposalMethod(0), /* imgIContainer::kDisposeNotSpecified */
mLockCount(0),
mBlendMethod(1), /* imgIContainer::kBlendOver */
mSinglePixel(false),
mFormatChanged(false),
mCompositingFailed(false),
mNonPremult(false),
+ mDiscardable(false),
mInformedDiscardTracker(false),
mDirty(false)
{
static bool hasCheckedOptimize = false;
if (!hasCheckedOptimize) {
if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) {
gDisableOptimize = true;
}
@@ -169,22 +213,27 @@ nsresult imgFrame::Init(int32_t aX, int3
// no error
mImageSurface = mWinSurface->GetAsImageSurface();
} else {
mWinSurface = nullptr;
}
}
#endif
- // For other platforms we create the image surface first and then
- // possibly wrap it in a device surface. This branch is also used
- // on Windows if we're not using device surfaces or if we couldn't
- // create one.
- if (!mImageSurface)
- mImageSurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height), mFormat);
+ // For other platforms, space for the image surface is first allocated in
+ // a volatile buffer and then wrapped by a LockedImageSurface.
+ // This branch is also used on Windows if we're not using device surfaces
+ // or if we couldn't create one.
+ if (!mImageSurface) {
+ mVBuf = LockedImageSurface::AllocateBuffer(mSize, mFormat);
+ if (!mVBuf) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
+ }
if (!mImageSurface || mImageSurface->CairoStatus()) {
mImageSurface = nullptr;
// guess
if (!mImageSurface) {
NS_WARNING("Allocation of gfxImageSurface should succeed");
} else if (!mImageSurface->CairoStatus()) {
NS_WARNING("gfxImageSurface should have good CairoStatus");
@@ -246,16 +295,17 @@ nsresult imgFrame::Optimize()
if (mFormat == gfxImageFormat::ARGB32)
inputType = gfxRGBA::PACKED_ARGB_PREMULTIPLIED;
mSinglePixelColor = gfxRGBA(firstPixel, inputType);
mSinglePixel = true;
// blow away the older surfaces (if they exist), to release their memory
+ mVBuf = nullptr;
mImageSurface = nullptr;
mOptSurface = nullptr;
#ifdef USE_WIN_SURFACE
mWinSurface = nullptr;
#endif
#ifdef XP_MACOSX
mQuartzSurface = nullptr;
#endif
@@ -295,16 +345,17 @@ nsresult imgFrame::Optimize()
mQuartzSurface->Flush();
}
#endif
if (mOptSurface == nullptr)
mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat);
if (mOptSurface) {
+ mVBuf = nullptr;
mImageSurface = nullptr;
#ifdef USE_WIN_SURFACE
mWinSurface = nullptr;
#endif
#ifdef XP_MACOSX
mQuartzSurface = nullptr;
#endif
}
@@ -478,20 +529,23 @@ bool imgFrame::GetNeedsBackground() cons
return (mFormat == gfxImageFormat::ARGB32 || !ImageComplete());
}
uint32_t imgFrame::GetImageBytesPerRow() const
{
if (mImageSurface)
return mImageSurface->Stride();
+ if (mVBuf)
+ return gfxImageSurface::ComputeStride(mSize, mFormat);
+
if (mPaletteDepth)
return mSize.width;
- NS_ERROR("GetImageBytesPerRow called with mImageSurface == null and mPaletteDepth == 0");
+ NS_ERROR("GetImageBytesPerRow called with mImageSurface == null, mVBuf == null and mPaletteDepth == 0");
return 0;
}
uint32_t imgFrame::GetImageDataLength() const
{
return GetImageBytesPerRow() * mSize.height;
}
@@ -564,50 +618,70 @@ nsresult imgFrame::LockImageData()
if (mLockCount != 1) {
return NS_OK;
}
// Paletted images don't have surfaces, so there's nothing to do.
if (mPalettedImageData)
return NS_OK;
- if ((mOptSurface || mSinglePixel) && !mImageSurface) {
- // Recover the pixels
- mImageSurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height),
- gfxImageFormat::ARGB32);
- if (!mImageSurface || mImageSurface->CairoStatus())
- return NS_ERROR_OUT_OF_MEMORY;
+ if (!mImageSurface) {
+ if (mVBuf) {
+ VolatileBufferPtr<uint8_t> ref(mVBuf);
+ if (ref.WasBufferPurged())
+ return NS_ERROR_FAILURE;
+
+ mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
+ if (!mImageSurface || mImageSurface->CairoStatus())
+ return NS_ERROR_OUT_OF_MEMORY;
+ } else if (mOptSurface || mSinglePixel) {
+ // Recover the pixels
+ mVBuf = LockedImageSurface::AllocateBuffer(mSize, mFormat);
+ if (!mVBuf) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
- gfxContext context(mImageSurface);
- context.SetOperator(gfxContext::OPERATOR_SOURCE);
- if (mSinglePixel)
- context.SetDeviceColor(mSinglePixelColor);
- else
- context.SetSource(mOptSurface);
- context.Paint();
+ mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
+ if (!mImageSurface || mImageSurface->CairoStatus())
+ return NS_ERROR_OUT_OF_MEMORY;
- mOptSurface = nullptr;
+ gfxContext context(mImageSurface);
+ context.SetOperator(gfxContext::OPERATOR_SOURCE);
+ if (mSinglePixel)
+ context.SetDeviceColor(mSinglePixelColor);
+ else
+ context.SetSource(mOptSurface);
+ context.Paint();
+
+ mOptSurface = nullptr;
#ifdef USE_WIN_SURFACE
- mWinSurface = nullptr;
+ mWinSurface = nullptr;
#endif
#ifdef XP_MACOSX
- mQuartzSurface = nullptr;
+ mQuartzSurface = nullptr;
#endif
+ }
}
// We might write to the bits in this image surface, so we need to make the
// surface ready for that.
if (mImageSurface)
mImageSurface->Flush();
#ifdef USE_WIN_SURFACE
if (mWinSurface)
mWinSurface->Flush();
#endif
+#ifdef XP_MACOSX
+ if (!mQuartzSurface && !ShouldUseImageSurfaces()) {
+ mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
+ }
+#endif
+
return NS_OK;
}
nsresult imgFrame::UnlockImageData()
{
MOZ_ASSERT(NS_IsMainThread());
NS_ABORT_IF_FALSE(mLockCount != 0, "Unlocking an unlocked image!");
@@ -653,16 +727,23 @@ nsresult imgFrame::UnlockImageData()
#ifdef XP_MACOSX
// The quartz image surface (ab)uses the flush method to get the
// cairo_image_surface data into a CGImage, so we have to call Flush() here.
if (mQuartzSurface)
mQuartzSurface->Flush();
#endif
+ if (mVBuf && mDiscardable) {
+ mImageSurface = nullptr;
+#ifdef XP_MACOSX
+ mQuartzSurface = nullptr;
+#endif
+ }
+
return NS_OK;
}
void imgFrame::ApplyDirtToSurfaces()
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mDirtyMutex);
@@ -692,16 +773,22 @@ void imgFrame::ApplyDirtToSurfaces()
if (mQuartzSurface)
mQuartzSurface->Flush();
#endif
mDirty = false;
}
}
+void imgFrame::SetDiscardable()
+{
+ MOZ_ASSERT(mLockCount, "Expected to be locked when SetDiscardable is called");
+ mDiscardable = true;
+}
+
int32_t imgFrame::GetRawTimeout() const
{
return mTimeout;
}
void imgFrame::SetRawTimeout(int32_t aTimeout)
{
mTimeout = aTimeout;
@@ -790,30 +877,35 @@ imgFrame::SizeOfExcludingThisWithCompute
#ifdef USE_WIN_SURFACE
if (mWinSurface && aLocation == mWinSurface->GetMemoryLocation()) {
n += mWinSurface->KnownMemoryUsed();
} else
#endif
#ifdef XP_MACOSX
if (mQuartzSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
- n += mSize.width * mSize.height * 4;
- } else
+ n += aMallocSizeOf(mQuartzSurface);
+ }
#endif
if (mImageSurface && aLocation == mImageSurface->GetMemoryLocation()) {
size_t n2 = 0;
if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) { // HEAP: measure
n2 = mImageSurface->SizeOfIncludingThis(aMallocSizeOf);
}
if (n2 == 0) { // non-HEAP or computed fallback for HEAP
n2 = mImageSurface->KnownMemoryUsed();
}
n += n2;
}
+ if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
+ n += aMallocSizeOf(mVBuf);
+ n += mVBuf->HeapSizeOfExcludingThis(aMallocSizeOf);
+ }
+
if (mOptSurface && aLocation == mOptSurface->GetMemoryLocation()) {
size_t n2 = 0;
if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP &&
mOptSurface->SizeOfIsMeasured()) {
// HEAP: measure (but only if the sub-class is capable of measuring)
n2 = mOptSurface->SizeOfIncludingThis(aMallocSizeOf);
}
if (n2 == 0) { // non-HEAP or computed fallback for HEAP
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -4,31 +4,47 @@
* 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 imgFrame_h
#define imgFrame_h
#include "mozilla/MemoryReporting.h"
#include "mozilla/Mutex.h"
+#include "mozilla/VolatileBuffer.h"
#include "nsRect.h"
#include "nsPoint.h"
#include "nsSize.h"
#include "gfxPattern.h"
#include "gfxDrawable.h"
#include "gfxImageSurface.h"
#if defined(XP_WIN)
#include "gfxWindowsSurface.h"
#elif defined(XP_MACOSX)
#include "gfxQuartzImageSurface.h"
#endif
#include "nsAutoPtr.h"
#include "imgIContainer.h"
#include "gfxColor.h"
+/*
+ * This creates a gfxImageSurface which will unlock the buffer on destruction
+ */
+
+class LockedImageSurface
+{
+public:
+ static gfxImageSurface *
+ CreateSurface(mozilla::VolatileBuffer *vbuf,
+ const gfxIntSize& size,
+ gfxImageFormat format);
+ static mozilla::TemporaryRef<mozilla::VolatileBuffer>
+ AllocateBuffer(const gfxIntSize& size, gfxImageFormat format);
+};
+
class imgFrame
{
public:
imgFrame();
~imgFrame();
nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, gfxImageFormat aFormat, uint8_t aPaletteDepth = 0);
nsresult Optimize();
@@ -67,45 +83,77 @@ public:
bool GetCompositingFailed() const;
void SetCompositingFailed(bool val);
nsresult LockImageData();
nsresult UnlockImageData();
void ApplyDirtToSurfaces();
- nsresult GetSurface(gfxASurface **aSurface) const
+ void SetDiscardable();
+
+ nsresult GetSurface(gfxASurface **aSurface)
{
*aSurface = ThebesSurface();
NS_IF_ADDREF(*aSurface);
return NS_OK;
}
- nsresult GetPattern(gfxPattern **aPattern) const
+ nsresult GetPattern(gfxPattern **aPattern)
{
if (mSinglePixel)
*aPattern = new gfxPattern(mSinglePixelColor);
else
*aPattern = new gfxPattern(ThebesSurface());
NS_ADDREF(*aPattern);
return NS_OK;
}
- gfxASurface* ThebesSurface() const
+ bool IsSinglePixel()
+ {
+ return mSinglePixel;
+ }
+
+ gfxASurface* ThebesSurface()
{
if (mOptSurface)
return mOptSurface;
#if defined(XP_WIN)
if (mWinSurface)
return mWinSurface;
#elif defined(XP_MACOSX)
if (mQuartzSurface)
return mQuartzSurface;
#endif
- return mImageSurface;
+ if (mImageSurface)
+ return mImageSurface;
+ if (mVBuf) {
+ mozilla::VolatileBufferPtr<uint8_t> ref(mVBuf);
+ if (ref.WasBufferPurged())
+ return nullptr;
+
+ gfxImageSurface *sur =
+ LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
+#if defined(XP_MACOSX)
+ // Manually addref and release to make sure the cairo surface isn't lost
+ NS_ADDREF(sur);
+ gfxQuartzImageSurface *quartzSur = new gfxQuartzImageSurface(sur);
+ // quartzSur does not hold on to the gfxImageSurface
+ NS_RELEASE(sur);
+ return quartzSur;
+#else
+ return sur;
+#endif
+ }
+ // We can return null here if we're single pixel optimized
+ // or a paletted image. However, one has to check for paletted
+ // image data first before attempting to get a surface, so
+ // this is only valid for single pixel optimized images
+ MOZ_ASSERT(mSinglePixel, "No image surface and not a single pixel!");
+ return nullptr;
}
size_t SizeOfExcludingThisWithComputedFallbackIfHeap(
gfxMemoryLocation aLocation,
mozilla::MallocSizeOf aMallocSizeOf) const;
uint8_t GetPaletteDepth() const { return mPaletteDepth; }
uint32_t PaletteDataLength() const {
@@ -162,23 +210,26 @@ private: // data
gfxRGBA mSinglePixelColor;
int32_t mTimeout; // -1 means display forever
int32_t mDisposalMethod;
/** Indicates how many readers currently have locked this frame */
int32_t mLockCount;
+ mozilla::RefPtr<mozilla::VolatileBuffer> mVBuf;
+
gfxImageFormat mFormat;
uint8_t mPaletteDepth;
int8_t mBlendMethod;
bool mSinglePixel;
bool mFormatChanged;
bool mCompositingFailed;
bool mNonPremult;
+ bool mDiscardable;
/** Have we called DiscardTracker::InformAllocation()? */
bool mInformedDiscardTracker;
bool mDirty;
};
namespace mozilla {