Bug 545513: Flush surfaces and mark them dirty at the correct points. r=jrmuizel
--- a/modules/libpr0n/src/imgContainer.cpp
+++ b/modules/libpr0n/src/imgContainer.cpp
@@ -663,41 +663,57 @@ nsresult imgContainer::InternalAddFrameH
nsAutoPtr<imgFrame> frame(aFrame);
if (paletteData && paletteLength)
frame->GetPaletteData(paletteData, paletteLength);
frame->GetImageData(imageData, imageLength);
+ // We are in the middle of decoding. This will be unlocked when we finish the
+ // decoder->Write() call.
+ frame->LockImageData();
+
mFrames.InsertElementAt(framenum, frame.forget());
return NS_OK;
}
nsresult imgContainer::InternalAddFrame(PRUint32 framenum,
PRInt32 aX, PRInt32 aY,
PRInt32 aWidth, PRInt32 aHeight,
gfxASurface::gfxImageFormat aFormat,
PRUint8 aPaletteDepth,
PRUint8 **imageData,
PRUint32 *imageLength,
PRUint32 **paletteData,
PRUint32 *paletteLength)
{
+ // 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(mInDecoder, "Only decoders may add frames!");
+
NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
if (framenum > mFrames.Length())
return NS_ERROR_INVALID_ARG;
nsAutoPtr<imgFrame> frame(new imgFrame());
NS_ENSURE_TRUE(frame, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
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 (mFrames.Length() > 0) {
+ imgFrame *prevframe = mFrames.ElementAt(mFrames.Length() - 1);
+ prevframe->UnlockImageData();
+ }
+
if (mFrames.Length() == 0) {
return InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
paletteData, paletteLength);
}
if (mFrames.Length() == 1) {
// Since we're about to add our second frame, initialize animation stuff
if (!ensureAnimExists())
@@ -2184,20 +2200,38 @@ imgContainer::ShutdownDecoder(eShutdownI
// Writes the data to the decoder, updating the total number of bytes written.
nsresult
imgContainer::WriteToDecoder(const char *aBuffer, PRUint32 aCount)
{
// We should have a decoder
NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!");
+ // The decoder will start decoding into the current frame (if we have one).
+ // When it needs to add another frame, we will unlock this frame and lock the
+ // new frame.
+ // Our invariant is that, while in the decoder, the last frame is always
+ // locked, and all others are unlocked.
+ if (mFrames.Length() > 0) {
+ imgFrame *curframe = mFrames.ElementAt(mFrames.Length() - 1);
+ curframe->LockImageData();
+ }
+
// Write
mInDecoder = PR_TRUE;
nsresult rv = mDecoder->Write(aBuffer, aCount);
mInDecoder = PR_FALSE;
+
+ // We unlock the current frame, even if that frame is different from the
+ // frame we entered the decoder with. (See above.)
+ if (mFrames.Length() > 0) {
+ imgFrame *curframe = mFrames.ElementAt(mFrames.Length() - 1);
+ curframe->UnlockImageData();
+ }
+
CONTAINER_ENSURE_SUCCESS(rv);
// Keep track of the total number of bytes written over the lifetime of the
// decoder
mBytesDecoded += aCount;
return NS_OK;
}
--- a/modules/libpr0n/src/imgFrame.cpp
+++ b/modules/libpr0n/src/imgFrame.cpp
@@ -152,16 +152,17 @@ imgFrame::imgFrame() :
mBlendMethod(1), /* imgIContainer::kBlendOver */
mSinglePixel(PR_FALSE),
mNeverUseDeviceSurface(PR_FALSE),
mFormatChanged(PR_FALSE),
mCompositingFailed(PR_FALSE)
#ifdef USE_WIN_SURFACE
, mIsDDBSurface(PR_FALSE)
#endif
+ , mLocked(PR_FALSE)
{
static PRBool hasCheckedOptimize = PR_FALSE;
if (!hasCheckedOptimize) {
if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) {
gDisableOptimize = PR_TRUE;
}
hasCheckedOptimize = PR_TRUE;
}
@@ -808,16 +809,22 @@ void imgFrame::GetPaletteData(PRUint32 *
}
}
nsresult imgFrame::LockImageData()
{
if (mPalettedImageData)
return NS_ERROR_NOT_AVAILABLE;
+ NS_ABORT_IF_FALSE(!mLocked, "Trying to lock already locked image data.");
+ if (mLocked) {
+ return NS_ERROR_FAILURE;
+ }
+ mLocked = PR_TRUE;
+
if ((mOptSurface || mSinglePixel) && !mImageSurface) {
// Recover the pixels
mImageSurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height),
gfxImageSurface::ImageFormatARGB32);
if (!mImageSurface || mImageSurface->CairoStatus())
return NS_ERROR_OUT_OF_MEMORY;
gfxContext context(mImageSurface);
@@ -840,16 +847,23 @@ nsresult imgFrame::LockImageData()
return NS_OK;
}
nsresult imgFrame::UnlockImageData()
{
if (mPalettedImageData)
return NS_ERROR_NOT_AVAILABLE;
+ NS_ABORT_IF_FALSE(mLocked, "Unlocking an unlocked image!");
+ if (!mLocked) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mLocked = PR_FALSE;
+
#ifdef XP_MACOSX
if (mQuartzSurface)
mQuartzSurface->Flush();
#endif
return NS_OK;
}
PRInt32 imgFrame::GetTimeout() const
--- a/modules/libpr0n/src/imgFrame.h
+++ b/modules/libpr0n/src/imgFrame.h
@@ -167,16 +167,18 @@ private: // data
gfxASurface::gfxImageFormat mFormat;
PRInt8 mPaletteDepth;
PRInt8 mBlendMethod;
PRPackedBool mSinglePixel;
PRPackedBool mNeverUseDeviceSurface;
PRPackedBool mFormatChanged;
PRPackedBool mCompositingFailed;
+ /** Indicates if the image data is currently locked */
+ PRPackedBool mLocked;
#ifdef XP_WIN
PRPackedBool mIsDDBSurface;
#endif
};
#endif /* imgFrame_h */