diff --git a/image/src/imgFrame.cpp b/image/src/imgFrame.cpp index 77c89f7776b7..66b85c93fa37 100644 --- a/image/src/imgFrame.cpp +++ b/image/src/imgFrame.cpp @@ -312,6 +312,18 @@ nsresult imgFrame::Optimize() return NS_OK; } +DrawableFrameRef +imgFrame::DrawableRef() +{ + return DrawableFrameRef(this); +} + +RawAccessFrameRef +imgFrame::RawAccessRef() +{ + return RawAccessFrameRef(this); +} + imgFrame::SurfaceWithFormat imgFrame::SurfaceForDrawing(bool aDoPadding, bool aDoPartialDecode, diff --git a/image/src/imgFrame.h b/image/src/imgFrame.h index 9db2e59a7a41..a8ae60b0caaa 100644 --- a/image/src/imgFrame.h +++ b/image/src/imgFrame.h @@ -8,6 +8,7 @@ #define imgFrame_h #include "mozilla/MemoryReporting.h" +#include "mozilla/Move.h" #include "mozilla/Mutex.h" #include "mozilla/VolatileBuffer.h" #include "gfxDrawable.h" @@ -17,6 +18,8 @@ namespace mozilla { namespace image { class ImageRegion; +class DrawableFrameRef; +class RawAccessFrameRef; class imgFrame { @@ -36,6 +39,9 @@ public: nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, SurfaceFormat aFormat, uint8_t aPaletteDepth = 0); nsresult Optimize(); + DrawableFrameRef DrawableRef(); + RawAccessFrameRef RawAccessRef(); + bool Draw(gfxContext* aContext, const ImageRegion& aRegion, const nsIntMargin& aPadding, GraphicsFilter aFilter, uint32_t aImageFlags); @@ -165,32 +171,166 @@ private: // data /** Have we called DiscardTracker::InformAllocation()? */ bool mInformedDiscardTracker; + + friend class DrawableFrameRef; + friend class RawAccessFrameRef; }; - // An RAII class to ensure it's easy to balance locks and unlocks on - // imgFrames. - class AutoFrameLocker - { - public: - explicit AutoFrameLocker(imgFrame* frame) - : mFrame(frame) - , mSucceeded(NS_SUCCEEDED(frame->LockImageData())) - {} +/** + * A reference to an imgFrame that holds the imgFrame's surface in memory, + * allowing drawing. If you have a DrawableFrameRef |ref| and |if (ref)| returns + * true, then calls to Draw() and GetSurface() are guaranteed to succeed. + */ +class DrawableFrameRef MOZ_FINAL +{ + // Implementation details for safe boolean conversion. + typedef void (DrawableFrameRef::* ConvertibleToBool)(float*****, double*****); + void nonNull(float*****, double*****) {} - ~AutoFrameLocker() - { - if (mSucceeded) { - mFrame->UnlockImageData(); - } +public: + DrawableFrameRef() { } + + explicit DrawableFrameRef(imgFrame* aFrame) + : mFrame(aFrame) + , mRef(aFrame->mVBuf) + { + if (mRef.WasBufferPurged()) { + mFrame = nullptr; + mRef = nullptr; + } + } + + DrawableFrameRef(DrawableFrameRef&& aOther) + : mFrame(aOther.mFrame.forget()) + , mRef(Move(aOther.mRef)) + { } + + DrawableFrameRef& operator=(DrawableFrameRef&& aOther) + { + MOZ_ASSERT(this != &aOther, "Self-moves are prohibited"); + mFrame = aOther.mFrame.forget(); + mRef = Move(aOther.mRef); + return *this; + } + + operator ConvertibleToBool() const + { + return bool(mFrame) ? &DrawableFrameRef::nonNull : 0; + } + + imgFrame* operator->() + { + MOZ_ASSERT(mFrame); + return mFrame; + } + + const imgFrame* operator->() const + { + MOZ_ASSERT(mFrame); + return mFrame; + } + + imgFrame* get() { return mFrame; } + const imgFrame* get() const { return mFrame; } + + void reset() + { + mFrame = nullptr; + mRef = nullptr; + } + +private: + nsRefPtr mFrame; + VolatileBufferPtr mRef; +}; + +/** + * A reference to an imgFrame that holds the imgFrame's surface in memory in a + * format appropriate for access as raw data. If you have a RawAccessFrameRef + * |ref| and |if (ref)| is true, then calls to GetImageData(), GetPaletteData(), + * and GetDrawTarget() are guaranteed to succeed. This guarantee is stronger + * than DrawableFrameRef, so everything that a valid DrawableFrameRef guarantees + * is also guaranteed by a valid RawAccessFrameRef. + * + * This may be considerably more expensive than is necessary just for drawing, + * so only use this when you need to read or write the raw underlying image data + * that the imgFrame holds. + */ +class RawAccessFrameRef MOZ_FINAL +{ + // Implementation details for safe boolean conversion. + typedef void (RawAccessFrameRef::* ConvertibleToBool)(float*****, double*****); + void nonNull(float*****, double*****) {} + +public: + RawAccessFrameRef() { } + + explicit RawAccessFrameRef(imgFrame* aFrame) + : mFrame(aFrame) + { + MOZ_ASSERT(mFrame, "Need a frame"); + + if (NS_FAILED(mFrame->LockImageData())) { + mFrame->UnlockImageData(); + mFrame = nullptr; + } + } + + RawAccessFrameRef(RawAccessFrameRef&& aOther) + : mFrame(aOther.mFrame.forget()) + { } + + ~RawAccessFrameRef() + { + if (mFrame) { + mFrame->UnlockImageData(); + } + } + + RawAccessFrameRef& operator=(RawAccessFrameRef&& aOther) + { + MOZ_ASSERT(this != &aOther, "Self-moves are prohibited"); + + if (mFrame) { + mFrame->UnlockImageData(); } - // Whether the lock request succeeded. - bool Succeeded() { return mSucceeded; } + mFrame = aOther.mFrame.forget(); - private: - nsRefPtr mFrame; - bool mSucceeded; - }; + return *this; + } + + operator ConvertibleToBool() const + { + return bool(mFrame) ? &RawAccessFrameRef::nonNull : 0; + } + + imgFrame* operator->() + { + MOZ_ASSERT(mFrame); + return mFrame.get(); + } + + const imgFrame* operator->() const + { + MOZ_ASSERT(mFrame); + return mFrame; + } + + imgFrame* get() { return mFrame; } + const imgFrame* get() const { return mFrame; } + + void reset() + { + if (mFrame) { + mFrame->UnlockImageData(); + } + mFrame = nullptr; + } + +private: + nsRefPtr mFrame; +}; } // namespace image } // namespace mozilla