diff options
Diffstat (limited to 'image/imgFrame.h')
-rw-r--r-- | image/imgFrame.h | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/image/imgFrame.h b/image/imgFrame.h new file mode 100644 index 0000000000..95f219f93e --- /dev/null +++ b/image/imgFrame.h @@ -0,0 +1,421 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * 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 mozilla_image_imgFrame_h +#define mozilla_image_imgFrame_h + +#include <functional> +#include <utility> + +#include "AnimationParams.h" +#include "MainThreadUtils.h" +#include "gfxDrawable.h" +#include "mozilla/layers/SourceSurfaceSharedData.h" +#include "mozilla/Maybe.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/Monitor.h" +#include "nsRect.h" + +namespace mozilla { +namespace image { + +class ImageRegion; +class DrawableFrameRef; +class RawAccessFrameRef; + +enum class Opacity : uint8_t { FULLY_OPAQUE, SOME_TRANSPARENCY }; + +class imgFrame { + typedef gfx::SourceSurfaceSharedData SourceSurfaceSharedData; + typedef gfx::DrawTarget DrawTarget; + typedef gfx::SamplingFilter SamplingFilter; + typedef gfx::IntPoint IntPoint; + typedef gfx::IntRect IntRect; + typedef gfx::IntSize IntSize; + typedef gfx::SourceSurface SourceSurface; + typedef gfx::SurfaceFormat SurfaceFormat; + + public: + MOZ_DECLARE_REFCOUNTED_TYPENAME(imgFrame) + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgFrame) + + imgFrame(); + + /** + * Initialize this imgFrame with an empty surface and prepare it for being + * written to by a decoder. + * + * This is appropriate for use with decoded images, but it should not be used + * when drawing content into an imgFrame, as it may use a different graphics + * backend than normal content drawing. + */ + nsresult InitForDecoder(const nsIntSize& aImageSize, SurfaceFormat aFormat, + bool aNonPremult, + const Maybe<AnimationParams>& aAnimParams, + bool aShouldRecycle); + + /** + * Reinitialize this imgFrame with the new parameters, but otherwise retain + * the underlying buffer. + * + * This is appropriate for use with animated images, where the decoder was + * given an IDecoderFrameRecycler object which may yield a recycled imgFrame + * that was discarded to save memory. + */ + nsresult InitForDecoderRecycle(const AnimationParams& aAnimParams); + + /** + * Initialize this imgFrame with a new surface and draw the provided + * gfxDrawable into it. + * + * This is appropriate to use when drawing content into an imgFrame, as it + * uses the same graphics backend as normal content drawing. The downside is + * that the underlying surface may not be stored in a volatile buffer on all + * platforms, and raw access to the surface (using RawAccessRef()) may be much + * more expensive than in the InitForDecoder() case. + * + * aBackend specifies the DrawTarget backend type this imgFrame is supposed + * to be drawn to. + */ + nsresult InitWithDrawable(gfxDrawable* aDrawable, const nsIntSize& aSize, + const SurfaceFormat aFormat, + SamplingFilter aSamplingFilter, + uint32_t aImageFlags, gfx::BackendType aBackend); + + DrawableFrameRef DrawableRef(); + + /** + * Create a RawAccessFrameRef for the frame. + */ + RawAccessFrameRef RawAccessRef(); + + bool Draw(gfxContext* aContext, const ImageRegion& aRegion, + SamplingFilter aSamplingFilter, uint32_t aImageFlags, + float aOpacity); + + 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 aFinalize Finalize the underlying surface (e.g. so that it + * may be marked as read only if possible). + */ + void Finish(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY, + bool aFinalize = true, + bool aOrientationSwapsWidthAndHeight = false); + + /** + * 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 has been aborted. + */ + bool IsAborted() const; + + /** + * Returns true if this imgFrame is completely decoded. + */ + bool IsFinished() 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 WaitUntilFinished() const; + + /** + * Returns the number of bytes per pixel this imgFrame requires. + */ + uint32_t GetBytesPerPixel() const { return 4; } + + const IntSize& GetSize() const { return mImageSize; } + IntRect GetRect() const { return IntRect(IntPoint(0, 0), mImageSize); } + const IntRect& GetBlendRect() const { return mBlendRect; } + IntRect GetBoundedBlendRect() const { + return mBlendRect.Intersect(GetRect()); + } + nsIntRect GetDecodedRect() const { + MonitorAutoLock lock(mMonitor); + return mDecoded; + } + FrameTimeout GetTimeout() const { return mTimeout; } + BlendMethod GetBlendMethod() const { return mBlendMethod; } + DisposalMethod GetDisposalMethod() const { return mDisposalMethod; } + bool FormatHasAlpha() const { return mFormat == SurfaceFormat::OS_RGBA; } + void GetImageData(uint8_t** aData, uint32_t* length) const; + uint8_t* GetImageData() const; + + const IntRect& GetDirtyRect() const { return mDirtyRect; } + void SetDirtyRect(const IntRect& aDirtyRect) { mDirtyRect = aDirtyRect; } + + void FinalizeSurface(); + already_AddRefed<SourceSurface> GetSourceSurface(); + + struct AddSizeOfCbData : public SourceSurface::SizeOfInfo { + AddSizeOfCbData() : mIndex(0), mFinished(false) {} + + size_t mIndex; + bool mFinished; + }; + + typedef std::function<void(AddSizeOfCbData& aMetadata)> AddSizeOfCb; + + void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, + const AddSizeOfCb& aCallback) const; + + private: // methods + ~imgFrame(); + + bool AreAllPixelsWritten() const MOZ_REQUIRES(mMonitor); + nsresult ImageUpdatedInternal(const nsIntRect& aUpdateRect); + void GetImageDataInternal(uint8_t** aData, uint32_t* length) const; + uint32_t GetImageBytesPerRow() const; + uint32_t GetImageDataLength() const; + void FinalizeSurfaceInternal(); + already_AddRefed<SourceSurface> GetSourceSurfaceInternal(); + + struct SurfaceWithFormat { + RefPtr<gfxDrawable> mDrawable; + SurfaceFormat mFormat; + SurfaceWithFormat() : mFormat(SurfaceFormat::UNKNOWN) {} + SurfaceWithFormat(gfxDrawable* aDrawable, SurfaceFormat aFormat) + : mDrawable(aDrawable), mFormat(aFormat) {} + SurfaceWithFormat(SurfaceWithFormat&& aOther) + : mDrawable(std::move(aOther.mDrawable)), mFormat(aOther.mFormat) {} + SurfaceWithFormat& operator=(SurfaceWithFormat&& aOther) { + mDrawable = std::move(aOther.mDrawable); + mFormat = aOther.mFormat; + return *this; + } + SurfaceWithFormat& operator=(const SurfaceWithFormat& aOther) = delete; + SurfaceWithFormat(const SurfaceWithFormat& aOther) = delete; + bool IsValid() { return !!mDrawable; } + }; + + SurfaceWithFormat SurfaceForDrawing(bool aDoPartialDecode, bool aDoTile, + ImageRegion& aRegion, + SourceSurface* aSurface); + + private: // data + friend class DrawableFrameRef; + friend class RawAccessFrameRef; + friend class UnlockImageDataRunnable; + + ////////////////////////////////////////////////////////////////////////////// + // Thread-safe mutable data, protected by mMonitor. + ////////////////////////////////////////////////////////////////////////////// + + mutable Monitor mMonitor; + + /** + * Used for rasterized images, this contains the raw pixel data. + */ + RefPtr<SourceSurfaceSharedData> mRawSurface MOZ_GUARDED_BY(mMonitor); + RefPtr<SourceSurfaceSharedData> mBlankRawSurface MOZ_GUARDED_BY(mMonitor); + + /** + * Used for vector images that were not rasterized directly. This might be a + * blob recording or native surface. + */ + RefPtr<SourceSurface> mOptSurface MOZ_GUARDED_BY(mMonitor); + + nsIntRect mDecoded MOZ_GUARDED_BY(mMonitor); + + bool mAborted MOZ_GUARDED_BY(mMonitor); + bool mFinished MOZ_GUARDED_BY(mMonitor); + bool mShouldRecycle MOZ_GUARDED_BY(mMonitor); + + ////////////////////////////////////////////////////////////////////////////// + // Effectively const data, only mutated in the Init methods. + ////////////////////////////////////////////////////////////////////////////// + + //! The size of the buffer we are decoding to. + IntSize mImageSize; + + //! The contents for the frame, as represented in the encoded image. This may + //! differ from mImageSize because it may be a partial frame. For the first + //! frame, this means we need to shift the data in place, and for animated + //! frames, it likely need to combine with a previous frame to get the full + //! contents. + IntRect mBlendRect; + + //! This is the region that has changed between this frame and the previous + //! frame of an animation. For the first frame, this will be the same as + //! mFrameRect. + IntRect mDirtyRect; + + //! The timeout for this frame. + FrameTimeout mTimeout; + + DisposalMethod mDisposalMethod; + BlendMethod mBlendMethod; + SurfaceFormat mFormat; + + bool mNonPremult; +}; + +/** + * 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 GetSourceSurface() are guaranteed to succeed. + */ +class DrawableFrameRef final { + typedef gfx::DataSourceSurface DataSourceSurface; + + public: + DrawableFrameRef() = default; + + explicit DrawableFrameRef(imgFrame* aFrame) : mFrame(aFrame) { + MOZ_ASSERT(aFrame); + MonitorAutoLock lock(aFrame->mMonitor); + + if (aFrame->mRawSurface) { + mRef.emplace(aFrame->mRawSurface, DataSourceSurface::READ); + if (!mRef->IsMapped()) { + mFrame = nullptr; + mRef.reset(); + } + } else if (!aFrame->mOptSurface || !aFrame->mOptSurface->IsValid()) { + // The optimized surface has become invalid, so we need to redecode. + // For example, on Windows, there may have been a device reset, and + // all D2D surfaces now need to be recreated. + mFrame = nullptr; + } + } + + DrawableFrameRef(DrawableFrameRef&& aOther) + : mFrame(std::move(aOther.mFrame)), mRef(std::move(aOther.mRef)) {} + + DrawableFrameRef& operator=(DrawableFrameRef&& aOther) { + MOZ_ASSERT(this != &aOther, "Self-moves are prohibited"); + mFrame = std::move(aOther.mFrame); + mRef = std::move(aOther.mRef); + return *this; + } + + explicit operator bool() const { return bool(mFrame); } + + 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.reset(); + } + + private: + DrawableFrameRef(const DrawableFrameRef& aOther) = delete; + DrawableFrameRef& operator=(const DrawableFrameRef& aOther) = delete; + + RefPtr<imgFrame> mFrame; + Maybe<DataSourceSurface::ScopedMap> 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() is 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. + * + * Once all an imgFrame's RawAccessFrameRefs go out of scope, new + * RawAccessFrameRefs cannot be created. + */ +class RawAccessFrameRef final { + public: + RawAccessFrameRef() : mData(nullptr) {} + + explicit RawAccessFrameRef(imgFrame* aFrame) + : mFrame(aFrame), mData(nullptr) { + MOZ_ASSERT(mFrame, "Need a frame"); + + mData = mFrame->GetImageData(); + if (!mData) { + mFrame = nullptr; + } + } + + RawAccessFrameRef(RawAccessFrameRef&& aOther) + : mFrame(std::move(aOther.mFrame)), mData(aOther.mData) { + aOther.mData = nullptr; + } + + ~RawAccessFrameRef() = default; + + RawAccessFrameRef& operator=(RawAccessFrameRef&& aOther) { + MOZ_ASSERT(this != &aOther, "Self-moves are prohibited"); + + mFrame = std::move(aOther.mFrame); + mData = aOther.mData; + aOther.mData = nullptr; + + return *this; + } + + explicit operator bool() const { return bool(mFrame); } + + 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() { + mFrame = nullptr; + mData = nullptr; + } + + uint8_t* Data() const { return mData; } + + private: + RawAccessFrameRef(const RawAccessFrameRef& aOther) = delete; + RawAccessFrameRef& operator=(const RawAccessFrameRef& aOther) = delete; + + RefPtr<imgFrame> mFrame; + uint8_t* mData; +}; + +} // namespace image +} // namespace mozilla + +#endif // mozilla_image_imgFrame_h |