--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -307,22 +307,24 @@ Decoder::EnsureFrame(uint32_t aFrameNum,
return RawAccessFrameRef();
}
MOZ_ASSERT(!aPreviousFrame->GetRect().IsEqualEdges(aFrameRect) ||
aPreviousFrame->GetFormat() != aFormat ||
aPreviousFrame->GetPaletteDepth() != aPaletteDepth,
"Replacing first frame with the same kind of frame?");
- // Remove the old frame from the SurfaceCache.
+ // Remove the old frame from the SurfaceCache and release our reference to it.
IntSize prevFrameSize = aPreviousFrame->GetImageSize();
SurfaceCache::RemoveSurface(ImageKey(&mImage),
RasterSurfaceKey(prevFrameSize, aDecodeFlags, 0));
mFrameCount = 0;
mInFrame = false;
+ mCurrentFrame->Abort();
+ mCurrentFrame = RawAccessFrameRef();
// Add the new frame as usual.
return InternalAddFrame(aFrameNum, aFrameRect, aDecodeFlags, aFormat,
aPaletteDepth, nullptr);
}
RawAccessFrameRef
Decoder::InternalAddFrame(uint32_t aFrameNum,
@@ -356,26 +358,28 @@ Decoder::InternalAddFrame(uint32_t aFram
if (NS_FAILED(frame->InitForDecoder(imageSize, aFrameRect, aFormat,
aPaletteDepth, nonPremult))) {
NS_WARNING("imgFrame::Init should succeed");
return RawAccessFrameRef();
}
RawAccessFrameRef ref = frame->RawAccessRef();
if (!ref) {
+ frame->Abort();
return RawAccessFrameRef();
}
bool succeeded =
SurfaceCache::Insert(frame, ImageKey(&mImage),
RasterSurfaceKey(imageSize.ToIntSize(),
aDecodeFlags,
aFrameNum),
Lifetime::Persistent);
if (!succeeded) {
+ ref->Abort();
return RawAccessFrameRef();
}
nsIntRect refreshArea;
if (aFrameNum == 1) {
MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
aPreviousFrame->SetRawAccessOnly();
@@ -509,28 +513,36 @@ Decoder::PostDecodeDone(int32_t aLoopCou
mProgress |= FLAG_DECODE_COMPLETE;
}
void
Decoder::PostDataError()
{
mDataError = true;
+
+ if (mInFrame && mCurrentFrame) {
+ mCurrentFrame->Abort();
+ }
}
void
Decoder::PostDecoderError(nsresult aFailureCode)
{
NS_ABORT_IF_FALSE(NS_FAILED(aFailureCode), "Not a failure code!");
mFailCode = aFailureCode;
// XXXbholley - we should report the image URI here, but imgContainer
// needs to know its URI first
NS_WARNING("Image decoding error - This is probably a bug!");
+
+ if (mInFrame && mCurrentFrame) {
+ mCurrentFrame->Abort();
+ }
}
void
Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset,
uint32_t width, uint32_t height,
gfx::SurfaceFormat format,
uint8_t palette_depth /* = 0 */)
{
--- a/image/src/FrameAnimator.cpp
+++ b/image/src/FrameAnimator.cpp
@@ -84,17 +84,18 @@ FrameAnimator::AdvanceFrame(TimeStamp aT
int32_t timeout = 0;
RefreshResult ret;
RawAccessFrameRef nextFrame = GetRawFrame(nextFrameIndex);
// If we're done decoding, we know we've got everything we're going to get.
// If we aren't, we only display fully-downloaded frames; everything else
// gets delayed.
- bool canDisplay = mDoneDecoding || (nextFrame && nextFrame->ImageComplete());
+ bool canDisplay = mDoneDecoding ||
+ (nextFrame && nextFrame->IsImageComplete());
if (!canDisplay) {
// Uh oh, the frame we want to show is currently being decoded (partial)
// Wait until the next refresh driver tick and try again
return ret;
}
// If we're done decoding the next frame, go ahead and display it now and
@@ -590,32 +591,30 @@ FrameAnimator::DoBlend(nsIntRect* aDirty
AnimationData compositingPrevFrameData =
mCompositingPrevFrame->GetAnimationData();
CopyFrameImage(compositingFrameData.mRawData,
compositingFrameData.mRect,
compositingPrevFrameData.mRawData,
compositingPrevFrameData.mRect);
+
+ mCompositingPrevFrame->Finish();
}
// blit next frame into it's correct spot
DrawFrameTo(nextFrameData.mRawData, nextFrameData.mRect,
nextFrameData.mPaletteDataLength,
nextFrameData.mHasAlpha,
compositingFrameData.mRawData,
compositingFrameData.mRect,
nextFrameData.mBlendMethod);
// Tell the image that it is fully 'downloaded'.
- nsresult rv =
- mCompositingFrame->ImageUpdated(compositingFrameData.mRect);
- if (NS_FAILED(rv)) {
- return false;
- }
+ mCompositingFrame->Finish();
mLastCompositedFrameIndex = int32_t(aNextFrameIndex);
return true;
}
//******************************************************************************
// Fill aFrame with black. Does also clears the mask.
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -199,17 +199,17 @@ public:
bool succeeded =
gfx::Scale(srcData.mRawData, srcData.mSize.width, srcData.mSize.height,
srcData.mBytesPerRow, dstData.mRawData, mDstSize.width,
mDstSize.height, dstData.mBytesPerRow, srcData.mFormat);
if (succeeded) {
// Mark the frame as complete and discardable.
mDstRef->ImageUpdated(mDstRef->GetRect());
- MOZ_ASSERT(mDstRef->ImageComplete(),
+ MOZ_ASSERT(mDstRef->IsImageComplete(),
"Incomplete, but just updated the entire frame");
}
// We need to send notifications and release our references on the main
// thread, so finish up there.
mState = succeeded ? eFinish : eFinishWithError;
NS_DispatchToMainThread(this);
} else if (mState == eFinish) {
@@ -546,31 +546,40 @@ RasterImage::LookupFrame(uint32_t aFrame
MOZ_ASSERT(!mAnim, "Animated frames should be locked");
// Update our state so the decoder knows what to do.
mFrameDecodeFlags = aFlags & DECODE_FLAGS_MASK;
mDecoded = false;
mFrameCount = 0;
WantDecodedFrames(aFlags, aShouldSyncNotify);
- // See if we managed to redecode enough to get the frame we want.
+ // If we were able to sync decode, we should already have the frame. If we
+ // had to decode asynchronously, maybe we've gotten lucky.
ref = LookupFrameInternal(aFrameNum, aSize, aFlags);
if (!ref) {
// We didn't successfully redecode, so just fail.
return DrawableFrameRef();
}
}
if (ref->GetCompositingFailed()) {
return DrawableFrameRef();
}
MOZ_ASSERT(!ref || !ref->GetIsPaletted(), "Should not have paletted frame");
+ // Sync decoding guarantees that we got the frame, but if it's owned by an
+ // async decoder that's currently running, the contents of the frame may not
+ // be available yet. Make sure we get everything.
+ if (ref && mHasSourceData && aShouldSyncNotify &&
+ (aFlags & FLAG_SYNC_DECODE)) {
+ ref->WaitUntilComplete();
+ }
+
return ref;
}
uint32_t
RasterImage::GetCurrentFrameIndex() const
{
if (mAnim) {
return mAnim->GetCurrentAnimationFrameIndex();
@@ -1906,17 +1915,17 @@ RasterImage::NotifyNewScaledFrame()
}
void
RasterImage::RequestScale(imgFrame* aFrame,
uint32_t aFlags,
const nsIntSize& aSize)
{
// We don't scale frames which aren't fully decoded.
- if (!aFrame->ImageComplete()) {
+ if (!aFrame->IsImageComplete()) {
return;
}
// We can't scale frames that need padding or are single pixel.
if (aFrame->NeedsPadding() || aFrame->IsSinglePixel()) {
return;
}
@@ -1955,17 +1964,17 @@ RasterImage::DrawWithPreDownscaleIfNeede
DecodeFlags(aFlags),
0));
if (!frameRef) {
// We either didn't have a matching scaled frame or the OS threw it away.
// Request a new one so we'll be ready next time. For now, we'll fall back
// to aFrameRef below.
RequestScale(aFrameRef.get(), aFlags, aSize);
}
- if (frameRef && !frameRef->ImageComplete()) {
+ if (frameRef && !frameRef->IsImageComplete()) {
frameRef.reset(); // We're still scaling, so we can't use this yet.
}
}
gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
ImageRegion region(aRegion);
if (!frameRef) {
frameRef = Move(aFrameRef);
@@ -2462,17 +2471,17 @@ RasterImage::OptimalImageSizeForDest(con
if (CanScale(aFilter, destSize, aFlags)) {
DrawableFrameRef frameRef =
SurfaceCache::Lookup(ImageKey(this),
RasterSurfaceKey(destSize.ToIntSize(),
DecodeFlags(aFlags),
0));
- if (frameRef && frameRef->ImageComplete()) {
+ if (frameRef && frameRef->IsImageComplete()) {
return destSize; // We have an existing HQ scale for this size.
}
if (!frameRef) {
// We could HQ scale to this size, but we haven't. Request a scale now.
frameRef = LookupFrame(GetRequestedFrameIndex(aWhichFrame),
mSize, aFlags);
if (frameRef) {
RequestScale(frameRef.get(), aFlags, destSize);
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -125,23 +125,24 @@ static bool AllowedImageAndFrameDimensio
if (!imageRect.Contains(aFrameRect)) {
return false;
}
return true;
}
imgFrame::imgFrame()
- : mMutex("imgFrame")
+ : mMonitor("imgFrame")
, mDecoded(0, 0, 0, 0)
, mLockCount(0)
, mTimeout(100)
, mDisposalMethod(DisposalMethod::NOT_SPECIFIED)
, mBlendMethod(BlendMethod::OVER)
, mHasNoAlpha(false)
+ , mAborted(false)
, mPalettedImageData(nullptr)
, mPaletteDepth(0)
, mNonPremult(false)
, mSinglePixel(false)
, mCompositingFailed(false)
, mOptimizable(false)
{
static bool hasCheckedOptimize = false;
@@ -150,74 +151,83 @@ imgFrame::imgFrame()
gDisableOptimize = true;
}
hasCheckedOptimize = true;
}
}
imgFrame::~imgFrame()
{
+#ifdef DEBUG
+ MonitorAutoLock lock(mMonitor);
+ MOZ_ASSERT(mAborted || IsImageCompleteInternal());
+#endif
+
moz_free(mPalettedImageData);
mPalettedImageData = nullptr;
}
nsresult
imgFrame::InitForDecoder(const nsIntSize& aImageSize,
const nsIntRect& aRect,
SurfaceFormat aFormat,
uint8_t aPaletteDepth /* = 0 */,
bool aNonPremult /* = false */)
{
// Assert for properties that should be verified by decoders,
// warn for properties related to bad content.
if (!AllowedImageAndFrameDimensions(aImageSize, aRect)) {
NS_WARNING("Should have legal image size");
+ mAborted = true;
return NS_ERROR_FAILURE;
}
mImageSize = aImageSize.ToIntSize();
mOffset.MoveTo(aRect.x, aRect.y);
mSize.SizeTo(aRect.width, aRect.height);
mFormat = aFormat;
mPaletteDepth = aPaletteDepth;
mNonPremult = aNonPremult;
if (aPaletteDepth != 0) {
// We're creating for a paletted image.
if (aPaletteDepth > 8) {
NS_WARNING("Should have legal palette depth");
NS_ERROR("This Depth is not supported");
+ mAborted = true;
return NS_ERROR_FAILURE;
}
// Use the fallible allocator here. Paletted images always use 1 byte per
// pixel, so calculating the amount of memory we need is straightforward.
mPalettedImageData =
static_cast<uint8_t*>(moz_malloc(PaletteDataLength() +
(mSize.width * mSize.height)));
if (!mPalettedImageData)
NS_WARNING("moz_malloc for paletted image data should succeed");
NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
} else {
MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitForDecoder() twice?");
mVBuf = AllocateBufferForImage(mSize, mFormat);
if (!mVBuf) {
+ mAborted = true;
return NS_ERROR_OUT_OF_MEMORY;
}
if (mVBuf->OnHeap()) {
int32_t stride = VolatileSurfaceStride(mSize, mFormat);
VolatileBufferPtr<uint8_t> ptr(mVBuf);
memset(ptr, 0, stride * mSize.height);
}
mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
if (!mImageSurface) {
NS_WARNING("Failed to create VolatileDataSourceSurface");
+ mAborted = true;
return NS_ERROR_OUT_OF_MEMORY;
}
}
return NS_OK;
}
nsresult
@@ -226,16 +236,17 @@ imgFrame::InitWithDrawable(gfxDrawable*
const SurfaceFormat aFormat,
GraphicsFilter aFilter,
uint32_t aImageFlags)
{
// Assert for properties that should be verified by decoders,
// warn for properties related to bad content.
if (!AllowedImageSize(aSize.width, aSize.height)) {
NS_WARNING("Should have legal image size");
+ mAborted = true;
return NS_ERROR_FAILURE;
}
mImageSize = aSize.ToIntSize();
mOffset.MoveTo(0, 0);
mSize.SizeTo(aSize.width, aSize.height);
mFormat = aFormat;
@@ -248,22 +259,24 @@ imgFrame::InitWithDrawable(gfxDrawable*
if (canUseDataSurface) {
// It's safe to use data surfaces for content on this platform, so we can
// get away with using volatile buffers.
MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitWithDrawable() twice?");
mVBuf = AllocateBufferForImage(mSize, mFormat);
if (!mVBuf) {
+ mAborted = true;
return NS_ERROR_OUT_OF_MEMORY;
}
int32_t stride = VolatileSurfaceStride(mSize, mFormat);
VolatileBufferPtr<uint8_t> ptr(mVBuf);
if (!ptr) {
+ mAborted = true;
return NS_ERROR_OUT_OF_MEMORY;
}
if (mVBuf->OnHeap()) {
memset(ptr, 0, stride * mSize.height);
}
mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
target = gfxPlatform::GetPlatform()->
@@ -275,44 +288,50 @@ imgFrame::InitWithDrawable(gfxDrawable*
// the documentation for this method.
MOZ_ASSERT(!mOptSurface, "Called imgFrame::InitWithDrawable() twice?");
target = gfxPlatform::GetPlatform()->
CreateOffscreenContentDrawTarget(mSize, mFormat);
}
if (!target) {
+ mAborted = true;
return NS_ERROR_OUT_OF_MEMORY;
}
// Draw using the drawable the caller provided.
nsIntRect imageRect(0, 0, mSize.width, mSize.height);
nsRefPtr<gfxContext> ctx = new gfxContext(target);
gfxUtils::DrawPixelSnapped(ctx, aDrawable, ThebesIntSize(mSize),
ImageRegion::Create(imageRect),
mFormat, aFilter, aImageFlags);
if (canUseDataSurface && !mImageSurface) {
NS_WARNING("Failed to create VolatileDataSourceSurface");
+ mAborted = true;
return NS_ERROR_OUT_OF_MEMORY;
}
if (!canUseDataSurface) {
// We used an offscreen surface, which is an "optimized" surface from
// imgFrame's perspective.
mOptSurface = target->Snapshot();
}
+ // If we reach this point, we should regard ourselves as complete.
+ mDecoded = GetRect();
+ MOZ_ASSERT(IsImageComplete());
+
return NS_OK;
}
nsresult imgFrame::Optimize()
{
MOZ_ASSERT(NS_IsMainThread());
- mMutex.AssertCurrentThreadOwns();
+ mMonitor.AssertCurrentThreadOwns();
MOZ_ASSERT(mLockCount == 1,
"Should only optimize when holding the lock exclusively");
// Don't optimize during shutdown because gfxPlatform may not be available.
if (ShutdownTracker::ShutdownHasStarted())
return NS_OK;
if (!mOptimizable || gDisableOptimize)
@@ -450,17 +469,17 @@ imgFrame::SurfaceForDrawing(bool
bool aDoTile,
gfxContext* aContext,
const nsIntMargin& aPadding,
gfxRect& aImageRect,
ImageRegion& aRegion,
SourceSurface* aSurface)
{
MOZ_ASSERT(NS_IsMainThread());
- mMutex.AssertCurrentThreadOwns();
+ mMonitor.AssertCurrentThreadOwns();
IntSize size(int32_t(aImageRect.Width()), int32_t(aImageRect.Height()));
if (!aDoPadding && !aDoPartialDecode) {
NS_ASSERTION(!mSinglePixel, "This should already have been handled");
return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, ThebesIntSize(size)), mFormat);
}
gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height);
@@ -511,25 +530,25 @@ bool imgFrame::Draw(gfxContext* aContext
MOZ_ASSERT(NS_IsMainThread());
NS_ASSERTION(!aRegion.Rect().IsEmpty(), "Drawing empty region!");
NS_ASSERTION(!aRegion.IsRestricted() ||
!aRegion.Rect().Intersect(aRegion.Restriction()).IsEmpty(),
"We must be allowed to sample *some* source pixels!");
NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!");
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
nsIntMargin padding(mOffset.y,
mImageSize.width - (mOffset.x + mSize.width),
mImageSize.height - (mOffset.y + mSize.height),
mOffset.x);
bool doPadding = padding != nsIntMargin(0,0,0,0);
- bool doPartialDecode = !ImageCompleteInternal();
+ bool doPartialDecode = !IsImageCompleteInternal();
if (mSinglePixel && !doPadding && !doPartialDecode) {
if (mSinglePixelColor.a == 0.0) {
return true;
}
RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
dt->FillRect(ToRect(aRegion.Rect()),
ColorPattern(mSinglePixelColor),
@@ -564,40 +583,47 @@ bool imgFrame::Draw(gfxContext* aContext
aFilter, aImageFlags);
}
return true;
}
nsresult
imgFrame::ImageUpdated(const nsIntRect& aUpdateRect)
{
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
return ImageUpdatedInternal(aUpdateRect);
}
nsresult
imgFrame::ImageUpdatedInternal(const nsIntRect& aUpdateRect)
{
- mMutex.AssertCurrentThreadOwns();
+ mMonitor.AssertCurrentThreadOwns();
mDecoded.UnionRect(mDecoded, aUpdateRect);
// clamp to bounds, in case someone sends a bogus updateRect (I'm looking at
// you, gif decoder)
nsIntRect boundsRect(mOffset, nsIntSize(mSize.width, mSize.height));
mDecoded.IntersectRect(mDecoded, boundsRect);
+ // If the image is now complete, wake up anyone who's waiting.
+ if (IsImageCompleteInternal()) {
+ mMonitor.NotifyAll();
+ }
+
return NS_OK;
}
void
-imgFrame::Finish(Opacity aFrameOpacity, DisposalMethod aDisposalMethod,
- int32_t aRawTimeout, BlendMethod aBlendMethod)
+imgFrame::Finish(Opacity aFrameOpacity /* = Opacity::SOME_TRANSPARENCY */,
+ DisposalMethod aDisposalMethod /* = DisposalMethod::KEEP */,
+ int32_t aRawTimeout /* = 0 */,
+ BlendMethod aBlendMethod /* = BlendMethod::OVER */)
{
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
if (aFrameOpacity == Opacity::OPAQUE) {
mHasNoAlpha = true;
}
mDisposalMethod = aDisposalMethod;
mTimeout = aRawTimeout;
@@ -608,34 +634,34 @@ imgFrame::Finish(Opacity aFrameOpacity,
nsIntRect imgFrame::GetRect() const
{
return nsIntRect(mOffset, nsIntSize(mSize.width, mSize.height));
}
int32_t
imgFrame::GetStride() const
{
- mMutex.AssertCurrentThreadOwns();
+ mMonitor.AssertCurrentThreadOwns();
if (mImageSurface) {
return mImageSurface->Stride();
}
return VolatileSurfaceStride(mSize, mFormat);
}
SurfaceFormat imgFrame::GetFormat() const
{
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
return mFormat;
}
uint32_t imgFrame::GetImageBytesPerRow() const
{
- mMutex.AssertCurrentThreadOwns();
+ mMonitor.AssertCurrentThreadOwns();
if (mVBuf)
return mSize.width * BytesPerPixel(mFormat);
if (mPaletteDepth)
return mSize.width;
return 0;
@@ -644,24 +670,24 @@ uint32_t imgFrame::GetImageBytesPerRow()
uint32_t imgFrame::GetImageDataLength() const
{
return GetImageBytesPerRow() * mSize.height;
}
void
imgFrame::GetImageData(uint8_t** aData, uint32_t* aLength) const
{
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
GetImageDataInternal(aData, aLength);
}
void
imgFrame::GetImageDataInternal(uint8_t** aData, uint32_t* aLength) const
{
- mMutex.AssertCurrentThreadOwns();
+ mMonitor.AssertCurrentThreadOwns();
MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
if (mImageSurface) {
*aData = mVBufPtr;
MOZ_ASSERT(*aData, "mImageSurface is non-null, but mVBufPtr is null in GetImageData");
} else if (mPalettedImageData) {
*aData = mPalettedImageData + PaletteDataLength();
MOZ_ASSERT(*aData, "mPalettedImageData is non-null, but result is null in GetImageData");
@@ -705,17 +731,17 @@ uint32_t* imgFrame::GetPaletteData() con
uint32_t length;
GetPaletteData(&data, &length);
return data;
}
nsresult
imgFrame::LockImageData()
{
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
MOZ_ASSERT(mLockCount >= 0, "Unbalanced locks and unlocks");
if (mLockCount < 0) {
return NS_ERROR_FAILURE;
}
mLockCount++;
@@ -737,17 +763,17 @@ imgFrame::LockImageData()
return Deoptimize();
}
nsresult
imgFrame::Deoptimize()
{
MOZ_ASSERT(NS_IsMainThread());
- mMutex.AssertCurrentThreadOwns();
+ mMonitor.AssertCurrentThreadOwns();
MOZ_ASSERT(!mImageSurface);
if (!mImageSurface) {
if (mVBuf) {
VolatileBufferPtr<uint8_t> ref(mVBuf);
if (ref.WasBufferPurged())
return NS_ERROR_FAILURE;
@@ -806,17 +832,17 @@ imgFrame::Deoptimize()
mVBufPtr = mVBuf;
return NS_OK;
}
void
imgFrame::AssertImageDataLocked() const
{
#ifdef DEBUG
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
#endif
}
class UnlockImageDataRunnable : public nsRunnable
{
public:
explicit UnlockImageDataRunnable(imgFrame* aTarget)
@@ -829,23 +855,26 @@ public:
private:
nsRefPtr<imgFrame> mTarget;
};
nsresult
imgFrame::UnlockImageData()
{
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
MOZ_ASSERT(mLockCount > 0, "Unlocking an unlocked image!");
if (mLockCount <= 0) {
return NS_ERROR_FAILURE;
}
+ MOZ_ASSERT(mLockCount > 1 || IsImageCompleteInternal() || mAborted,
+ "Should have marked complete or aborted before unlocking");
+
// If we're about to become unlocked, we don't need to hold on to our data
// surface anymore. (But we don't need to do anything for paletted images,
// which don't have surfaces.)
if (mLockCount == 1 && !mPalettedImageData) {
// We can't safely optimize off-main-thread, so create a runnable to do it.
if (!NS_IsMainThread()) {
nsCOMPtr<nsIRunnable> runnable = new UnlockImageDataRunnable(this);
NS_DispatchToMainThread(runnable);
@@ -893,24 +922,24 @@ imgFrame::IsSinglePixel() const
{
MOZ_ASSERT(NS_IsMainThread());
return mSinglePixel;
}
TemporaryRef<SourceSurface>
imgFrame::GetSurface()
{
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
return GetSurfaceInternal();
}
TemporaryRef<SourceSurface>
imgFrame::GetSurfaceInternal()
{
- mMutex.AssertCurrentThreadOwns();
+ mMonitor.AssertCurrentThreadOwns();
if (mOptSurface) {
if (mOptSurface->IsValid())
return mOptSurface;
else
mOptSurface = nullptr;
}
@@ -925,34 +954,34 @@ imgFrame::GetSurfaceInternal()
return nullptr;
return CreateLockedSurface(mVBuf, mSize, mFormat);
}
TemporaryRef<DrawTarget>
imgFrame::GetDrawTarget()
{
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
uint8_t* data;
uint32_t length;
GetImageDataInternal(&data, &length);
if (!data) {
return nullptr;
}
int32_t stride = GetStride();
return gfxPlatform::GetPlatform()->
CreateDrawTargetForData(data, mSize, stride, mFormat);
}
AnimationData
imgFrame::GetAnimationData() const
{
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
uint8_t* data;
if (mPalettedImageData) {
data = mPalettedImageData;
} else {
uint32_t length;
GetImageDataInternal(&data, &length);
@@ -962,38 +991,67 @@ imgFrame::GetAnimationData() const
return AnimationData(data, PaletteDataLength(), mTimeout, GetRect(),
mBlendMethod, mDisposalMethod, hasAlpha);
}
ScalingData
imgFrame::GetScalingData() const
{
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
MOZ_ASSERT(!GetIsPaletted(), "GetScalingData can't handle paletted images");
uint8_t* data;
uint32_t length;
GetImageDataInternal(&data, &length);
return ScalingData(data, mSize, GetImageBytesPerRow(), mFormat);
}
-bool
-imgFrame::ImageComplete() const
+void
+imgFrame::Abort()
{
- MutexAutoLock lock(mMutex);
- return ImageCompleteInternal();
+ MonitorAutoLock lock(mMonitor);
+
+ mAborted = true;
+
+ // Wake up anyone who's waiting.
+ if (IsImageCompleteInternal()) {
+ mMonitor.NotifyAll();
+ }
}
bool
-imgFrame::ImageCompleteInternal() const
+imgFrame::IsImageComplete() const
+{
+ MonitorAutoLock lock(mMonitor);
+ return IsImageCompleteInternal();
+}
+
+void
+imgFrame::WaitUntilComplete() const
{
- mMutex.AssertCurrentThreadOwns();
+ MonitorAutoLock lock(mMonitor);
+
+ while (true) {
+ // Return if we're aborted or complete.
+ if (mAborted || IsImageCompleteInternal()) {
+ return;
+ }
+
+ // Not complete yet, so we'll have to wait.
+ mMonitor.Wait();
+ }
+}
+
+bool
+imgFrame::IsImageCompleteInternal() const
+{
+ mMonitor.AssertCurrentThreadOwns();
return mDecoded.IsEqualInterior(nsIntRect(mOffset.x, mOffset.y,
mSize.width, mSize.height));
}
bool imgFrame::GetCompositingFailed() const
{
MOZ_ASSERT(NS_IsMainThread());
return mCompositingFailed;
@@ -1004,17 +1062,17 @@ void imgFrame::SetCompositingFailed(bool
MOZ_ASSERT(NS_IsMainThread());
mCompositingFailed = val;
}
size_t
imgFrame::SizeOfExcludingThis(gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf) const
{
- MutexAutoLock lock(mMutex);
+ MonitorAutoLock lock(mMonitor);
// aMallocSizeOf is only used if aLocation==gfxMemoryLocation::IN_PROCESS_HEAP. It
// should be nullptr otherwise.
NS_ABORT_IF_FALSE(
(aLocation == gfxMemoryLocation::IN_PROCESS_HEAP && aMallocSizeOf) ||
(aLocation != gfxMemoryLocation::IN_PROCESS_HEAP && !aMallocSizeOf),
"mismatch between aLocation and aMallocSizeOf");
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -3,18 +3,18 @@
* 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 imgFrame_h
#define imgFrame_h
#include "mozilla/MemoryReporting.h"
+#include "mozilla/Monitor.h"
#include "mozilla/Move.h"
-#include "mozilla/Mutex.h"
#include "mozilla/TypedEnum.h"
#include "mozilla/VolatileBuffer.h"
#include "gfxDrawable.h"
#include "imgIContainer.h"
#include "MainThreadUtils.h"
namespace mozilla {
namespace image {
@@ -177,29 +177,58 @@ public:
bool Draw(gfxContext* aContext, const ImageRegion& aRegion,
GraphicsFilter aFilter, uint32_t aImageFlags);
nsresult ImageUpdated(const nsIntRect &aUpdateRect);
/**
* Mark this imgFrame as completely decoded, and set final options.
*
+ * You must always call either Finish() or Abort() before releasing the last
+ * RawAccessFrameRef pointing to an imgFrame.
+ *
* @param aFrameOpacity Whether this imgFrame is opaque.
* @param aDisposalMethod For animation frames, how this imgFrame is cleared
* from the compositing frame before the next frame is
* displayed.
* @param aRawTimeout For animation frames, the timeout in milliseconds
* before the next frame is displayed. This timeout is
* not necessarily the timeout that will actually be
* used; see FrameAnimator::GetTimeoutForFrame.
* @param aBlendMethod For animation frames, a blending method to be used
* when compositing this frame.
*/
- void Finish(Opacity aFrameOpacity, DisposalMethod aDisposalMethod,
- int32_t aRawTimeout, BlendMethod aBlendMethod);
+ void Finish(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY,
+ DisposalMethod aDisposalMethod = DisposalMethod::KEEP,
+ int32_t aRawTimeout = 0,
+ BlendMethod aBlendMethod = BlendMethod::OVER);
+
+ /**
+ * Mark this imgFrame as aborted. This informs the imgFrame that if it isn't
+ * completely decoded now, it never will be.
+ *
+ * You must always call either Finish() or Abort() before releasing the last
+ * RawAccessFrameRef pointing to an imgFrame.
+ */
+ void Abort();
+
+ /**
+ * Returns true if this imgFrame is completely decoded.
+ */
+ bool IsImageComplete() const;
+
+ /**
+ * Blocks until this imgFrame is either completely decoded, or is marked as
+ * aborted.
+ *
+ * Note that calling this on the main thread _blocks the main thread_. Be very
+ * careful in your use of this method to avoid excessive main thread jank or
+ * deadlock.
+ */
+ void WaitUntilComplete() const;
IntSize GetImageSize() { return mImageSize; }
nsIntRect GetRect() const;
IntSize GetSize() const { return mSize; }
bool NeedsPadding() const { return mOffset != nsIntPoint(0, 0); }
void GetImageData(uint8_t **aData, uint32_t *length) const;
uint8_t* GetImageData() const;
@@ -213,18 +242,16 @@ public:
*
* This should only be used for assertions.
*/
SurfaceFormat GetFormat() const;
AnimationData GetAnimationData() const;
ScalingData GetScalingData() const;
- bool ImageComplete() const;
-
bool GetCompositingFailed() const;
void SetCompositingFailed(bool val);
void SetOptimizable();
Color SinglePixelColor() const;
bool IsSinglePixel() const;
@@ -240,17 +267,17 @@ private: // methods
nsresult LockImageData();
nsresult UnlockImageData();
nsresult Optimize();
nsresult Deoptimize();
void AssertImageDataLocked() const;
- bool ImageCompleteInternal() const;
+ bool IsImageCompleteInternal() const;
nsresult ImageUpdatedInternal(const nsIntRect& aUpdateRect);
void GetImageDataInternal(uint8_t **aData, uint32_t *length) const;
uint32_t GetImageBytesPerRow() const;
uint32_t GetImageDataLength() const;
int32_t GetStride() const;
TemporaryRef<SourceSurface> GetSurfaceInternal();
uint32_t PaletteDataLength() const
@@ -278,20 +305,20 @@ private: // methods
SourceSurface* aSurface);
private: // data
friend class DrawableFrameRef;
friend class RawAccessFrameRef;
friend class UnlockImageDataRunnable;
//////////////////////////////////////////////////////////////////////////////
- // Thread-safe mutable data, protected by mMutex.
+ // Thread-safe mutable data, protected by mMonitor.
//////////////////////////////////////////////////////////////////////////////
- mutable Mutex mMutex;
+ mutable Monitor mMonitor;
RefPtr<DataSourceSurface> mImageSurface;
RefPtr<SourceSurface> mOptSurface;
RefPtr<VolatileBuffer> mVBuf;
VolatileBufferPtr<uint8_t> mVBufPtr;
nsIntRect mDecoded;
@@ -302,16 +329,17 @@ private: // data
//! Raw timeout for this frame. (See FrameAnimator::GetTimeoutForFrame.)
int32_t mTimeout; // -1 means display forever.
DisposalMethod mDisposalMethod;
BlendMethod mBlendMethod;
SurfaceFormat mFormat;
bool mHasNoAlpha;
+ bool mAborted;
//////////////////////////////////////////////////////////////////////////////
// Effectively const data, only mutated in the Init methods.
//////////////////////////////////////////////////////////////////////////////
IntSize mImageSize;
IntSize mSize;