diff --git a/image/decoders/nsICODecoder.cpp b/image/decoders/nsICODecoder.cpp index 6313ffb984d..62075c79a1b 100644 --- a/image/decoders/nsICODecoder.cpp +++ b/image/decoders/nsICODecoder.cpp @@ -9,6 +9,7 @@ #include #include "mozilla/Endian.h" +#include "mozilla/Move.h" #include "nsICODecoder.h" #include "RasterImage.h" @@ -344,7 +345,7 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount, mContainedDecoder->SetSizeDecode(IsSizeDecode()); mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength, mColormap, mColormapSize, - mCurrentFrame); + Move(mRefForContainedDecoder)); if (!WriteToContainedDecoder(mSignature, PNGSIGNATURESIZE, aStrategy)) { return; } @@ -422,7 +423,7 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount, mContainedDecoder->SetSizeDecode(IsSizeDecode()); mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength, mColormap, mColormapSize, - mCurrentFrame); + Move(mRefForContainedDecoder)); // 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 @@ -640,15 +641,21 @@ nsICODecoder::NeedsNewFrame() const nsresult nsICODecoder::AllocateFrame() { + nsresult rv; + if (mContainedDecoder) { - nsresult rv = mContainedDecoder->AllocateFrame(); - mCurrentFrame = mContainedDecoder->GetCurrentFrame(); + rv = mContainedDecoder->AllocateFrame(); + mCurrentFrame = mContainedDecoder->GetCurrentFrameRef(); mProgress |= mContainedDecoder->TakeProgress(); mInvalidRect.Union(mContainedDecoder->TakeInvalidRect()); return rv; } - return Decoder::AllocateFrame(); + // 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; } } // namespace image diff --git a/image/decoders/nsICODecoder.h b/image/decoders/nsICODecoder.h index 8d5614fc9a3..080e0ae70d7 100644 --- a/image/decoders/nsICODecoder.h +++ b/image/decoders/nsICODecoder.h @@ -9,6 +9,7 @@ #include "nsAutoPtr.h" #include "Decoder.h" +#include "imgFrame.h" #include "nsBMPDecoder.h" #include "nsPNGDecoder.h" #include "ICOFileHeaders.h" @@ -82,6 +83,7 @@ private: uint32_t mRowBytes; // How many bytes of the row were already received int32_t mOldLine; // Previous index of the line nsRefPtr 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 diff --git a/image/src/Decoder.cpp b/image/src/Decoder.cpp index ee6a9a19a56..58fcd401ec9 100644 --- a/image/src/Decoder.cpp +++ b/image/src/Decoder.cpp @@ -16,7 +16,6 @@ namespace image { Decoder::Decoder(RasterImage &aImage) : mImage(aImage) - , mCurrentFrame(nullptr) , mProgress(NoProgress) , mImageData(nullptr) , mColormap(nullptr) @@ -68,18 +67,19 @@ Decoder::Init() // Initializes a decoder whose image and observer is already being used by a // parent decoder void -Decoder::InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength, - uint32_t* colormap, uint32_t colormapSize, - imgFrame* currentFrame) +Decoder::InitSharedDecoder(uint8_t* aImageData, uint32_t aImageDataLength, + uint32_t* aColormap, uint32_t aColormapSize, + RawAccessFrameRef&& aFrameRef) { // No re-initializing NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!"); - mImageData = imageData; - mImageDataLength = imageDataLength; - mColormap = colormap; - mColormapSize = colormapSize; - mCurrentFrame = currentFrame; + mImageData = aImageData; + mImageDataLength = aImageDataLength; + mColormap = aColormap; + mColormapSize = aColormapSize; + mCurrentFrame = Move(aFrameRef); + // We have all the frame data, so we've started the frame. if (!IsSizeDecode()) { PostFrameStart(); @@ -206,11 +206,13 @@ Decoder::Finish(ShutdownReason aReason) } } - // 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) { - mImage.DecodingComplete(); + MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame"); + mImage.DecodingComplete(mCurrentFrame.get()); } } @@ -230,34 +232,22 @@ Decoder::AllocateFrame() MOZ_ASSERT(mNeedsNewFrame); MOZ_ASSERT(NS_IsMainThread()); - nsresult rv; - nsRefPtr 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)); - } + mCurrentFrame = mImage.EnsureFrame(mNewFrameData.mFrameNum, + mNewFrameData.mFrameRect, + mDecodeFlags, + mNewFrameData.mFormat, + mNewFrameData.mPaletteDepth, + mCurrentFrame.get()); - if (NS_SUCCEEDED(rv)) { - mCurrentFrame = frame; - } else { - mCurrentFrame = nullptr; - } + if (mCurrentFrame) { + // Gather the raw pointers the decoders will use. + mCurrentFrame->GetImageData(&mImageData, &mImageDataLength); + mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize); - // Notify if appropriate - if (NS_SUCCEEDED(rv) && mNewFrameData.mFrameNum == mFrameCount) { - PostFrameStart(); - } else if (NS_FAILED(rv)) { + if (mNewFrameData.mFrameNum == mFrameCount) { + PostFrameStart(); + } + } else { PostDataError(); } @@ -271,7 +261,7 @@ Decoder::AllocateFrame() mNeedsToFlushData = true; } - return rv; + return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE; } void @@ -338,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. - NS_ABORT_IF_FALSE(mFrameCount == mImage.GetNumFrames(), - "Decoder frame count doesn't match image's!"); + MOZ_ASSERT(mFrameCount == mImage.GetNumFrames(), + "Decoder frame count doesn't match image's!"); } void @@ -389,7 +379,6 @@ Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */) mDecodeDone = true; mImageMetadata.SetLoopCount(aLoopCount); - mImageMetadata.SetIsNonPremultiplied(GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA); mProgress |= FLAG_DECODE_COMPLETE; } @@ -424,7 +413,9 @@ 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, x_offset, y_offset, width, height, format, palette_depth); + mNewFrameData = NewFrameData(framenum, + nsIntRect(x_offset, y_offset, width, height), + format, palette_depth); mNeedsNewFrame = true; } diff --git a/image/src/Decoder.h b/image/src/Decoder.h index 0ae05af5408..544b14e916f 100644 --- a/image/src/Decoder.h +++ b/image/src/Decoder.h @@ -36,9 +36,9 @@ public: * * Notifications Sent: TODO */ - void InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength, - uint32_t* colormap, uint32_t colormapSize, - imgFrame* currentFrame); + void InitSharedDecoder(uint8_t* aImageData, uint32_t aImageDataLength, + uint32_t* aColormap, uint32_t aColormapSize, + RawAccessFrameRef&& aFrameRef); /** * Writes data to the decoder. @@ -186,12 +186,17 @@ public: // status code from that attempt. Clears mNewFrameData. virtual nsresult AllocateFrame(); - already_AddRefed GetCurrentFrame() const + already_AddRefed GetCurrentFrame() { - nsRefPtr frame = mCurrentFrame; + nsRefPtr frame = mCurrentFrame.get(); return frame.forget(); } + RawAccessFrameRef GetCurrentFrameRef() + { + return mCurrentFrame->RawAccessRef(); + } + protected: virtual ~Decoder(); @@ -263,7 +268,7 @@ protected: * */ RasterImage &mImage; - nsRefPtr mCurrentFrame; + RawAccessFrameRef mCurrentFrame; ImageMetadata mImageMetadata; nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame. Progress mProgress; @@ -289,28 +294,22 @@ private: struct NewFrameData { - NewFrameData() - {} + NewFrameData() { } + + NewFrameData(uint32_t aFrameNum, const nsIntRect& aFrameRect, + gfx::SurfaceFormat aFormat, uint8_t aPaletteDepth) + : mFrameNum(aFrameNum) + , mFrameRect(aFrameRect) + , mFormat(aFormat) + , mPaletteDepth(aPaletteDepth) + { } - 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; - uint32_t mOffsetX; - uint32_t mOffsetY; - uint32_t mWidth; - uint32_t mHeight; + nsIntRect mFrameRect; gfx::SurfaceFormat mFormat; uint8_t mPaletteDepth; }; + NewFrameData mNewFrameData; bool mNeedsNewFrame; bool mNeedsToFlushData; diff --git a/image/src/ImageMetadata.cpp b/image/src/ImageMetadata.cpp index cb74600802d..03bd48965e3 100644 --- a/image/src/ImageMetadata.cpp +++ b/image/src/ImageMetadata.cpp @@ -27,10 +27,6 @@ ImageMetadata::SetOnImage(RasterImage* image) } image->SetLoopCount(mLoopCount); - - for (uint32_t i = 0; i < image->GetNumFrames(); i++) { - image->SetFrameAsNonPremult(i, mIsNonPremultiplied); - } } } // namespace image diff --git a/image/src/ImageMetadata.h b/image/src/ImageMetadata.h index acaeb10dd1a..e05d9b3c0e6 100644 --- a/image/src/ImageMetadata.h +++ b/image/src/ImageMetadata.h @@ -25,7 +25,6 @@ public: : mHotspotX(-1) , mHotspotY(-1) , mLoopCount(-1) - , mIsNonPremultiplied(false) {} // Set the metadata this object represents on an image. @@ -41,11 +40,6 @@ public: mLoopCount = loopcount; } - void SetIsNonPremultiplied(bool nonPremult) - { - mIsNonPremultiplied = nonPremult; - } - void SetSize(int32_t width, int32_t height, Orientation orientation) { mSize.emplace(nsIntSize(width, height)); @@ -68,9 +62,7 @@ private: int32_t mLoopCount; Maybe mSize; - Maybe mOrientation; - - bool mIsNonPremultiplied; + Maybe mOrientation; }; } // namespace image diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index 1e80e667db2..8ad0eb2cc14 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -233,9 +233,6 @@ 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 @@ -322,7 +319,9 @@ RasterImage::RasterImage(ProgressTracker* aProgressTracker, mDiscardable(false), mHasSourceData(false), mDecoded(false), + mHasFirstFrame(false), mHasBeenDecoded(false), + mPendingAnimation(false), mAnimationFinished(false), mWantFullDecode(false), mPendingError(false) @@ -364,7 +363,7 @@ RasterImage::~RasterImage() mDecoder = nullptr; } - // Release any HQ scaled frames from the surface cache. + // Release all frames from the surface cache. SurfaceCache::RemoveImage(ImageKey(this)); mAnim = nullptr; @@ -419,6 +418,11 @@ 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); @@ -558,21 +562,33 @@ RasterImage::GetType() return imgIContainer::TYPE_RASTER; } -already_AddRefed -RasterImage::LookupFrameNoDecode(uint32_t aFrameNum) +DrawableFrameRef +RasterImage::LookupFrameInternal(uint32_t aFrameNum, + const nsIntSize& aSize, + uint32_t aFlags) { - if (!mAnim) { - NS_ASSERTION(aFrameNum == 0, "Don't ask for a frame > 0 if we're not animated!"); - return mFrameBlender.GetFrame(0); + if (mAnim) { + MOZ_ASSERT(mFrameBlender, "mAnim but no mFrameBlender?"); + nsRefPtr frame = mFrameBlender->GetFrame(aFrameNum); + return frame->DrawableRef(); } - return mFrameBlender.GetFrame(aFrameNum); + + NS_ASSERTION(aFrameNum == 0, + "Don't ask for a frame > 0 if we're not animated!"); + + return SurfaceCache::Lookup(ImageKey(this), + RasterSurfaceKey(aSize.ToIntSize(), + DecodeFlags(aFlags))); } DrawableFrameRef RasterImage::LookupFrame(uint32_t aFrameNum, + const nsIntSize& aSize, uint32_t aFlags, bool aShouldSyncNotify /* = true */) { + MOZ_ASSERT(NS_IsMainThread()); + if (mMultipart && aFrameNum == GetCurrentFrameIndex() && mMultipartDecodedFrame) { @@ -582,26 +598,18 @@ RasterImage::LookupFrame(uint32_t aFrameNum, return mMultipartDecodedFrame->DrawableRef(); } - // Try our best to start decoding if it's necessary. - nsresult rv = WantDecodedFrames(aFlags, aShouldSyncNotify); - CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), DrawableFrameRef()); + DrawableFrameRef ref = LookupFrameInternal(aFrameNum, aSize, aFlags); - nsRefPtr 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"); if (CanForciblyDiscardAndRedecode()) { - ForceDiscard(); + Discard(/* aForce = */ true, /* aNotify = */ false); + ApplyDecodeFlags(aFlags); WantDecodedFrames(aFlags, aShouldSyncNotify); - // See if we managed to entirely redecode the frame. - frame = LookupFrameNoDecode(aFrameNum); - ref = frame->DrawableRef(); + // See if we managed to redecode enough to get the frame we want. + ref = LookupFrameInternal(aFrameNum, aSize, aFlags); } if (!ref) { @@ -672,9 +680,18 @@ RasterImage::FrameRect(uint32_t aWhichFrame) return nsIntRect(); } - // Get the requested frame. + 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"); nsRefPtr frame = - LookupFrameNoDecode(GetRequestedFrameIndex(aWhichFrame)); + mFrameBlender->RawGetFrame(GetRequestedFrameIndex(aWhichFrame)); // If we have the frame, use that rectangle. if (frame) { @@ -692,7 +709,10 @@ RasterImage::FrameRect(uint32_t aWhichFrame) uint32_t RasterImage::GetNumFrames() const { - return mFrameBlender.GetNumFrames(); + if (mFrameBlender) { + return mFrameBlender->GetNumFrames(); + } + return mHasFirstFrame ? 1 : 0; } //****************************************************************************** @@ -734,7 +754,8 @@ RasterImage::GetFirstFrameDelay() if (NS_FAILED(GetAnimated(&animated)) || !animated) return -1; - return mFrameBlender.GetTimeoutForFrame(0); + MOZ_ASSERT(mFrameBlender, "Animated images should have a FrameBlender"); + return mFrameBlender->GetTimeoutForFrame(0); } TemporaryRef @@ -755,7 +776,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), - aFlags, aShouldSyncNotify); + mSize, aFlags, aShouldSyncNotify); if (!frameRef) { // The OS threw this frame away and we couldn't redecode it right now. return nullptr; @@ -832,7 +853,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), - aFlags, aShouldSyncNotify); + mSize, aFlags, aShouldSyncNotify); if (!frameRef) { // The OS threw this frame away and we couldn't redecode it. return nullptr; @@ -960,7 +981,10 @@ size_t RasterImage::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation, MallocSizeOf aMallocSizeOf) const { - return mFrameBlender.SizeOfDecodedWithComputedFallbackIfHeap(aLocation, aMallocSizeOf); + return mFrameBlender + ? mFrameBlender->SizeOfDecodedWithComputedFallbackIfHeap(aLocation, + aMallocSizeOf) + : 0; } size_t @@ -984,115 +1008,114 @@ RasterImage::OutOfProcessSizeOfDecoded() const nullptr); } -void -RasterImage::EnsureAnimExists() -{ - if (!mAnim) { - - // Create the animation context - mAnim = MakeUnique(mFrameBlender, mAnimationMode); - - // We don't support discarding animated images (See bug 414259). - // Lock the image and throw away the key. - // - // Note that this is inefficient, since we could get rid of the source - // data too. However, doing this is actually hard, because we're probably - // calling ensureAnimExists mid-decode, and thus we're decoding out of - // the source buffer. Since we're going to fix this anyway later, and - // since we didn't kill the source data in the old world either, locking - // is acceptable for the moment. - LockImage(); - } -} - -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; - - nsRefPtr frame = aFrame; - RawAccessFrameRef ref = frame->RawAccessRef(); - if (!ref) { - // Probably the OS discarded the frame. Exceedingly unlikely since we just - // created it, but it could happen. - return NS_ERROR_FAILURE; - } - - if (paletteData && paletteLength) - frame->GetPaletteData(paletteData, paletteLength); - - frame->GetImageData(imageData, imageLength); - - mFrameBlender.InsertFrame(framenum, Move(ref)); - - frame.forget(aRetFrame); - return NS_OK; -} - -nsresult -RasterImage::InternalAddFrame(uint32_t framenum, - int32_t aX, int32_t aY, - int32_t aWidth, int32_t aHeight, +RawAccessFrameRef +RasterImage::InternalAddFrame(uint32_t aFrameNum, + const nsIntRect& aFrameRect, + uint32_t aDecodeFlags, SurfaceFormat aFormat, uint8_t aPaletteDepth, - uint8_t **imageData, - uint32_t *imageLength, - uint32_t **paletteData, - uint32_t *paletteLength, - imgFrame** aRetFrame) + imgFrame* aPreviousFrame) { // 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!"); + MOZ_ASSERT(mDecoder, "Only decoders may add frames!"); - NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Invalid frame index!"); - if (framenum > GetNumFrames()) - return NS_ERROR_INVALID_ARG; + MOZ_ASSERT(aFrameNum <= GetNumFrames(), "Invalid frame index!"); + if (aFrameNum > GetNumFrames()) { + return RawAccessFrameRef(); + } - nsRefPtr frame(new imgFrame()); + if (mSize.width <= 0 || mSize.height <= 0) { + NS_WARNING("Trying to add frame with zero or negative size"); + return RawAccessFrameRef(); + } - 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)) + if (!SurfaceCache::CanHold(mSize.ToIntSize())) { + NS_WARNING("Trying to add frame that's too large for the SurfaceCache"); + return RawAccessFrameRef(); + } + + nsRefPtr frame = new imgFrame(); + if (NS_FAILED(frame->InitForDecoder(mSize, aFrameRect, aFormat, + aPaletteDepth))) { NS_WARNING("imgFrame::Init should succeed"); - NS_ENSURE_SUCCESS(rv, rv); + return RawAccessFrameRef(); + } + frame->SetAsNonPremult(aDecodeFlags & FLAG_DECODE_NO_PREMULTIPLY_ALPHA); + + RawAccessFrameRef ref = frame->RawAccessRef(); + if (!ref) { + return RawAccessFrameRef(); + } if (GetNumFrames() == 0) { - return InternalAddFrameHelper(framenum, frame, imageData, imageLength, - paletteData, paletteLength, aRetFrame); + bool succeeded = + SurfaceCache::Insert(frame, ImageKey(this), + RasterSurfaceKey(mSize.ToIntSize(), aDecodeFlags), + Lifetime::Persistent); + if (!succeeded) { + return RawAccessFrameRef(); + } + mHasFirstFrame = true; + return ref; } if (GetNumFrames() == 1) { - // Since we're about to add our second frame, initialize animation stuff - EnsureAnimExists(); + // 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(*mFrameBlender, mAnimationMode); - // 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 firstFrame = mFrameBlender.RawGetFrame(0); - int32_t frameDisposalMethod = firstFrame->GetFrameDisposalMethod(); + // We don't support discarding animated images (See bug 414259). + // Lock the image and throw away the key. + // + // Note that this is inefficient, since we could get rid of the source data + // too. However, doing this is actually hard, because we're probably + // mid-decode, and thus we're decoding out of the source buffer. Since we're + // going to fix this anyway later, and since we didn't kill the source data + // in the old world either, locking 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)); + + // 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(mSize.ToIntSize(), + aDecodeFlags)); + + // 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(firstFrame->GetRect()); + frameDisposalMethod == FrameBlender::kDisposeRestorePrevious) { + mAnim->SetFirstFrameRefreshArea(aPreviousFrame->GetRect()); + } + + if (mPendingAnimation && ShouldAnimate()) { + StartAnimation(); + } } - // 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 + // 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()); - rv = InternalAddFrameHelper(framenum, frame, imageData, imageLength, - paletteData, paletteLength, aRetFrame); + MOZ_ASSERT(mFrameBlender, "Should have a FrameBlender by now"); + mFrameBlender->InsertFrame(aFrameNum, frame->RawAccessRef()); - return rv; + return ref; } bool @@ -1162,121 +1185,70 @@ RasterImage::SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation) mOrientation = aOrientation; mHasSize = true; - mFrameBlender.SetSize(mSize); - return NS_OK; } -nsresult -RasterImage::EnsureFrame(uint32_t aFrameNum, int32_t aX, int32_t aY, - int32_t aWidth, int32_t aHeight, +RawAccessFrameRef +RasterImage::EnsureFrame(uint32_t aFrameNum, + const nsIntRect& aFrameRect, + uint32_t aDecodeFlags, SurfaceFormat aFormat, uint8_t aPaletteDepth, - uint8_t **imageData, uint32_t *imageLength, - uint32_t **paletteData, uint32_t *paletteLength, - imgFrame** aRetFrame) + imgFrame* aPreviousFrame) { - 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); + if (mError) { + return RawAccessFrameRef(); } - if (aFrameNum > GetNumFrames()) - return NS_ERROR_INVALID_ARG; + MOZ_ASSERT(aFrameNum <= GetNumFrames(), "Invalid frame index!"); + if (aFrameNum > GetNumFrames()) { + return RawAccessFrameRef(); + } - // Adding a frame that doesn't already exist. + // Adding a frame that doesn't already exist. This is the normal case. if (aFrameNum == GetNumFrames()) { - return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat, - aPaletteDepth, imageData, imageLength, - paletteData, paletteLength, aRetFrame); + return InternalAddFrame(aFrameNum, aFrameRect, aDecodeFlags, aFormat, + aPaletteDepth, aPreviousFrame); } - nsRefPtr frame = mFrameBlender.RawGetFrame(aFrameNum); - if (!frame) { - 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(); } - // 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); - } + MOZ_ASSERT(!aPreviousFrame->GetRect().IsEqualEdges(aFrameRect) || + aPreviousFrame->GetFormat() != aFormat || + aPreviousFrame->GetPaletteDepth() != aPaletteDepth, + "Replacing first frame with the same kind of frame?"); - // 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; - } - } + // Remove the old frame from the SurfaceCache. + IntSize prevFrameSize = aPreviousFrame->GetImageSize(); + SurfaceCache::RemoveSurface(ImageKey(this), + RasterSurfaceKey(prevFrameSize, aDecodeFlags)); + mHasFirstFrame = false; - // Not reusable, so replace the frame directly. - mFrameBlender.RemoveFrame(aFrameNum); - nsRefPtr 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); + // Add the new frame as usual. + return InternalAddFrame(aFrameNum, aFrameRect, aDecodeFlags, aFormat, + aPaletteDepth, nullptr); } -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 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() +void +RasterImage::DecodingComplete(imgFrame* aFinalFrame) { MOZ_ASSERT(NS_IsMainThread()); - if (mError) - return NS_ERROR_FAILURE; + if (mError) { + return; + } // Flag that we're done decoding. // XXX - these should probably be combined when we fix animated image @@ -1284,48 +1256,42 @@ RasterImage::DecodingComplete() 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"); - rv = DiscardTracker::Reset(&mDiscardTrackerNode); - CONTAINER_ENSURE_SUCCESS(rv); + DiscardTracker::Reset(&mDiscardTrackerNode); } + bool singleFrame = GetNumFrames() == 1; + // If there's only 1 frame, mark it as optimizable. Optimizing animated images // is not supported. // // We don't optimize the frame for multipart images because we reuse // the frame. - if ((GetNumFrames() == 1) && !mMultipart) { - nsRefPtr firstFrame = mFrameBlender.RawGetFrame(0); - firstFrame->SetOptimizable(); - if (DiscardingEnabled() && CanForciblyDiscard()) { - firstFrame->SetDiscardable(); - } + if (singleFrame && !mMultipart && aFinalFrame) { + aFinalFrame->SetOptimizable(); } // 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) { - mMultipartDecodedFrame = mFrameBlender.GetFrame(GetCurrentFrameIndex()); + if (singleFrame && aFinalFrame) { + // aFinalFrame must be the first frame since we only have one. + mMultipartDecodedFrame = aFinalFrame->DrawableRef(); } 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 = nullptr; + mMultipartDecodedFrame.reset(); } } if (mAnim) { mAnim->SetDoneDecoding(true); } - - return NS_OK; } NS_IMETHODIMP @@ -1347,12 +1313,17 @@ RasterImage::StartAnimation() NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!"); - EnsureAnimExists(); + // 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; + } - nsRefPtr currentFrame = LookupFrameNoDecode(GetCurrentFrameIndex()); // A timeout of -1 means we should display this frame forever. - if (currentFrame && - mFrameBlender.GetTimeoutForFrame(GetCurrentFrameIndex()) < 0) { + MOZ_ASSERT(mFrameBlender, "Have an animation but no FrameBlender?"); + if (mFrameBlender->GetTimeoutForFrame(GetCurrentFrameIndex()) < 0) { mAnimationFinished = true; return NS_ERROR_ABORT; } @@ -1392,16 +1363,20 @@ RasterImage::ResetAnimation() if (mError) return NS_ERROR_FAILURE; - if (mAnimationMode == kDontAnimMode || - !mAnim || mAnim->GetCurrentAnimationFrameIndex() == 0) + mPendingAnimation = false; + + if (mAnimationMode == kDontAnimMode || !mAnim || + mAnim->GetCurrentAnimationFrameIndex() == 0) { return NS_OK; + } mAnimationFinished = false; if (mAnimating) StopAnimation(); - mFrameBlender.ResetAnimation(); + MOZ_ASSERT(mFrameBlender, "Should have a FrameBlender"); + mFrameBlender->ResetAnimation(); mAnim->ResetAnimation(); UpdateImageContainer(); @@ -1450,7 +1425,8 @@ RasterImage::SetLoopCount(int32_t aLoopCount) if (mAnim) { // No need to set this if we're not an animation - mFrameBlender.SetLoopCount(aLoopCount); + MOZ_ASSERT(mFrameBlender, "Should have a FrameBlender"); + mFrameBlender->SetLoopCount(aLoopCount); } } @@ -1489,18 +1465,26 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount) // This needs to happen just before we start getting EnsureFrame() call(s), // so that there's no gap for anything to miss us. if (mMultipart && (!mDecoder || mDecoder->BytesDecoded() == 0)) { - // Our previous state may have been animated, so let's clean up - if (mAnimating) + // Our previous state may have been animated, so let's clean up. + if (mAnimating) { StopAnimation(); + } mAnimationFinished = false; + mPendingAnimation = false; if (mAnim) { mAnim = nullptr; } - // If there's only one frame, this could cause flickering - int old_frame_count = GetNumFrames(); - if (old_frame_count > 1) { - mFrameBlender.ClearFrames(); + + // If we had a FrameBlender, clean it up. We'll hold on to the first frame + // so we have something to draw until the next frame is decoded. + if (mFrameBlender) { + nsRefPtr firstFrame = mFrameBlender->RawGetFrame(0); + mMultipartDecodedFrame = firstFrame->DrawableRef(); + mFrameBlender.reset(); } + + // Remove everything stored in the surface cache for this image. + SurfaceCache::RemoveImage(ImageKey(this)); } // If we're not storing source data and we've previously gotten the size, @@ -1693,6 +1677,7 @@ RasterImage::OnNewSourceData() mDecoded = false; mHasSourceData = false; mHasSize = false; + mHasFirstFrame = false; mWantFullDecode = true; mDecodeStatus = DecodeStatus::INACTIVE; @@ -1771,12 +1756,12 @@ RasterImage::GetKeys(uint32_t *count, char ***keys) } void -RasterImage::Discard(bool force) +RasterImage::Discard(bool aForce, bool aNotify) { MOZ_ASSERT(NS_IsMainThread()); // We should be ok for discard - NS_ABORT_IF_FALSE(force ? CanForciblyDiscard() : CanDiscard(), "Asked to discard but can't!"); + NS_ABORT_IF_FALSE(aForce ? CanForciblyDiscard() : CanDiscard(), "Asked to discard but can't!"); // We should never discard when we have an active decoder NS_ABORT_IF_FALSE(!mDecoder, "Asked to discard with open decoder!"); @@ -1789,22 +1774,26 @@ RasterImage::Discard(bool force) int old_frame_count = GetNumFrames(); // Delete all the decoded frames - mFrameBlender.Discard(); + mFrameBlender.reset(); + SurfaceCache::RemoveImage(ImageKey(this)); // Clear the last decoded multipart frame. - mMultipartDecodedFrame = nullptr; + mMultipartDecodedFrame.reset(); // Flag that we no longer have decoded frames for this image mDecoded = false; + mHasFirstFrame = false; // Notify that we discarded - if (mProgressTracker) + if (aNotify && mProgressTracker) { mProgressTracker->OnDiscard(); + } mDecodeStatus = DecodeStatus::INACTIVE; - if (force) + if (aForce) { DiscardTracker::Remove(&mDiscardTrackerNode); + } // Log PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG, @@ -1834,14 +1823,12 @@ RasterImage::CanDiscard() { bool RasterImage::CanForciblyDiscard() { - return mDiscardable && // ...Enabled at creation time... - mHasSourceData; // ...have the source data... + return mHasSourceData; // ...have the source data... } bool RasterImage::CanForciblyDiscardAndRedecode() { - return mDiscardable && // ...Enabled at creation time... - mHasSourceData && // ...have the source data... + return mHasSourceData && // ...have the source data... !mDecoder && // Can't discard with an open decoder !mAnim; // Can never discard animated images } @@ -1857,7 +1844,7 @@ RasterImage::DiscardingActive() { // or just writing it directly to the decoder bool RasterImage::StoringSourceData() const { - return (mDecodeOnDraw || mDiscardable); + return !mMultipart; } @@ -2431,26 +2418,15 @@ RasterImage::DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef, // By now we may have a frame with the requested size. If not, we need to // adjust the drawing parameters accordingly. - nsIntRect finalFrameRect = frameRef->GetRect(); - if (finalFrameRect.Size() != aSize) { - gfx::Size scale(double(aSize.width) / mSize.width, - double(aSize.height) / mSize.height); + IntSize finalSize = frameRef->GetImageSize(); + if (ThebesIntSize(finalSize) != aSize) { + gfx::Size scale(double(aSize.width) / finalSize.width, + double(aSize.height) / finalSize.height); aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height)); region.Scale(1.0 / scale.width, 1.0 / scale.height); } - // We can only use padding if we're using the original |aFrameRef|, unscaled. - // (If so, we moved it into |frameRef|, so |aFrameRef| is empty.) Because of - // this restriction, we don't scale frames that require padding. - nsIntMargin padding(0, 0, 0, 0); - if (!aFrameRef) { - padding = nsIntMargin(finalFrameRect.y, - mSize.width - finalFrameRect.XMost(), - mSize.height - finalFrameRect.YMost(), - finalFrameRect.x); - } - - frameRef->Draw(aContext, region, padding, aFilter, aFlags); + frameRef->Draw(aContext, region, aFilter, aFlags); } //****************************************************************************** @@ -2528,13 +2504,20 @@ 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), - aFlags); + mSize, aFlags); if (!ref) { - return NS_OK; // Getting the frame (above) touches the image and kicks off decoding + // Getting the frame (above) touches the image and kicks off decoding. + return NS_OK; } - 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; @@ -2562,6 +2545,11 @@ 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; } @@ -2587,6 +2575,11 @@ 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 @@ -2974,7 +2967,8 @@ 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), aFlags); + frameRef = LookupFrame(GetRequestedFrameIndex(aWhichFrame), + mSize, aFlags); if (frameRef) { RequestScale(frameRef.get(), aFlags, destSize); } diff --git a/image/src/RasterImage.h b/image/src/RasterImage.h index 5d4e974032d..f4b017dc88b 100644 --- a/image/src/RasterImage.h +++ b/image/src/RasterImage.h @@ -29,6 +29,7 @@ #include "DiscardTracker.h" #include "Orientation.h" #include "nsIObserver.h" +#include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" #include "mozilla/ReentrantMonitor.h" #include "mozilla/TimeStamp.h" @@ -180,12 +181,10 @@ public: } /* Triggers discarding. */ - void Discard(bool force = false); - void ForceDiscard() { Discard(/* force = */ true); } + void Discard(bool aForce = false, bool aNotify = true); + void ForceDiscard() { Discard(/* aForce = */ 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. @@ -194,33 +193,21 @@ public: /** * Ensures that a given frame number exists with the given parameters, and - * returns pointers to the data storage for that frame. + * returns a RawAccessFrameRef for that frame. * It is not possible to create sparse frame arrays; you can only append - * frames to the current frame array. + * 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. */ - 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); + RawAccessFrameRef EnsureFrame(uint32_t aFrameNum, + const nsIntRect& aFrameRect, + uint32_t aDecodeFlags, + gfx::SurfaceFormat aFormat, + uint8_t aPaletteDepth, + imgFrame* aPreviousFrame); /* notification that the entire image has been decoded */ - nsresult DecodingComplete(); + void DecodingComplete(imgFrame* aFinalFrame); /** * Number of times to loop the image. @@ -314,8 +301,13 @@ private: uint32_t aFlags, bool aShouldSyncNotify = true); - already_AddRefed LookupFrameNoDecode(uint32_t aFrameNum); - DrawableFrameRef LookupFrame(uint32_t aFrameNum, uint32_t aFlags, bool aShouldSyncNotify = true); + DrawableFrameRef LookupFrameInternal(uint32_t aFrameNum, + const nsIntSize& aSize, + uint32_t aFlags); + DrawableFrameRef LookupFrame(uint32_t aFrameNum, + const nsIntSize& aSize, + uint32_t aFlags, + bool aShouldSyncNotify = true); uint32_t GetCurrentFrameIndex() const; uint32_t GetRequestedFrameIndex(uint32_t aWhichFrame) const; @@ -324,18 +316,12 @@ private: size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation, MallocSizeOf aMallocSizeOf) const; - void EnsureAnimExists(); - - 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); - + RawAccessFrameRef InternalAddFrame(uint32_t aFrameNum, + const nsIntRect& aFrameRect, + uint32_t aDecodeFlags, + gfx::SurfaceFormat aFormat, + uint8_t aPaletteDepth, + imgFrame* aPreviousFrame); nsresult DoImageDataComplete(); bool ApplyDecodeFlags(uint32_t aNewFlags); @@ -370,11 +356,11 @@ private: // data // and imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION. uint32_t mFrameDecodeFlags; - //! All the frames of the image - FrameBlender mFrameBlender; + //! All the frames of the image. + Maybe mFrameBlender; - // The last frame we decoded for multipart images. - nsRefPtr mMultipartDecodedFrame; + //! The last frame we decoded for multipart images. + DrawableFrameRef mMultipartDecodedFrame; nsCOMPtr mProperties; @@ -439,8 +425,13 @@ private: // data // Do we have the frames in decoded form? bool mDecoded:1; + bool mHasFirstFrame:1; bool mHasBeenDecoded: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 diff --git a/image/src/imgFrame.cpp b/image/src/imgFrame.cpp index fcc0015b805..094f698a974 100644 --- a/image/src/imgFrame.cpp +++ b/image/src/imgFrame.cpp @@ -111,6 +111,23 @@ static bool AllowedImageSize(int32_t aWidth, int32_t aHeight) return true; } +static bool AllowedImageAndFrameDimensions(const nsIntSize& aImageSize, + const nsIntRect& aFrameRect) +{ + if (!AllowedImageSize(aImageSize.width, aImageSize.height)) { + return false; + } + if (!AllowedImageSize(aFrameRect.width, aFrameRect.height)) { + return false; + } + nsIntRect imageRect(0, 0, aImageSize.width, aImageSize.height); + if (!imageRect.Contains(aFrameRect)) { + return false; + } + return true; +} + + imgFrame::imgFrame() : mDecoded(0, 0, 0, 0), mDecodedMutex("imgFrame::mDecoded"), @@ -123,7 +140,6 @@ imgFrame::imgFrame() : mCompositingFailed(false), mHasNoAlpha(false), mNonPremult(false), - mDiscardable(false), mOptimizable(false), mInformedDiscardTracker(false) { @@ -147,17 +163,19 @@ imgFrame::~imgFrame() } nsresult -imgFrame::InitForDecoder(const nsIntRect& aRect, +imgFrame::InitForDecoder(const nsIntSize& aImageSize, + const nsIntRect& aRect, SurfaceFormat aFormat, uint8_t aPaletteDepth /* = 0 */) { // Assert for properties that should be verified by decoders, // warn for properties related to bad content. - if (!AllowedImageSize(aRect.width, aRect.height)) { + if (!AllowedImageAndFrameDimensions(aImageSize, aRect)) { NS_WARNING("Should have legal image size"); return NS_ERROR_FAILURE; } + mImageSize = aImageSize.ToIntSize(); mOffset.MoveTo(aRect.x, aRect.y); mSize.SizeTo(aRect.width, aRect.height); @@ -221,6 +239,7 @@ imgFrame::InitWithDrawable(gfxDrawable* aDrawable, return NS_ERROR_FAILURE; } + mImageSize = aSize.ToIntSize(); mOffset.MoveTo(0, 0); mSize.SizeTo(aSize.width, aSize.height); @@ -415,9 +434,7 @@ 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. - if (mDiscardable) { - mImageSurface = nullptr; - } + mImageSurface = nullptr; #endif return NS_OK; @@ -492,8 +509,7 @@ imgFrame::SurfaceForDrawing(bool aDoPadding, } bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion, - const nsIntMargin& aPadding, GraphicsFilter aFilter, - uint32_t aImageFlags) + GraphicsFilter aFilter, uint32_t aImageFlags) { PROFILER_LABEL("imgFrame", "Draw", js::ProfileEntry::Category::GRAPHICS); @@ -504,7 +520,12 @@ bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion, "We must be allowed to sample *some* source pixels!"); NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!"); - bool doPadding = aPadding != nsIntMargin(0,0,0,0); + 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 = !ImageComplete(); if (mSinglePixel && !doPadding && !doPartialDecode) { @@ -519,14 +540,12 @@ bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion, return true; } - gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(), - mSize.height + aPadding.TopBottom()); - RefPtr surf = GetSurface(); if (!surf && !mSinglePixel) { return false; } + gfxRect imageRect(0, 0, mImageSize.width, mImageSize.height); bool doTile = !imageRect.Contains(aRegion.Rect()) && !(aImageFlags & imgIContainer::FLAG_CLAMP); ImageRegion region(aRegion); @@ -539,7 +558,7 @@ bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion, gfxContextMatrixAutoSaveRestore autoSR(aContext); SurfaceWithFormat surfaceResult = SurfaceForDrawing(doPadding, doPartialDecode, doTile, aContext, - aPadding, imageRect, region, surf); + padding, imageRect, region, surf); if (surfaceResult.IsValid()) { gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable, @@ -579,34 +598,9 @@ imgFrame::GetStride() const return VolatileSurfaceStride(mSize, mFormat); } -<<<<<<< found SurfaceFormat imgFrame::GetFormat() const { -||||||| expected -bool imgFrame::GetNeedsBackground() const -{ - // We need a background painted if we have alpha or we're incomplete. -======= -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. ->>>>>>> replacement -<<<<<<< found return mFormat; -||||||| expected - return (mFormat == SurfaceFormat::B8G8R8A8 || !ImageComplete()); -======= - if (mFormat == SurfaceFormat::B8G8R8A8 && !mHasNoAlpha) { - return true; - } - - return false; ->>>>>>> replacement } uint32_t imgFrame::GetImageBytesPerRow() const @@ -804,13 +798,6 @@ 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() { diff --git a/image/src/imgFrame.h b/image/src/imgFrame.h index 1014071b4de..8c61039ff00 100644 --- a/image/src/imgFrame.h +++ b/image/src/imgFrame.h @@ -44,7 +44,8 @@ public: * when drawing content into an imgFrame, as it may use a different graphics * backend than normal content drawing. */ - nsresult InitForDecoder(const nsIntRect& aRect, + nsresult InitForDecoder(const nsIntSize& aImageSize, + const nsIntRect& aRect, SurfaceFormat aFormat, uint8_t aPaletteDepth = 0); @@ -52,7 +53,7 @@ public: SurfaceFormat aFormat, uint8_t aPaletteDepth = 0) { - return InitForDecoder(nsIntRect(0, 0, aSize.width, aSize.height), + return InitForDecoder(aSize, nsIntRect(0, 0, aSize.width, aSize.height), aFormat, aPaletteDepth); } @@ -77,11 +78,11 @@ public: RawAccessFrameRef RawAccessRef(); bool Draw(gfxContext* aContext, const ImageRegion& aRegion, - const nsIntMargin& aPadding, GraphicsFilter aFilter, - uint32_t aImageFlags); + GraphicsFilter aFilter, uint32_t aImageFlags); nsresult ImageUpdated(const nsIntRect &aUpdateRect); + IntSize GetImageSize() { return mImageSize; } nsIntRect GetRect() const; IntSize GetSize() const { return mSize; } bool NeedsPadding() const { return mOffset != nsIntPoint(0, 0); } @@ -112,7 +113,6 @@ public: bool GetCompositingFailed() const; void SetCompositingFailed(bool val); - void SetDiscardable(); void SetOptimizable(); TemporaryRef GetSurface(); @@ -173,6 +173,7 @@ private: // data RefPtr mImageSurface; RefPtr mOptSurface; + IntSize mImageSize; IntSize mSize; nsIntPoint mOffset; @@ -205,7 +206,6 @@ private: // data bool mCompositingFailed; bool mHasNoAlpha; bool mNonPremult; - bool mDiscardable; bool mOptimizable; /** Have we called DiscardTracker::InformAllocation()? */