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/. */
5
6 /**
7 * An interface for objects which can either store a surface or dynamically
8 * generate one, and various implementations.
9 */
10
11 #ifndef mozilla_image_ISurfaceProvider_h
12 #define mozilla_image_ISurfaceProvider_h
13
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"
21
22 #include "imgFrame.h"
23 #include "SurfaceCache.h"
24
25 namespace mozilla {
26 namespace image {
27
28 class CachedSurface;
29 class DrawableSurface;
30
31 /**
32 * An interface for objects which can either store a surface or dynamically
33 * generate one.
34 */
35 class ISurfaceProvider
36 {
37 public:
38 // Subclasses may or may not be XPCOM classes, so we just require that they
39 // implement AddRef and Release.
40 NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
41 NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
42
43 /// @return key data used for identifying which image this ISurfaceProvider is
44 /// associated with in the surface cache.
GetImageKey()45 ImageKey GetImageKey() const { return mImageKey; }
46
47 /// @return key data used to uniquely identify this ISurfaceProvider's cache
48 /// entry in the surface cache.
GetSurfaceKey()49 const SurfaceKey& GetSurfaceKey() const { return mSurfaceKey; }
50
51 /// @return a (potentially lazily computed) drawable reference to a surface.
52 virtual DrawableSurface Surface();
53
54 /// @return true if DrawableRef() will return a completely decoded surface.
55 virtual bool IsFinished() const = 0;
56
57 /// @return the number of bytes of memory this ISurfaceProvider is expected to
58 /// require. Optimizations may result in lower real memory usage. Trivial
59 /// overhead is ignored. Because this value is used in bookkeeping, it's
60 /// important that it be constant over the lifetime of this object.
61 virtual size_t LogicalSizeInBytes() const = 0;
62
63 /// @return the actual number of bytes of memory this ISurfaceProvider is
64 /// using. May vary over the lifetime of the ISurfaceProvider. The default
65 /// implementation is appropriate for static ISurfaceProviders.
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,size_t & aHeapSizeOut,size_t & aNonHeapSizeOut)66 virtual void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
67 size_t& aHeapSizeOut,
68 size_t& aNonHeapSizeOut)
69 {
70 DrawableFrameRef ref = DrawableRef(/* aFrame = */ 0);
71 if (!ref) {
72 return;
73 }
74
75 ref->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut, aNonHeapSizeOut);
76 }
77
78 /// @return the availability state of this ISurfaceProvider, which indicates
79 /// whether DrawableRef() could successfully return a surface. Should only be
80 /// called from SurfaceCache code as it relies on SurfaceCache for
81 /// synchronization.
Availability()82 AvailabilityState& Availability() { return mAvailability; }
Availability()83 const AvailabilityState& Availability() const { return mAvailability; }
84
85 protected:
ISurfaceProvider(const ImageKey aImageKey,const SurfaceKey & aSurfaceKey,AvailabilityState aAvailability)86 ISurfaceProvider(const ImageKey aImageKey,
87 const SurfaceKey& aSurfaceKey,
88 AvailabilityState aAvailability)
89 : mImageKey(aImageKey)
90 , mSurfaceKey(aSurfaceKey)
91 , mAvailability(aAvailability)
92 {
93 MOZ_ASSERT(aImageKey, "Must have a valid image key");
94 }
95
~ISurfaceProvider()96 virtual ~ISurfaceProvider() { }
97
98 /// @return an eagerly computed drawable reference to a surface. For
99 /// dynamically generated animation surfaces, @aFrame specifies the 0-based
100 /// index of the desired frame.
101 virtual DrawableFrameRef DrawableRef(size_t aFrame) = 0;
102
103 /// @return true if this ISurfaceProvider is locked. (@see SetLocked())
104 /// Should only be called from SurfaceCache code as it relies on SurfaceCache
105 /// for synchronization.
106 virtual bool IsLocked() const = 0;
107
108 /// If @aLocked is true, hint that this ISurfaceProvider is in use and it
109 /// should avoid releasing its resources. Should only be called from
110 /// SurfaceCache code as it relies on SurfaceCache for synchronization.
111 virtual void SetLocked(bool aLocked) = 0;
112
113 private:
114 friend class CachedSurface;
115 friend class DrawableSurface;
116
117 const ImageKey mImageKey;
118 const SurfaceKey mSurfaceKey;
119 AvailabilityState mAvailability;
120 };
121
122
123 /**
124 * A reference to a surface (stored in an imgFrame) that holds the surface in
125 * memory, guaranteeing that it can be drawn. If you have a DrawableSurface
126 * |surf| and |if (surf)| returns true, then calls to |surf->Draw()| and
127 * |surf->GetSourceSurface()| are guaranteed to succeed.
128 *
129 * Note that the surface may be computed lazily, so a DrawableSurface should not
130 * be dereferenced (i.e., operator->() should not be called) until you're
131 * sure that you want to draw it.
132 */
133 class MOZ_STACK_CLASS DrawableSurface final
134 {
135 public:
DrawableSurface()136 DrawableSurface() : mHaveSurface(false) { }
137
DrawableSurface(DrawableFrameRef && aDrawableRef)138 explicit DrawableSurface(DrawableFrameRef&& aDrawableRef)
139 : mDrawableRef(Move(aDrawableRef))
140 , mHaveSurface(bool(mDrawableRef))
141 { }
142
DrawableSurface(NotNull<ISurfaceProvider * > aProvider)143 explicit DrawableSurface(NotNull<ISurfaceProvider*> aProvider)
144 : mProvider(aProvider)
145 , mHaveSurface(true)
146 { }
147
DrawableSurface(DrawableSurface && aOther)148 DrawableSurface(DrawableSurface&& aOther)
149 : mDrawableRef(Move(aOther.mDrawableRef))
150 , mProvider(Move(aOther.mProvider))
151 , mHaveSurface(aOther.mHaveSurface)
152 {
153 aOther.mHaveSurface = false;
154 }
155
156 DrawableSurface& operator=(DrawableSurface&& aOther)
157 {
158 MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
159 mDrawableRef = Move(aOther.mDrawableRef);
160 mProvider = Move(aOther.mProvider);
161 mHaveSurface = aOther.mHaveSurface;
162 aOther.mHaveSurface = false;
163 return *this;
164 }
165
166 /**
167 * If this DrawableSurface is dynamically generated from an animation, attempt
168 * to seek to frame @aFrame, where @aFrame is a 0-based index into the frames
169 * of the animation. Otherwise, nothing will blow up at runtime, but we assert
170 * in debug builds, since calling this in an unexpected situation probably
171 * indicates a bug.
172 *
173 * @return a successful result if we could obtain frame @aFrame. Note that
174 * |mHaveSurface| being true means that we're guaranteed to have *some* frame,
175 * so the caller can dereference this DrawableSurface even if Seek() fails,
176 * but while nothing will blow up, the frame won't be the one they expect.
177 */
Seek(size_t aFrame)178 nsresult Seek(size_t aFrame)
179 {
180 MOZ_ASSERT(mHaveSurface, "Trying to seek an empty DrawableSurface?");
181
182 if (!mProvider) {
183 MOZ_ASSERT_UNREACHABLE("Trying to seek a static DrawableSurface?");
184 return NS_ERROR_FAILURE;
185 }
186
187 mDrawableRef = mProvider->DrawableRef(aFrame);
188
189 return mDrawableRef ? NS_OK : NS_ERROR_FAILURE;
190 }
191
192 explicit operator bool() const { return mHaveSurface; }
193 imgFrame* operator->() { return DrawableRef().get(); }
194
195 private:
196 DrawableSurface(const DrawableSurface& aOther) = delete;
197 DrawableSurface& operator=(const DrawableSurface& aOther) = delete;
198
DrawableRef()199 DrawableFrameRef& DrawableRef()
200 {
201 MOZ_ASSERT(mHaveSurface);
202
203 // If we weren't created with a DrawableFrameRef directly, we should've been
204 // created with an ISurfaceProvider which can give us one. Note that if
205 // Seek() has been called, we'll already have a DrawableFrameRef, so we
206 // won't need to get one here.
207 if (!mDrawableRef) {
208 MOZ_ASSERT(mProvider);
209 mDrawableRef = mProvider->DrawableRef(/* aFrame = */ 0);
210 }
211
212 MOZ_ASSERT(mDrawableRef);
213 return mDrawableRef;
214 }
215
216 DrawableFrameRef mDrawableRef;
217 RefPtr<ISurfaceProvider> mProvider;
218 bool mHaveSurface;
219 };
220
221
222 // Surface() is implemented here so that DrawableSurface's definition is
223 // visible. This default implementation eagerly obtains a DrawableFrameRef for
224 // the first frame and is intended for static ISurfaceProviders.
225 inline DrawableSurface
Surface()226 ISurfaceProvider::Surface()
227 {
228 return DrawableSurface(DrawableRef(/* aFrame = */ 0));
229 }
230
231
232 /**
233 * An ISurfaceProvider that stores a single surface.
234 */
235 class SimpleSurfaceProvider final : public ISurfaceProvider
236 {
237 public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleSurfaceProvider,override)238 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleSurfaceProvider, override)
239
240 SimpleSurfaceProvider(const ImageKey aImageKey,
241 const SurfaceKey& aSurfaceKey,
242 NotNull<imgFrame*> aSurface)
243 : ISurfaceProvider(aImageKey, aSurfaceKey,
244 AvailabilityState::StartAvailable())
245 , mSurface(aSurface)
246 {
247 MOZ_ASSERT(aSurfaceKey.Size() == mSurface->GetSize());
248 }
249
IsFinished()250 bool IsFinished() const override { return mSurface->IsFinished(); }
251
LogicalSizeInBytes()252 size_t LogicalSizeInBytes() const override
253 {
254 gfx::IntSize size = mSurface->GetSize();
255 return size.width * size.height * mSurface->GetBytesPerPixel();
256 }
257
258 protected:
DrawableRef(size_t aFrame)259 DrawableFrameRef DrawableRef(size_t aFrame) override
260 {
261 MOZ_ASSERT(aFrame == 0,
262 "Requesting an animation frame from a SimpleSurfaceProvider?");
263 return mSurface->DrawableRef();
264 }
265
IsLocked()266 bool IsLocked() const override { return bool(mLockRef); }
267
SetLocked(bool aLocked)268 void SetLocked(bool aLocked) override
269 {
270 if (aLocked == IsLocked()) {
271 return; // Nothing changed.
272 }
273
274 // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep
275 // any volatile buffer it owns in memory.
276 mLockRef = aLocked ? mSurface->DrawableRef()
277 : DrawableFrameRef();
278 }
279
280 private:
~SimpleSurfaceProvider()281 virtual ~SimpleSurfaceProvider() { }
282
283 NotNull<RefPtr<imgFrame>> mSurface;
284 DrawableFrameRef mLockRef;
285 };
286
287 } // namespace image
288 } // namespace mozilla
289
290 #endif // mozilla_image_ISurfaceProvider_h
291