Coverage Report

Created: 2018-09-25 14:53

/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