Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : *
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef mozilla_image_imgFrame_h
8 : #define mozilla_image_imgFrame_h
9 :
10 : #include "mozilla/Maybe.h"
11 : #include "mozilla/MemoryReporting.h"
12 : #include "mozilla/Monitor.h"
13 : #include "mozilla/Move.h"
14 : #include "FrameTimeout.h"
15 : #include "gfxDrawable.h"
16 : #include "imgIContainer.h"
17 : #include "MainThreadUtils.h"
18 : #include "nsAutoPtr.h"
19 :
20 : namespace mozilla {
21 : namespace image {
22 :
23 : class ImageRegion;
24 : class DrawableFrameRef;
25 : class RawAccessFrameRef;
26 :
27 : enum class BlendMethod : int8_t {
28 : // All color components of the frame, including alpha, overwrite the current
29 : // contents of the frame's output buffer region.
30 : SOURCE,
31 :
32 : // The frame should be composited onto the output buffer based on its alpha,
33 : // using a simple OVER operation.
34 : OVER
35 : };
36 :
37 : enum class DisposalMethod : int8_t {
38 : CLEAR_ALL = -1, // Clear the whole image, revealing what's underneath.
39 : NOT_SPECIFIED, // Leave the frame and let the new frame draw on top.
40 : KEEP, // Leave the frame and let the new frame draw on top.
41 : CLEAR, // Clear the frame's area, revealing what's underneath.
42 : RESTORE_PREVIOUS // Restore the previous (composited) frame.
43 : };
44 :
45 : enum class Opacity : uint8_t {
46 : FULLY_OPAQUE,
47 : SOME_TRANSPARENCY
48 : };
49 :
50 : /**
51 : * AnimationData contains all of the information necessary for using an imgFrame
52 : * as part of an animation.
53 : *
54 : * It includes pointers to the raw image data of the underlying imgFrame, but
55 : * does not own that data. A RawAccessFrameRef for the underlying imgFrame must
56 : * outlive the AnimationData for it to remain valid.
57 : */
58 144 : struct AnimationData
59 : {
60 144 : AnimationData(uint8_t* aRawData, uint32_t aPaletteDataLength,
61 : FrameTimeout aTimeout, const nsIntRect& aRect,
62 : BlendMethod aBlendMethod, const Maybe<gfx::IntRect>& aBlendRect,
63 : DisposalMethod aDisposalMethod, bool aHasAlpha)
64 144 : : mRawData(aRawData)
65 : , mPaletteDataLength(aPaletteDataLength)
66 : , mTimeout(aTimeout)
67 : , mRect(aRect)
68 : , mBlendMethod(aBlendMethod)
69 : , mBlendRect(aBlendRect)
70 : , mDisposalMethod(aDisposalMethod)
71 144 : , mHasAlpha(aHasAlpha)
72 144 : { }
73 :
74 : uint8_t* mRawData;
75 : uint32_t mPaletteDataLength;
76 : FrameTimeout mTimeout;
77 : nsIntRect mRect;
78 : BlendMethod mBlendMethod;
79 : Maybe<gfx::IntRect> mBlendRect;
80 : DisposalMethod mDisposalMethod;
81 : bool mHasAlpha;
82 : };
83 :
84 : class imgFrame
85 : {
86 : typedef gfx::Color Color;
87 : typedef gfx::DataSourceSurface DataSourceSurface;
88 : typedef gfx::DrawTarget DrawTarget;
89 : typedef gfx::SamplingFilter SamplingFilter;
90 : typedef gfx::IntPoint IntPoint;
91 : typedef gfx::IntRect IntRect;
92 : typedef gfx::IntSize IntSize;
93 : typedef gfx::SourceSurface SourceSurface;
94 : typedef gfx::SurfaceFormat SurfaceFormat;
95 :
96 : public:
97 : MOZ_DECLARE_REFCOUNTED_TYPENAME(imgFrame)
98 1146 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgFrame)
99 :
100 : imgFrame();
101 :
102 : /**
103 : * Initialize this imgFrame with an empty surface and prepare it for being
104 : * written to by a decoder.
105 : *
106 : * This is appropriate for use with decoded images, but it should not be used
107 : * when drawing content into an imgFrame, as it may use a different graphics
108 : * backend than normal content drawing.
109 : */
110 : nsresult InitForDecoder(const nsIntSize& aImageSize,
111 : const nsIntRect& aRect,
112 : SurfaceFormat aFormat,
113 : uint8_t aPaletteDepth = 0,
114 : bool aNonPremult = false,
115 : bool aIsAnimated = false);
116 :
117 2 : nsresult InitForAnimator(const nsIntSize& aSize,
118 : SurfaceFormat aFormat)
119 : {
120 4 : return InitForDecoder(aSize, nsIntRect(0, 0, aSize.width, aSize.height),
121 4 : aFormat, 0, false, true);
122 : }
123 :
124 :
125 : /**
126 : * Initialize this imgFrame with a new surface and draw the provided
127 : * gfxDrawable into it.
128 : *
129 : * This is appropriate to use when drawing content into an imgFrame, as it
130 : * uses the same graphics backend as normal content drawing. The downside is
131 : * that the underlying surface may not be stored in a volatile buffer on all
132 : * platforms, and raw access to the surface (using RawAccessRef()) may be much
133 : * more expensive than in the InitForDecoder() case.
134 : *
135 : * aBackend specifies the DrawTarget backend type this imgFrame is supposed
136 : * to be drawn to.
137 : */
138 : nsresult InitWithDrawable(gfxDrawable* aDrawable,
139 : const nsIntSize& aSize,
140 : const SurfaceFormat aFormat,
141 : SamplingFilter aSamplingFilter,
142 : uint32_t aImageFlags,
143 : gfx::BackendType aBackend);
144 :
145 : DrawableFrameRef DrawableRef();
146 : RawAccessFrameRef RawAccessRef();
147 :
148 : /**
149 : * Make this imgFrame permanently available for raw access.
150 : *
151 : * This is irrevocable, and should be avoided whenever possible, since it
152 : * prevents this imgFrame from being optimized and makes it impossible for its
153 : * volatile buffer to be freed.
154 : *
155 : * It is an error to call this without already holding a RawAccessFrameRef to
156 : * this imgFrame.
157 : */
158 : void SetRawAccessOnly();
159 :
160 : bool Draw(gfxContext* aContext, const ImageRegion& aRegion,
161 : SamplingFilter aSamplingFilter, uint32_t aImageFlags,
162 : float aOpacity);
163 :
164 : nsresult ImageUpdated(const nsIntRect& aUpdateRect);
165 :
166 : /**
167 : * Mark this imgFrame as completely decoded, and set final options.
168 : *
169 : * You must always call either Finish() or Abort() before releasing the last
170 : * RawAccessFrameRef pointing to an imgFrame.
171 : *
172 : * @param aFrameOpacity Whether this imgFrame is opaque.
173 : * @param aDisposalMethod For animation frames, how this imgFrame is cleared
174 : * from the compositing frame before the next frame is
175 : * displayed.
176 : * @param aTimeout For animation frames, the timeout before the next
177 : * frame is displayed.
178 : * @param aBlendMethod For animation frames, a blending method to be used
179 : * when compositing this frame.
180 : * @param aBlendRect For animation frames, if present, the subrect in
181 : * which @aBlendMethod applies. Outside of this
182 : * subrect, BlendMethod::OVER is always used.
183 : * @param aFinalize Finalize the underlying surface (e.g. so that it
184 : * may be marked as read only if possible).
185 : */
186 : void Finish(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY,
187 : DisposalMethod aDisposalMethod = DisposalMethod::KEEP,
188 : FrameTimeout aTimeout = FrameTimeout::FromRawMilliseconds(0),
189 : BlendMethod aBlendMethod = BlendMethod::OVER,
190 : const Maybe<IntRect>& aBlendRect = Nothing(),
191 : bool aFinalize = true);
192 :
193 : /**
194 : * Mark this imgFrame as aborted. This informs the imgFrame that if it isn't
195 : * completely decoded now, it never will be.
196 : *
197 : * You must always call either Finish() or Abort() before releasing the last
198 : * RawAccessFrameRef pointing to an imgFrame.
199 : */
200 : void Abort();
201 :
202 : /**
203 : * Returns true if this imgFrame has been aborted.
204 : */
205 : bool IsAborted() const;
206 :
207 : /**
208 : * Returns true if this imgFrame is completely decoded.
209 : */
210 : bool IsFinished() const;
211 :
212 : /**
213 : * Blocks until this imgFrame is either completely decoded, or is marked as
214 : * aborted.
215 : *
216 : * Note that calling this on the main thread _blocks the main thread_. Be very
217 : * careful in your use of this method to avoid excessive main thread jank or
218 : * deadlock.
219 : */
220 : void WaitUntilFinished() const;
221 :
222 : /**
223 : * Returns the number of bytes per pixel this imgFrame requires. This is a
224 : * worst-case value that does not take into account the effects of format
225 : * changes caused by Optimize(), since an imgFrame is not optimized throughout
226 : * its lifetime.
227 : */
228 36 : uint32_t GetBytesPerPixel() const { return GetIsPaletted() ? 1 : 4; }
229 :
230 35 : IntSize GetImageSize() const { return mImageSize; }
231 266 : IntRect GetRect() const { return mFrameRect; }
232 109 : IntSize GetSize() const { return mFrameRect.Size(); }
233 : void GetImageData(uint8_t** aData, uint32_t* length) const;
234 : uint8_t* GetImageData() const;
235 :
236 : bool GetIsPaletted() const;
237 : void GetPaletteData(uint32_t** aPalette, uint32_t* length) const;
238 : uint32_t* GetPaletteData() const;
239 : uint8_t GetPaletteDepth() const { return mPaletteDepth; }
240 :
241 : AnimationData GetAnimationData() const;
242 :
243 : bool GetCompositingFailed() const;
244 : void SetCompositingFailed(bool val);
245 :
246 : void SetOptimizable();
247 :
248 : void FinalizeSurface();
249 : already_AddRefed<SourceSurface> GetSourceSurface();
250 :
251 : void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, size_t& aHeapSizeOut,
252 : size_t& aNonHeapSizeOut,
253 : size_t& aSharedHandlesOut) const;
254 :
255 : private: // methods
256 :
257 : ~imgFrame();
258 :
259 : nsresult LockImageData();
260 : nsresult UnlockImageData();
261 : nsresult Optimize(gfx::DrawTarget* aTarget);
262 :
263 : void AssertImageDataLocked() const;
264 :
265 : bool AreAllPixelsWritten() const;
266 : nsresult ImageUpdatedInternal(const nsIntRect& aUpdateRect);
267 : void GetImageDataInternal(uint8_t** aData, uint32_t* length) const;
268 : uint32_t GetImageBytesPerRow() const;
269 : uint32_t GetImageDataLength() const;
270 : void FinalizeSurfaceInternal();
271 : already_AddRefed<SourceSurface> GetSourceSurfaceInternal();
272 :
273 144 : uint32_t PaletteDataLength() const
274 : {
275 144 : return mPaletteDepth ? (size_t(1) << mPaletteDepth) * sizeof(uint32_t)
276 144 : : 0;
277 : }
278 :
279 35 : struct SurfaceWithFormat {
280 : RefPtr<gfxDrawable> mDrawable;
281 : SurfaceFormat mFormat;
282 0 : SurfaceWithFormat() { }
283 35 : SurfaceWithFormat(gfxDrawable* aDrawable, SurfaceFormat aFormat)
284 35 : : mDrawable(aDrawable), mFormat(aFormat)
285 35 : { }
286 35 : bool IsValid() { return !!mDrawable; }
287 : };
288 :
289 : SurfaceWithFormat SurfaceForDrawing(bool aDoPartialDecode,
290 : bool aDoTile,
291 : ImageRegion& aRegion,
292 : SourceSurface* aSurface);
293 :
294 : private: // data
295 : friend class DrawableFrameRef;
296 : friend class RawAccessFrameRef;
297 : friend class UnlockImageDataRunnable;
298 :
299 : //////////////////////////////////////////////////////////////////////////////
300 : // Thread-safe mutable data, protected by mMonitor.
301 : //////////////////////////////////////////////////////////////////////////////
302 :
303 : mutable Monitor mMonitor;
304 :
305 : /**
306 : * Surface which contains either a weak or a strong reference to its
307 : * underlying data buffer. If it is a weak reference, and there are no strong
308 : * references, the buffer may be released due to events such as low memory.
309 : */
310 : RefPtr<DataSourceSurface> mRawSurface;
311 :
312 : /**
313 : * Refers to the same data as mRawSurface, but when set, it guarantees that
314 : * we hold a strong reference to the underlying data buffer.
315 : */
316 : RefPtr<DataSourceSurface> mLockedSurface;
317 :
318 : /**
319 : * Optimized copy of mRawSurface for the DrawTarget that will render it. This
320 : * is unused if the DrawTarget is able to render DataSourceSurface buffers
321 : * directly.
322 : */
323 : RefPtr<SourceSurface> mOptSurface;
324 :
325 : nsIntRect mDecoded;
326 :
327 : //! Number of RawAccessFrameRefs currently alive for this imgFrame.
328 : int32_t mLockCount;
329 :
330 : //! The timeout for this frame.
331 : FrameTimeout mTimeout;
332 :
333 : DisposalMethod mDisposalMethod;
334 : BlendMethod mBlendMethod;
335 : Maybe<IntRect> mBlendRect;
336 : SurfaceFormat mFormat;
337 :
338 : bool mAborted;
339 : bool mFinished;
340 : bool mOptimizable;
341 :
342 :
343 : //////////////////////////////////////////////////////////////////////////////
344 : // Effectively const data, only mutated in the Init methods.
345 : //////////////////////////////////////////////////////////////////////////////
346 :
347 : IntSize mImageSize;
348 : IntRect mFrameRect;
349 :
350 : // The palette and image data for images that are paletted, since Cairo
351 : // doesn't support these images.
352 : // The paletted data comes first, then the image data itself.
353 : // Total length is PaletteDataLength() + GetImageDataLength().
354 : uint8_t* mPalettedImageData;
355 : uint8_t mPaletteDepth;
356 :
357 : bool mNonPremult;
358 :
359 :
360 : //////////////////////////////////////////////////////////////////////////////
361 : // Main-thread-only mutable data.
362 : //////////////////////////////////////////////////////////////////////////////
363 :
364 : bool mCompositingFailed;
365 : };
366 :
367 : /**
368 : * A reference to an imgFrame that holds the imgFrame's surface in memory,
369 : * allowing drawing. If you have a DrawableFrameRef |ref| and |if (ref)| returns
370 : * true, then calls to Draw() and GetSourceSurface() are guaranteed to succeed.
371 : */
372 692 : class DrawableFrameRef final
373 : {
374 : typedef gfx::DataSourceSurface DataSourceSurface;
375 :
376 : public:
377 168 : DrawableFrameRef() { }
378 :
379 284 : explicit DrawableFrameRef(imgFrame* aFrame)
380 284 : : mFrame(aFrame)
381 : {
382 284 : MOZ_ASSERT(aFrame);
383 568 : MonitorAutoLock lock(aFrame->mMonitor);
384 :
385 : // Paletted images won't have a surface so there is no strong reference
386 : // to hold on to. Since Draw() and GetSourceSurface() calls will not work
387 : // in that case, we should be using RawAccessFrameRef exclusively instead.
388 : // See FrameAnimator::GetRawFrame for an example of this behaviour.
389 284 : if (aFrame->mRawSurface) {
390 : mRef = new DataSourceSurface::ScopedMap(aFrame->mRawSurface,
391 422 : DataSourceSurface::READ_WRITE);
392 211 : if (!mRef->IsMapped()) {
393 0 : mFrame = nullptr;
394 0 : mRef = nullptr;
395 : }
396 : } else {
397 73 : MOZ_ASSERT(aFrame->mOptSurface || aFrame->GetIsPaletted());
398 : }
399 284 : }
400 :
401 270 : DrawableFrameRef(DrawableFrameRef&& aOther)
402 540 : : mFrame(aOther.mFrame.forget())
403 540 : , mRef(Move(aOther.mRef))
404 270 : { }
405 :
406 213 : DrawableFrameRef& operator=(DrawableFrameRef&& aOther)
407 : {
408 213 : MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
409 213 : mFrame = aOther.mFrame.forget();
410 213 : mRef = Move(aOther.mRef);
411 213 : return *this;
412 : }
413 :
414 1456 : explicit operator bool() const { return bool(mFrame); }
415 :
416 0 : imgFrame* operator->()
417 : {
418 0 : MOZ_ASSERT(mFrame);
419 0 : return mFrame;
420 : }
421 :
422 : const imgFrame* operator->() const
423 : {
424 : MOZ_ASSERT(mFrame);
425 : return mFrame;
426 : }
427 :
428 496 : imgFrame* get() { return mFrame; }
429 : const imgFrame* get() const { return mFrame; }
430 :
431 : void reset()
432 : {
433 : mFrame = nullptr;
434 : mRef = nullptr;
435 : }
436 :
437 : private:
438 : DrawableFrameRef(const DrawableFrameRef& aOther) = delete;
439 :
440 : RefPtr<imgFrame> mFrame;
441 : nsAutoPtr<DataSourceSurface::ScopedMap> mRef;
442 : };
443 :
444 : /**
445 : * A reference to an imgFrame that holds the imgFrame's surface in memory in a
446 : * format appropriate for access as raw data. If you have a RawAccessFrameRef
447 : * |ref| and |if (ref)| is true, then calls to GetImageData() and
448 : * GetPaletteData() are guaranteed to succeed. This guarantee is stronger than
449 : * DrawableFrameRef, so everything that a valid DrawableFrameRef guarantees is
450 : * also guaranteed by a valid RawAccessFrameRef.
451 : *
452 : * This may be considerably more expensive than is necessary just for drawing,
453 : * so only use this when you need to read or write the raw underlying image data
454 : * that the imgFrame holds.
455 : *
456 : * Once all an imgFrame's RawAccessFrameRefs go out of scope, new
457 : * RawAccessFrameRefs cannot be created.
458 : */
459 : class RawAccessFrameRef final
460 : {
461 : public:
462 37 : RawAccessFrameRef() { }
463 :
464 240 : explicit RawAccessFrameRef(imgFrame* aFrame)
465 240 : : mFrame(aFrame)
466 : {
467 240 : MOZ_ASSERT(mFrame, "Need a frame");
468 :
469 240 : if (NS_FAILED(mFrame->LockImageData())) {
470 0 : mFrame->UnlockImageData();
471 0 : mFrame = nullptr;
472 : }
473 240 : }
474 :
475 84 : RawAccessFrameRef(RawAccessFrameRef&& aOther)
476 84 : : mFrame(aOther.mFrame.forget())
477 84 : { }
478 :
479 321 : ~RawAccessFrameRef()
480 321 : {
481 321 : if (mFrame) {
482 168 : mFrame->UnlockImageData();
483 : }
484 321 : }
485 :
486 50 : RawAccessFrameRef& operator=(RawAccessFrameRef&& aOther)
487 : {
488 50 : MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
489 :
490 50 : if (mFrame) {
491 34 : mFrame->UnlockImageData();
492 : }
493 :
494 50 : mFrame = aOther.mFrame.forget();
495 :
496 50 : return *this;
497 : }
498 :
499 2162 : explicit operator bool() const { return bool(mFrame); }
500 :
501 1020 : imgFrame* operator->()
502 : {
503 1020 : MOZ_ASSERT(mFrame);
504 1020 : return mFrame.get();
505 : }
506 :
507 0 : const imgFrame* operator->() const
508 : {
509 0 : MOZ_ASSERT(mFrame);
510 0 : return mFrame;
511 : }
512 :
513 128 : imgFrame* get() { return mFrame; }
514 : const imgFrame* get() const { return mFrame; }
515 :
516 0 : void reset()
517 : {
518 0 : if (mFrame) {
519 0 : mFrame->UnlockImageData();
520 : }
521 0 : mFrame = nullptr;
522 0 : }
523 :
524 : private:
525 : RawAccessFrameRef(const RawAccessFrameRef& aOther) = delete;
526 :
527 : RefPtr<imgFrame> mFrame;
528 : };
529 :
530 : void MarkSurfaceShared(gfx::SourceSurface* aSurface);
531 :
532 : } // namespace image
533 : } // namespace mozilla
534 :
535 : #endif // mozilla_image_imgFrame_h
|