summaryrefslogtreecommitdiffstats
path: root/image/ISurfaceProvider.h
diff options
context:
space:
mode:
Diffstat (limited to 'image/ISurfaceProvider.h')
-rw-r--r--image/ISurfaceProvider.h321
1 files changed, 321 insertions, 0 deletions
diff --git a/image/ISurfaceProvider.h b/image/ISurfaceProvider.h
new file mode 100644
index 0000000000..5bb3a26a9b
--- /dev/null
+++ b/image/ISurfaceProvider.h
@@ -0,0 +1,321 @@
+/* -*- 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/. */
+
+/**
+ * An interface for objects which can either store a surface or dynamically
+ * generate one, and various implementations.
+ */
+
+#ifndef mozilla_image_ISurfaceProvider_h
+#define mozilla_image_ISurfaceProvider_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/NotNull.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/gfx/2D.h"
+
+#include "imgFrame.h"
+#include "SurfaceCache.h"
+
+namespace mozilla {
+namespace image {
+
+class CachedSurface;
+class DrawableSurface;
+
+/**
+ * An interface for objects which can either store a surface or dynamically
+ * generate one.
+ */
+class ISurfaceProvider {
+ public:
+ // Subclasses may or may not be XPCOM classes, so we just require that they
+ // implement AddRef and Release.
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ /// @return key data used for identifying which image this ISurfaceProvider is
+ /// associated with in the surface cache.
+ ImageKey GetImageKey() const { return mImageKey; }
+
+ /// @return key data used to uniquely identify this ISurfaceProvider's cache
+ /// entry in the surface cache.
+ const SurfaceKey& GetSurfaceKey() const { return mSurfaceKey; }
+
+ /// @return a (potentially lazily computed) drawable reference to a surface.
+ virtual DrawableSurface Surface();
+
+ /// @return true if DrawableRef() will return a completely decoded surface.
+ virtual bool IsFinished() const = 0;
+
+ /// @return true if the underlying decoder is currently fully decoded. For
+ /// animated images, this means that at least every frame has been decoded
+ /// at least once. It does not guarantee that all of the frames are present,
+ /// as the surface provider has the option to discard as it deems necessary.
+ virtual bool IsFullyDecoded() const { return IsFinished(); }
+
+ /// @return the number of bytes of memory this ISurfaceProvider is expected to
+ /// require. Optimizations may result in lower real memory usage. Trivial
+ /// overhead is ignored. Because this value is used in bookkeeping, it's
+ /// important that it be constant over the lifetime of this object.
+ virtual size_t LogicalSizeInBytes() const = 0;
+
+ typedef imgFrame::AddSizeOfCbData AddSizeOfCbData;
+ typedef imgFrame::AddSizeOfCb AddSizeOfCb;
+
+ /// @return the actual number of bytes of memory this ISurfaceProvider is
+ /// using. May vary over the lifetime of the ISurfaceProvider. The default
+ /// implementation is appropriate for static ISurfaceProviders.
+ virtual void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+ const AddSizeOfCb& aCallback) {
+ DrawableFrameRef ref = DrawableRef(/* aFrame = */ 0);
+ if (!ref) {
+ return;
+ }
+
+ ref->AddSizeOfExcludingThis(aMallocSizeOf, aCallback);
+ }
+
+ virtual void Reset() {}
+ virtual void Advance(size_t aFrame) {}
+
+ /// @return the availability state of this ISurfaceProvider, which indicates
+ /// whether DrawableRef() could successfully return a surface. Should only be
+ /// called from SurfaceCache code as it relies on SurfaceCache for
+ /// synchronization.
+ AvailabilityState& Availability() { return mAvailability; }
+ const AvailabilityState& Availability() const { return mAvailability; }
+
+ protected:
+ ISurfaceProvider(const ImageKey aImageKey, const SurfaceKey& aSurfaceKey,
+ AvailabilityState aAvailability)
+ : mImageKey(aImageKey),
+ mSurfaceKey(aSurfaceKey),
+ mAvailability(aAvailability) {
+ MOZ_ASSERT(aImageKey, "Must have a valid image key");
+ }
+
+ virtual ~ISurfaceProvider() {}
+
+ /// @return an eagerly computed drawable reference to a surface. For
+ /// dynamically generated animation surfaces, @aFrame specifies the 0-based
+ /// index of the desired frame.
+ virtual DrawableFrameRef DrawableRef(size_t aFrame) = 0;
+
+ /// @return an imgFrame at the 0-based index of the desired frame, as
+ /// specified by @aFrame. Only applies for animated images.
+ virtual already_AddRefed<imgFrame> GetFrame(size_t aFrame) {
+ MOZ_ASSERT_UNREACHABLE("Surface provider does not support direct access!");
+ return nullptr;
+ }
+
+ /// @return true if this ISurfaceProvider is locked. (@see SetLocked())
+ /// Should only be called from SurfaceCache code as it relies on SurfaceCache
+ /// for synchronization.
+ virtual bool IsLocked() const = 0;
+
+ /// If @aLocked is true, hint that this ISurfaceProvider is in use and it
+ /// should avoid releasing its resources. Should only be called from
+ /// SurfaceCache code as it relies on SurfaceCache for synchronization.
+ virtual void SetLocked(bool aLocked) = 0;
+
+ private:
+ friend class CachedSurface;
+ friend class DrawableSurface;
+
+ const ImageKey mImageKey;
+ const SurfaceKey mSurfaceKey;
+ AvailabilityState mAvailability;
+};
+
+/**
+ * A reference to a surface (stored in an imgFrame) that holds the surface in
+ * memory, guaranteeing that it can be drawn. If you have a DrawableSurface
+ * |surf| and |if (surf)| returns true, then calls to |surf->Draw()| and
+ * |surf->GetSourceSurface()| are guaranteed to succeed.
+ *
+ * Note that the surface may be computed lazily, so a DrawableSurface should not
+ * be dereferenced (i.e., operator->() should not be called) until you're
+ * sure that you want to draw it.
+ */
+class MOZ_STACK_CLASS DrawableSurface final {
+ public:
+ DrawableSurface() : mHaveSurface(false) {}
+
+ explicit DrawableSurface(DrawableFrameRef&& aDrawableRef)
+ : mDrawableRef(std::move(aDrawableRef)),
+ mHaveSurface(bool(mDrawableRef)) {}
+
+ explicit DrawableSurface(NotNull<ISurfaceProvider*> aProvider)
+ : mProvider(aProvider), mHaveSurface(true) {}
+
+ DrawableSurface(DrawableSurface&& aOther)
+ : mDrawableRef(std::move(aOther.mDrawableRef)),
+ mProvider(std::move(aOther.mProvider)),
+ mHaveSurface(aOther.mHaveSurface) {
+ aOther.mHaveSurface = false;
+ }
+
+ DrawableSurface& operator=(DrawableSurface&& aOther) {
+ MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
+ mDrawableRef = std::move(aOther.mDrawableRef);
+ mProvider = std::move(aOther.mProvider);
+ mHaveSurface = aOther.mHaveSurface;
+ aOther.mHaveSurface = false;
+ return *this;
+ }
+
+ /**
+ * If this DrawableSurface is dynamically generated from an animation, attempt
+ * to seek to frame @aFrame, where @aFrame is a 0-based index into the frames
+ * of the animation. Otherwise, nothing will blow up at runtime, but we assert
+ * in debug builds, since calling this in an unexpected situation probably
+ * indicates a bug.
+ *
+ * @return a successful result if we could obtain frame @aFrame. Note that
+ * |mHaveSurface| being true means that we're guaranteed to have *some* frame,
+ * so the caller can dereference this DrawableSurface even if Seek() fails,
+ * but while nothing will blow up, the frame won't be the one they expect.
+ */
+ nsresult Seek(size_t aFrame) {
+ MOZ_ASSERT(mHaveSurface, "Trying to seek an empty DrawableSurface?");
+
+ if (!mProvider) {
+ MOZ_ASSERT_UNREACHABLE("Trying to seek a static DrawableSurface?");
+ return NS_ERROR_FAILURE;
+ }
+
+ mDrawableRef = mProvider->DrawableRef(aFrame);
+
+ return mDrawableRef ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ already_AddRefed<imgFrame> GetFrame(size_t aFrame) {
+ MOZ_ASSERT(mHaveSurface, "Trying to get on an empty DrawableSurface?");
+
+ if (!mProvider) {
+ MOZ_ASSERT_UNREACHABLE("Trying to get on a static DrawableSurface?");
+ return nullptr;
+ }
+
+ return mProvider->GetFrame(aFrame);
+ }
+
+ void Reset() {
+ if (!mProvider) {
+ MOZ_ASSERT_UNREACHABLE("Trying to reset a static DrawableSurface?");
+ return;
+ }
+
+ mProvider->Reset();
+ }
+
+ void Advance(size_t aFrame) {
+ if (!mProvider) {
+ MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?");
+ return;
+ }
+
+ mProvider->Advance(aFrame);
+ }
+
+ bool IsFullyDecoded() const {
+ if (!mProvider) {
+ MOZ_ASSERT_UNREACHABLE(
+ "Trying to check decoding state of a static DrawableSurface?");
+ return false;
+ }
+
+ return mProvider->IsFullyDecoded();
+ }
+
+ explicit operator bool() const { return mHaveSurface; }
+ imgFrame* operator->() { return DrawableRef().get(); }
+
+ private:
+ DrawableSurface(const DrawableSurface& aOther) = delete;
+ DrawableSurface& operator=(const DrawableSurface& aOther) = delete;
+
+ DrawableFrameRef& DrawableRef() {
+ MOZ_ASSERT(mHaveSurface);
+
+ // If we weren't created with a DrawableFrameRef directly, we should've been
+ // created with an ISurfaceProvider which can give us one. Note that if
+ // Seek() has been called, we'll already have a DrawableFrameRef, so we
+ // won't need to get one here.
+ if (!mDrawableRef) {
+ MOZ_ASSERT(mProvider);
+ mDrawableRef = mProvider->DrawableRef(/* aFrame = */ 0);
+ }
+
+ MOZ_ASSERT(mDrawableRef);
+ return mDrawableRef;
+ }
+
+ DrawableFrameRef mDrawableRef;
+ RefPtr<ISurfaceProvider> mProvider;
+ bool mHaveSurface;
+};
+
+// Surface() is implemented here so that DrawableSurface's definition is
+// visible. This default implementation eagerly obtains a DrawableFrameRef for
+// the first frame and is intended for static ISurfaceProviders.
+inline DrawableSurface ISurfaceProvider::Surface() {
+ return DrawableSurface(DrawableRef(/* aFrame = */ 0));
+}
+
+/**
+ * An ISurfaceProvider that stores a single surface.
+ */
+class SimpleSurfaceProvider final : public ISurfaceProvider {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleSurfaceProvider, override)
+
+ SimpleSurfaceProvider(const ImageKey aImageKey, const SurfaceKey& aSurfaceKey,
+ NotNull<imgFrame*> aSurface)
+ : ISurfaceProvider(aImageKey, aSurfaceKey,
+ AvailabilityState::StartAvailable()),
+ mSurface(aSurface) {
+ MOZ_ASSERT(aSurfaceKey.Size() == mSurface->GetSize());
+ }
+
+ bool IsFinished() const override { return mSurface->IsFinished(); }
+
+ size_t LogicalSizeInBytes() const override {
+ gfx::IntSize size = mSurface->GetSize();
+ return size.width * size.height * mSurface->GetBytesPerPixel();
+ }
+
+ protected:
+ DrawableFrameRef DrawableRef(size_t aFrame) override {
+ MOZ_ASSERT(aFrame == 0,
+ "Requesting an animation frame from a SimpleSurfaceProvider?");
+ return mSurface->DrawableRef();
+ }
+
+ bool IsLocked() const override { return bool(mLockRef); }
+
+ void SetLocked(bool aLocked) override {
+ if (aLocked == IsLocked()) {
+ return; // Nothing changed.
+ }
+
+ // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep
+ // any volatile buffer it owns in memory.
+ mLockRef = aLocked ? mSurface->DrawableRef() : DrawableFrameRef();
+ }
+
+ private:
+ virtual ~SimpleSurfaceProvider() {}
+
+ NotNull<RefPtr<imgFrame>> mSurface;
+ DrawableFrameRef mLockRef;
+};
+
+} // namespace image
+} // namespace mozilla
+
+#endif // mozilla_image_ISurfaceProvider_h