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