/src/mozilla-central/image/ISurfaceProvider.h
Line | Count | Source (jump to first uncovered line) |
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_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING |
41 | | |
42 | | /// @return key data used for identifying which image this ISurfaceProvider is |
43 | | /// associated with in the surface cache. |
44 | 0 | ImageKey GetImageKey() const { return mImageKey; } |
45 | | |
46 | | /// @return key data used to uniquely identify this ISurfaceProvider's cache |
47 | | /// entry in the surface cache. |
48 | 0 | const SurfaceKey& GetSurfaceKey() const { return mSurfaceKey; } |
49 | | |
50 | | /// @return a (potentially lazily computed) drawable reference to a surface. |
51 | | virtual DrawableSurface Surface(); |
52 | | |
53 | | /// @return true if DrawableRef() will return a completely decoded surface. |
54 | | virtual bool IsFinished() const = 0; |
55 | | |
56 | | /// @return true if the underlying decoder is currently fully decoded. For |
57 | | /// animated images, this means that at least every frame has been decoded |
58 | | /// at least once. It does not guarantee that all of the frames are present, |
59 | | /// as the surface provider has the option to discard as it deems necessary. |
60 | 0 | virtual bool IsFullyDecoded() const { return IsFinished(); } |
61 | | |
62 | | /// @return the number of bytes of memory this ISurfaceProvider is expected to |
63 | | /// require. Optimizations may result in lower real memory usage. Trivial |
64 | | /// overhead is ignored. Because this value is used in bookkeeping, it's |
65 | | /// important that it be constant over the lifetime of this object. |
66 | | virtual size_t LogicalSizeInBytes() const = 0; |
67 | | |
68 | | /// @return the actual number of bytes of memory this ISurfaceProvider is |
69 | | /// using. May vary over the lifetime of the ISurfaceProvider. The default |
70 | | /// implementation is appropriate for static ISurfaceProviders. |
71 | | virtual void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, |
72 | | size_t& aHeapSizeOut, |
73 | | size_t& aNonHeapSizeOut, |
74 | | size_t& aExtHandlesOut) |
75 | 0 | { |
76 | 0 | DrawableFrameRef ref = DrawableRef(/* aFrame = */ 0); |
77 | 0 | if (!ref) { |
78 | 0 | return; |
79 | 0 | } |
80 | 0 | |
81 | 0 | ref->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut, |
82 | 0 | aNonHeapSizeOut, aExtHandlesOut); |
83 | 0 | } |
84 | | |
85 | 0 | virtual void Reset() { } |
86 | 0 | virtual void Advance(size_t aFrame) { } |
87 | | |
88 | | /// @return the availability state of this ISurfaceProvider, which indicates |
89 | | /// whether DrawableRef() could successfully return a surface. Should only be |
90 | | /// called from SurfaceCache code as it relies on SurfaceCache for |
91 | | /// synchronization. |
92 | 0 | AvailabilityState& Availability() { return mAvailability; } |
93 | 0 | const AvailabilityState& Availability() const { return mAvailability; } |
94 | | |
95 | | protected: |
96 | | ISurfaceProvider(const ImageKey aImageKey, |
97 | | const SurfaceKey& aSurfaceKey, |
98 | | AvailabilityState aAvailability) |
99 | | : mImageKey(aImageKey) |
100 | | , mSurfaceKey(aSurfaceKey) |
101 | | , mAvailability(aAvailability) |
102 | 0 | { |
103 | 0 | MOZ_ASSERT(aImageKey, "Must have a valid image key"); |
104 | 0 | } |
105 | | |
106 | 0 | virtual ~ISurfaceProvider() { } |
107 | | |
108 | | /// @return an eagerly computed drawable reference to a surface. For |
109 | | /// dynamically generated animation surfaces, @aFrame specifies the 0-based |
110 | | /// index of the desired frame. |
111 | | virtual DrawableFrameRef DrawableRef(size_t aFrame) = 0; |
112 | | |
113 | | /// @return an eagerly computed raw access reference to a surface. For |
114 | | /// dynamically generated animation surfaces, @aFrame specifies the 0-based |
115 | | /// index of the desired frame. |
116 | | virtual RawAccessFrameRef RawAccessRef(size_t aFrame) |
117 | 0 | { |
118 | 0 | MOZ_ASSERT_UNREACHABLE("Surface provider does not support raw access!"); |
119 | 0 | return RawAccessFrameRef(); |
120 | 0 | } |
121 | | |
122 | | /// @return true if this ISurfaceProvider is locked. (@see SetLocked()) |
123 | | /// Should only be called from SurfaceCache code as it relies on SurfaceCache |
124 | | /// for synchronization. |
125 | | virtual bool IsLocked() const = 0; |
126 | | |
127 | | /// If @aLocked is true, hint that this ISurfaceProvider is in use and it |
128 | | /// should avoid releasing its resources. Should only be called from |
129 | | /// SurfaceCache code as it relies on SurfaceCache for synchronization. |
130 | | virtual void SetLocked(bool aLocked) = 0; |
131 | | |
132 | | private: |
133 | | friend class CachedSurface; |
134 | | friend class DrawableSurface; |
135 | | |
136 | | const ImageKey mImageKey; |
137 | | const SurfaceKey mSurfaceKey; |
138 | | AvailabilityState mAvailability; |
139 | | }; |
140 | | |
141 | | |
142 | | /** |
143 | | * A reference to a surface (stored in an imgFrame) that holds the surface in |
144 | | * memory, guaranteeing that it can be drawn. If you have a DrawableSurface |
145 | | * |surf| and |if (surf)| returns true, then calls to |surf->Draw()| and |
146 | | * |surf->GetSourceSurface()| are guaranteed to succeed. |
147 | | * |
148 | | * Note that the surface may be computed lazily, so a DrawableSurface should not |
149 | | * be dereferenced (i.e., operator->() should not be called) until you're |
150 | | * sure that you want to draw it. |
151 | | */ |
152 | | class MOZ_STACK_CLASS DrawableSurface final |
153 | | { |
154 | | public: |
155 | 0 | DrawableSurface() : mHaveSurface(false) { } |
156 | | |
157 | | explicit DrawableSurface(DrawableFrameRef&& aDrawableRef) |
158 | | : mDrawableRef(std::move(aDrawableRef)) |
159 | | , mHaveSurface(bool(mDrawableRef)) |
160 | 0 | { } |
161 | | |
162 | | explicit DrawableSurface(NotNull<ISurfaceProvider*> aProvider) |
163 | | : mProvider(aProvider) |
164 | | , mHaveSurface(true) |
165 | 0 | { } |
166 | | |
167 | | DrawableSurface(DrawableSurface&& aOther) |
168 | | : mDrawableRef(std::move(aOther.mDrawableRef)) |
169 | | , mProvider(std::move(aOther.mProvider)) |
170 | | , mHaveSurface(aOther.mHaveSurface) |
171 | 0 | { |
172 | 0 | aOther.mHaveSurface = false; |
173 | 0 | } |
174 | | |
175 | | DrawableSurface& operator=(DrawableSurface&& aOther) |
176 | 0 | { |
177 | 0 | MOZ_ASSERT(this != &aOther, "Self-moves are prohibited"); |
178 | 0 | mDrawableRef = std::move(aOther.mDrawableRef); |
179 | 0 | mProvider = std::move(aOther.mProvider); |
180 | 0 | mHaveSurface = aOther.mHaveSurface; |
181 | 0 | aOther.mHaveSurface = false; |
182 | 0 | return *this; |
183 | 0 | } |
184 | | |
185 | | /** |
186 | | * If this DrawableSurface is dynamically generated from an animation, attempt |
187 | | * to seek to frame @aFrame, where @aFrame is a 0-based index into the frames |
188 | | * of the animation. Otherwise, nothing will blow up at runtime, but we assert |
189 | | * in debug builds, since calling this in an unexpected situation probably |
190 | | * indicates a bug. |
191 | | * |
192 | | * @return a successful result if we could obtain frame @aFrame. Note that |
193 | | * |mHaveSurface| being true means that we're guaranteed to have *some* frame, |
194 | | * so the caller can dereference this DrawableSurface even if Seek() fails, |
195 | | * but while nothing will blow up, the frame won't be the one they expect. |
196 | | */ |
197 | | nsresult Seek(size_t aFrame) |
198 | 0 | { |
199 | 0 | MOZ_ASSERT(mHaveSurface, "Trying to seek an empty DrawableSurface?"); |
200 | 0 |
|
201 | 0 | if (!mProvider) { |
202 | 0 | MOZ_ASSERT_UNREACHABLE("Trying to seek a static DrawableSurface?"); |
203 | 0 | return NS_ERROR_FAILURE; |
204 | 0 | } |
205 | 0 |
|
206 | 0 | mDrawableRef = mProvider->DrawableRef(aFrame); |
207 | 0 |
|
208 | 0 | return mDrawableRef ? NS_OK : NS_ERROR_FAILURE; |
209 | 0 | } |
210 | | |
211 | | RawAccessFrameRef RawAccessRef(size_t aFrame) |
212 | 0 | { |
213 | 0 | MOZ_ASSERT(mHaveSurface, "Trying to get on an empty DrawableSurface?"); |
214 | 0 |
|
215 | 0 | if (!mProvider) { |
216 | 0 | MOZ_ASSERT_UNREACHABLE("Trying to get on a static DrawableSurface?"); |
217 | 0 | return RawAccessFrameRef(); |
218 | 0 | } |
219 | 0 |
|
220 | 0 | return mProvider->RawAccessRef(aFrame); |
221 | 0 | } |
222 | | |
223 | | void Reset() |
224 | 0 | { |
225 | 0 | if (!mProvider) { |
226 | 0 | MOZ_ASSERT_UNREACHABLE("Trying to reset a static DrawableSurface?"); |
227 | 0 | return; |
228 | 0 | } |
229 | 0 |
|
230 | 0 | mProvider->Reset(); |
231 | 0 | } |
232 | | |
233 | | void Advance(size_t aFrame) |
234 | 0 | { |
235 | 0 | if (!mProvider) { |
236 | 0 | MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?"); |
237 | 0 | return; |
238 | 0 | } |
239 | 0 |
|
240 | 0 | mProvider->Advance(aFrame); |
241 | 0 | } |
242 | | |
243 | | bool IsFullyDecoded() const |
244 | 0 | { |
245 | 0 | if (!mProvider) { |
246 | 0 | MOZ_ASSERT_UNREACHABLE("Trying to check decoding state of a static DrawableSurface?"); |
247 | 0 | return false; |
248 | 0 | } |
249 | 0 |
|
250 | 0 | return mProvider->IsFullyDecoded(); |
251 | 0 | } |
252 | | |
253 | 0 | explicit operator bool() const { return mHaveSurface; } |
254 | 0 | imgFrame* operator->() { return DrawableRef().get(); } |
255 | | |
256 | | private: |
257 | | DrawableSurface(const DrawableSurface& aOther) = delete; |
258 | | DrawableSurface& operator=(const DrawableSurface& aOther) = delete; |
259 | | |
260 | | DrawableFrameRef& DrawableRef() |
261 | 0 | { |
262 | 0 | MOZ_ASSERT(mHaveSurface); |
263 | 0 |
|
264 | 0 | // If we weren't created with a DrawableFrameRef directly, we should've been |
265 | 0 | // created with an ISurfaceProvider which can give us one. Note that if |
266 | 0 | // Seek() has been called, we'll already have a DrawableFrameRef, so we |
267 | 0 | // won't need to get one here. |
268 | 0 | if (!mDrawableRef) { |
269 | 0 | MOZ_ASSERT(mProvider); |
270 | 0 | mDrawableRef = mProvider->DrawableRef(/* aFrame = */ 0); |
271 | 0 | } |
272 | 0 |
|
273 | 0 | MOZ_ASSERT(mDrawableRef); |
274 | 0 | return mDrawableRef; |
275 | 0 | } |
276 | | |
277 | | DrawableFrameRef mDrawableRef; |
278 | | RefPtr<ISurfaceProvider> mProvider; |
279 | | bool mHaveSurface; |
280 | | }; |
281 | | |
282 | | |
283 | | // Surface() is implemented here so that DrawableSurface's definition is |
284 | | // visible. This default implementation eagerly obtains a DrawableFrameRef for |
285 | | // the first frame and is intended for static ISurfaceProviders. |
286 | | inline DrawableSurface |
287 | | ISurfaceProvider::Surface() |
288 | 0 | { |
289 | 0 | return DrawableSurface(DrawableRef(/* aFrame = */ 0)); |
290 | 0 | } |
291 | | |
292 | | |
293 | | /** |
294 | | * An ISurfaceProvider that stores a single surface. |
295 | | */ |
296 | | class SimpleSurfaceProvider final : public ISurfaceProvider |
297 | | { |
298 | | public: |
299 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleSurfaceProvider, override) |
300 | | |
301 | | SimpleSurfaceProvider(const ImageKey aImageKey, |
302 | | const SurfaceKey& aSurfaceKey, |
303 | | NotNull<imgFrame*> aSurface) |
304 | | : ISurfaceProvider(aImageKey, aSurfaceKey, |
305 | | AvailabilityState::StartAvailable()) |
306 | | , mSurface(aSurface) |
307 | 0 | { |
308 | 0 | MOZ_ASSERT(aSurfaceKey.Size() == mSurface->GetSize()); |
309 | 0 | } |
310 | | |
311 | 0 | bool IsFinished() const override { return mSurface->IsFinished(); } |
312 | | |
313 | | size_t LogicalSizeInBytes() const override |
314 | 0 | { |
315 | 0 | gfx::IntSize size = mSurface->GetSize(); |
316 | 0 | return size.width * size.height * mSurface->GetBytesPerPixel(); |
317 | 0 | } |
318 | | |
319 | | protected: |
320 | | DrawableFrameRef DrawableRef(size_t aFrame) override |
321 | 0 | { |
322 | 0 | MOZ_ASSERT(aFrame == 0, |
323 | 0 | "Requesting an animation frame from a SimpleSurfaceProvider?"); |
324 | 0 | return mSurface->DrawableRef(); |
325 | 0 | } |
326 | | |
327 | 0 | bool IsLocked() const override { return bool(mLockRef); } |
328 | | |
329 | | void SetLocked(bool aLocked) override |
330 | 0 | { |
331 | 0 | if (aLocked == IsLocked()) { |
332 | 0 | return; // Nothing changed. |
333 | 0 | } |
334 | 0 | |
335 | 0 | // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep |
336 | 0 | // any volatile buffer it owns in memory. |
337 | 0 | mLockRef = aLocked ? mSurface->DrawableRef() |
338 | 0 | : DrawableFrameRef(); |
339 | 0 | } |
340 | | |
341 | | private: |
342 | 0 | virtual ~SimpleSurfaceProvider() { } |
343 | | |
344 | | NotNull<RefPtr<imgFrame>> mSurface; |
345 | | DrawableFrameRef mLockRef; |
346 | | }; |
347 | | |
348 | | } // namespace image |
349 | | } // namespace mozilla |
350 | | |
351 | | #endif // mozilla_image_ISurfaceProvider_h |