1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 * An interface for objects which can either store a surface or dynamically
8 * generate one, and various implementations.
11 #ifndef mozilla_image_ISurfaceProvider_h
12 #define mozilla_image_ISurfaceProvider_h
14 #include "mozilla/Attributes.h"
15 #include "mozilla/Maybe.h"
16 #include "mozilla/MemoryReporting.h"
17 #include "mozilla/NotNull.h"
18 #include "mozilla/TimeStamp.h"
19 #include "mozilla/Variant.h"
20 #include "mozilla/gfx/2D.h"
23 #include "SurfaceCache.h"
29 class DrawableSurface
;
32 * An interface for objects which can either store a surface or dynamically
35 class ISurfaceProvider
{
37 // Subclasses may or may not be XPCOM classes, so we just require that they
38 // implement AddRef and Release.
39 NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
41 /// @return key data used for identifying which image this ISurfaceProvider is
42 /// associated with in the surface cache.
43 ImageKey
GetImageKey() const { return mImageKey
; }
45 /// @return key data used to uniquely identify this ISurfaceProvider's cache
46 /// entry in the surface cache.
47 const SurfaceKey
& GetSurfaceKey() const { return mSurfaceKey
; }
49 /// @return a (potentially lazily computed) drawable reference to a surface.
50 virtual DrawableSurface
Surface();
52 /// @return true if DrawableRef() will return a completely decoded surface.
53 virtual bool IsFinished() const = 0;
55 /// @return true if the underlying decoder is currently fully decoded. For
56 /// animated images, this means that at least every frame has been decoded
57 /// at least once. It does not guarantee that all of the frames are present,
58 /// as the surface provider has the option to discard as it deems necessary.
59 virtual bool IsFullyDecoded() const { return IsFinished(); }
61 /// @return the number of bytes of memory this ISurfaceProvider is expected to
62 /// require. Optimizations may result in lower real memory usage. Trivial
63 /// overhead is ignored. Because this value is used in bookkeeping, it's
64 /// important that it be constant over the lifetime of this object.
65 virtual size_t LogicalSizeInBytes() const = 0;
67 typedef imgFrame::AddSizeOfCbData AddSizeOfCbData
;
68 typedef imgFrame::AddSizeOfCb AddSizeOfCb
;
70 /// @return the actual number of bytes of memory this ISurfaceProvider is
71 /// using. May vary over the lifetime of the ISurfaceProvider. The default
72 /// implementation is appropriate for static ISurfaceProviders.
73 virtual void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
74 const AddSizeOfCb
& aCallback
) {
75 DrawableFrameRef ref
= DrawableRef(/* aFrame = */ 0);
80 ref
->AddSizeOfExcludingThis(aMallocSizeOf
, aCallback
);
83 virtual void Reset() {}
84 virtual void Advance(size_t aFrame
) {}
86 /// @return the availability state of this ISurfaceProvider, which indicates
87 /// whether DrawableRef() could successfully return a surface. Should only be
88 /// called from SurfaceCache code as it relies on SurfaceCache for
90 AvailabilityState
& Availability() { return mAvailability
; }
91 const AvailabilityState
& Availability() const { return mAvailability
; }
94 ISurfaceProvider(const ImageKey aImageKey
, const SurfaceKey
& aSurfaceKey
,
95 AvailabilityState aAvailability
)
96 : mImageKey(aImageKey
),
97 mSurfaceKey(aSurfaceKey
),
98 mAvailability(aAvailability
) {
99 MOZ_ASSERT(aImageKey
, "Must have a valid image key");
102 virtual ~ISurfaceProvider() {}
104 /// @return an eagerly computed drawable reference to a surface. For
105 /// dynamically generated animation surfaces, @aFrame specifies the 0-based
106 /// index of the desired frame.
107 virtual DrawableFrameRef
DrawableRef(size_t aFrame
) = 0;
109 /// @return an imgFrame at the 0-based index of the desired frame, as
110 /// specified by @aFrame. Only applies for animated images.
111 virtual already_AddRefed
<imgFrame
> GetFrame(size_t aFrame
) {
112 MOZ_ASSERT_UNREACHABLE("Surface provider does not support direct access!");
116 /// @return true if this ISurfaceProvider is locked. (@see SetLocked())
117 /// Should only be called from SurfaceCache code as it relies on SurfaceCache
118 /// for synchronization.
119 virtual bool IsLocked() const = 0;
121 /// If @aLocked is true, hint that this ISurfaceProvider is in use and it
122 /// should avoid releasing its resources. Should only be called from
123 /// SurfaceCache code as it relies on SurfaceCache for synchronization.
124 virtual void SetLocked(bool aLocked
) = 0;
127 friend class CachedSurface
;
128 friend class DrawableSurface
;
130 const ImageKey mImageKey
;
131 const SurfaceKey mSurfaceKey
;
132 AvailabilityState mAvailability
;
136 * A reference to a surface (stored in an imgFrame) that holds the surface in
137 * memory, guaranteeing that it can be drawn. If you have a DrawableSurface
138 * |surf| and |if (surf)| returns true, then calls to |surf->Draw()| and
139 * |surf->GetSourceSurface()| are guaranteed to succeed.
141 * Note that the surface may be computed lazily, so a DrawableSurface should not
142 * be dereferenced (i.e., operator->() should not be called) until you're
143 * sure that you want to draw it.
145 class MOZ_STACK_CLASS DrawableSurface final
{
147 DrawableSurface() : mHaveSurface(false) {}
149 explicit DrawableSurface(DrawableFrameRef
&& aDrawableRef
)
150 : mDrawableRef(std::move(aDrawableRef
)),
151 mHaveSurface(bool(mDrawableRef
)) {}
153 explicit DrawableSurface(NotNull
<ISurfaceProvider
*> aProvider
)
154 : mProvider(aProvider
), mHaveSurface(true) {}
156 DrawableSurface(DrawableSurface
&& aOther
)
157 : mDrawableRef(std::move(aOther
.mDrawableRef
)),
158 mProvider(std::move(aOther
.mProvider
)),
159 mHaveSurface(aOther
.mHaveSurface
) {
160 aOther
.mHaveSurface
= false;
163 DrawableSurface
& operator=(DrawableSurface
&& aOther
) {
164 MOZ_ASSERT(this != &aOther
, "Self-moves are prohibited");
165 mDrawableRef
= std::move(aOther
.mDrawableRef
);
166 mProvider
= std::move(aOther
.mProvider
);
167 mHaveSurface
= aOther
.mHaveSurface
;
168 aOther
.mHaveSurface
= false;
173 * If this DrawableSurface is dynamically generated from an animation, attempt
174 * to seek to frame @aFrame, where @aFrame is a 0-based index into the frames
175 * of the animation. Otherwise, nothing will blow up at runtime, but we assert
176 * in debug builds, since calling this in an unexpected situation probably
179 * @return a successful result if we could obtain frame @aFrame. Note that
180 * |mHaveSurface| being true means that we're guaranteed to have *some* frame,
181 * so the caller can dereference this DrawableSurface even if Seek() fails,
182 * but while nothing will blow up, the frame won't be the one they expect.
184 nsresult
Seek(size_t aFrame
) {
185 MOZ_ASSERT(mHaveSurface
, "Trying to seek an empty DrawableSurface?");
188 MOZ_ASSERT_UNREACHABLE("Trying to seek a static DrawableSurface?");
189 return NS_ERROR_FAILURE
;
192 mDrawableRef
= mProvider
->DrawableRef(aFrame
);
194 return mDrawableRef
? NS_OK
: NS_ERROR_FAILURE
;
197 already_AddRefed
<imgFrame
> GetFrame(size_t aFrame
) {
198 MOZ_ASSERT(mHaveSurface
, "Trying to get on an empty DrawableSurface?");
201 MOZ_ASSERT_UNREACHABLE("Trying to get on a static DrawableSurface?");
205 return mProvider
->GetFrame(aFrame
);
210 MOZ_ASSERT_UNREACHABLE("Trying to reset a static DrawableSurface?");
217 void Advance(size_t aFrame
) {
219 MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?");
223 mProvider
->Advance(aFrame
);
226 bool IsFullyDecoded() const {
228 MOZ_ASSERT_UNREACHABLE(
229 "Trying to check decoding state of a static DrawableSurface?");
233 return mProvider
->IsFullyDecoded();
236 explicit operator bool() const { return mHaveSurface
; }
237 imgFrame
* operator->() { return DrawableRef().get(); }
240 DrawableSurface(const DrawableSurface
& aOther
) = delete;
241 DrawableSurface
& operator=(const DrawableSurface
& aOther
) = delete;
243 DrawableFrameRef
& DrawableRef() {
244 MOZ_ASSERT(mHaveSurface
);
246 // If we weren't created with a DrawableFrameRef directly, we should've been
247 // created with an ISurfaceProvider which can give us one. Note that if
248 // Seek() has been called, we'll already have a DrawableFrameRef, so we
249 // won't need to get one here.
251 MOZ_ASSERT(mProvider
);
252 mDrawableRef
= mProvider
->DrawableRef(/* aFrame = */ 0);
255 MOZ_ASSERT(mDrawableRef
);
259 DrawableFrameRef mDrawableRef
;
260 RefPtr
<ISurfaceProvider
> mProvider
;
264 // Surface() is implemented here so that DrawableSurface's definition is
265 // visible. This default implementation eagerly obtains a DrawableFrameRef for
266 // the first frame and is intended for static ISurfaceProviders.
267 inline DrawableSurface
ISurfaceProvider::Surface() {
268 return DrawableSurface(DrawableRef(/* aFrame = */ 0));
272 * An ISurfaceProvider that stores a single surface.
274 class SimpleSurfaceProvider final
: public ISurfaceProvider
{
276 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleSurfaceProvider
, override
)
278 SimpleSurfaceProvider(const ImageKey aImageKey
, const SurfaceKey
& aSurfaceKey
,
279 NotNull
<imgFrame
*> aSurface
)
280 : ISurfaceProvider(aImageKey
, aSurfaceKey
,
281 AvailabilityState::StartAvailable()),
283 MOZ_ASSERT(aSurfaceKey
.Size() == mSurface
->GetSize());
286 bool IsFinished() const override
{ return mSurface
->IsFinished(); }
288 size_t LogicalSizeInBytes() const override
{
289 gfx::IntSize size
= mSurface
->GetSize();
290 return size
.width
* size
.height
* mSurface
->GetBytesPerPixel();
294 DrawableFrameRef
DrawableRef(size_t aFrame
) override
{
295 MOZ_ASSERT(aFrame
== 0,
296 "Requesting an animation frame from a SimpleSurfaceProvider?");
297 return mSurface
->DrawableRef();
300 bool IsLocked() const override
{ return bool(mLockRef
); }
302 void SetLocked(bool aLocked
) override
{
303 if (aLocked
== IsLocked()) {
304 return; // Nothing changed.
307 // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep
308 // any volatile buffer it owns in memory.
309 mLockRef
= aLocked
? mSurface
->DrawableRef() : DrawableFrameRef();
313 virtual ~SimpleSurfaceProvider() {}
315 NotNull
<RefPtr
<imgFrame
>> mSurface
;
316 DrawableFrameRef mLockRef
;
320 } // namespace mozilla
322 #endif // mozilla_image_ISurfaceProvider_h