1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
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/. */
8 #include "ImageRegion.h"
9 #include "SurfaceCache.h"
13 #include "gfx2DGlue.h"
14 #include "gfxContext.h"
15 #include "gfxPlatform.h"
19 #include "MainThreadUtils.h"
20 #include "mozilla/CheckedInt.h"
21 #include "mozilla/gfx/Tools.h"
22 #include "mozilla/Likely.h"
23 #include "mozilla/MemoryReporting.h"
24 #include "mozilla/ProfilerLabels.h"
25 #include "mozilla/StaticPrefs_browser.h"
27 #include "nsRefreshDriver.h"
28 #include "nsThreadUtils.h"
30 #include <algorithm> // for min, max
39 * This class is identical to SourceSurfaceSharedData but returns a different
40 * type so that SharedSurfacesChild is aware imagelib wants to recycle this
41 * surface for future animation frames.
43 class RecyclingSourceSurfaceSharedData final
: public SourceSurfaceSharedData
{
45 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(RecyclingSourceSurfaceSharedData
,
48 SurfaceType
GetType() const override
{
49 return SurfaceType::DATA_RECYCLING_SHARED
;
53 static already_AddRefed
<SourceSurfaceSharedData
> AllocateBufferForImage(
54 const IntSize
& size
, SurfaceFormat format
, bool aShouldRecycle
= false) {
55 // Stride must be a multiple of four or cairo will complain.
56 int32_t stride
= (size
.width
* BytesPerPixel(format
) + 0x3) & ~0x3;
58 RefPtr
<SourceSurfaceSharedData
> newSurf
;
60 newSurf
= new RecyclingSourceSurfaceSharedData();
62 newSurf
= new SourceSurfaceSharedData();
64 if (!newSurf
->Init(size
, stride
, format
)) {
67 return newSurf
.forget();
70 static bool GreenSurface(SourceSurfaceSharedData
* aSurface
,
71 const IntSize
& aSize
, SurfaceFormat aFormat
) {
72 int32_t stride
= aSurface
->Stride();
73 uint32_t* surfaceData
= reinterpret_cast<uint32_t*>(aSurface
->GetData());
74 uint32_t surfaceDataLength
= (stride
* aSize
.height
) / sizeof(uint32_t);
76 // Start by assuming that GG is in the second byte and
77 // AA is in the final byte -- the most common case.
78 uint32_t color
= mozilla::NativeEndian::swapFromBigEndian(0x00FF00FF);
80 // We are only going to handle this type of test under
81 // certain circumstances.
82 MOZ_ASSERT(surfaceData
);
83 MOZ_ASSERT(aFormat
== SurfaceFormat::B8G8R8A8
||
84 aFormat
== SurfaceFormat::B8G8R8X8
||
85 aFormat
== SurfaceFormat::R8G8B8A8
||
86 aFormat
== SurfaceFormat::R8G8B8X8
||
87 aFormat
== SurfaceFormat::A8R8G8B8
||
88 aFormat
== SurfaceFormat::X8R8G8B8
);
89 MOZ_ASSERT((stride
* aSize
.height
) % sizeof(uint32_t));
91 if (aFormat
== SurfaceFormat::A8R8G8B8
||
92 aFormat
== SurfaceFormat::X8R8G8B8
) {
93 color
= mozilla::NativeEndian::swapFromBigEndian(0xFF00FF00);
96 for (uint32_t i
= 0; i
< surfaceDataLength
; i
++) {
97 surfaceData
[i
] = color
;
103 static bool ClearSurface(SourceSurfaceSharedData
* aSurface
,
104 const IntSize
& aSize
, SurfaceFormat aFormat
) {
105 int32_t stride
= aSurface
->Stride();
106 uint8_t* data
= aSurface
->GetData();
109 if (aFormat
== SurfaceFormat::OS_RGBX
) {
110 // Skia doesn't support RGBX surfaces, so ensure the alpha value is set
111 // to opaque white. While it would be nice to only do this for Skia,
112 // imgFrame can run off main thread and past shutdown where
113 // we might not have gfxPlatform, so just memset every time instead.
114 memset(data
, 0xFF, stride
* aSize
.height
);
115 } else if (aSurface
->OnHeap()) {
116 // We only need to memset it if the buffer was allocated on the heap.
117 // Otherwise, it's allocated via mmap and refers to a zeroed page and will
118 // be COW once it's written to.
119 memset(data
, 0, stride
* aSize
.height
);
126 : mMonitor("imgFrame"),
127 mDecoded(0, 0, 0, 0),
130 mShouldRecycle(false),
131 mTimeout(FrameTimeout::FromRawMilliseconds(100)),
132 mDisposalMethod(DisposalMethod::NOT_SPECIFIED
),
133 mBlendMethod(BlendMethod::OVER
),
134 mFormat(SurfaceFormat::UNKNOWN
),
135 mNonPremult(false) {}
137 imgFrame::~imgFrame() {
139 MonitorAutoLock
lock(mMonitor
);
140 MOZ_ASSERT(mAborted
|| AreAllPixelsWritten());
141 MOZ_ASSERT(mAborted
|| mFinished
);
145 nsresult
imgFrame::InitForDecoder(const nsIntSize
& aImageSize
,
146 SurfaceFormat aFormat
, bool aNonPremult
,
147 const Maybe
<AnimationParams
>& aAnimParams
,
148 bool aShouldRecycle
) {
149 // Assert for properties that should be verified by decoders,
150 // warn for properties related to bad content.
151 if (!SurfaceCache::IsLegalSize(aImageSize
)) {
152 NS_WARNING("Should have legal image size");
153 MonitorAutoLock
lock(mMonitor
);
155 return NS_ERROR_FAILURE
;
158 mImageSize
= aImageSize
;
160 // May be updated shortly after InitForDecoder by BlendAnimationFilter
161 // because it needs to take into consideration the previous frames to
162 // properly calculate. We start with the whole frame as dirty.
163 mDirtyRect
= GetRect();
166 mBlendRect
= aAnimParams
->mBlendRect
;
167 mTimeout
= aAnimParams
->mTimeout
;
168 mBlendMethod
= aAnimParams
->mBlendMethod
;
169 mDisposalMethod
= aAnimParams
->mDisposalMethod
;
171 mBlendRect
= GetRect();
174 if (aShouldRecycle
) {
175 // If we are recycling then we should always use BGRA for the underlying
176 // surface because if we use BGRX, the next frame composited into the
177 // surface could be BGRA and cause rendering problems.
178 MOZ_ASSERT(aAnimParams
);
179 mFormat
= SurfaceFormat::OS_RGBA
;
184 mNonPremult
= aNonPremult
;
186 MonitorAutoLock
lock(mMonitor
);
187 mShouldRecycle
= aShouldRecycle
;
189 MOZ_ASSERT(!mRawSurface
, "Called imgFrame::InitForDecoder() twice?");
191 mRawSurface
= AllocateBufferForImage(mImageSize
, mFormat
, mShouldRecycle
);
194 return NS_ERROR_OUT_OF_MEMORY
;
197 if (StaticPrefs::browser_measurement_render_anims_and_video_solid() &&
199 mBlankRawSurface
= AllocateBufferForImage(mImageSize
, mFormat
);
200 if (!mBlankRawSurface
) {
202 return NS_ERROR_OUT_OF_MEMORY
;
206 if (!ClearSurface(mRawSurface
, mImageSize
, mFormat
)) {
207 NS_WARNING("Could not clear allocated buffer");
209 return NS_ERROR_OUT_OF_MEMORY
;
212 if (mBlankRawSurface
) {
213 if (!GreenSurface(mBlankRawSurface
, mImageSize
, mFormat
)) {
214 NS_WARNING("Could not clear allocated blank buffer");
216 return NS_ERROR_OUT_OF_MEMORY
;
223 nsresult
imgFrame::InitForDecoderRecycle(const AnimationParams
& aAnimParams
) {
224 // We want to recycle this frame, but there is no guarantee that consumers are
225 // done with it in a timely manner. Let's ensure they are done with it first.
226 MonitorAutoLock
lock(mMonitor
);
228 MOZ_ASSERT(mRawSurface
);
230 if (!mShouldRecycle
) {
231 // This frame either was never marked as recyclable, or the flag was cleared
232 // for a caller which does not support recycling.
233 return NS_ERROR_NOT_AVAILABLE
;
236 // Ensure we account for all internal references to the surface.
237 MozRefCountType internalRefs
= 1;
238 if (mOptSurface
== mRawSurface
) {
242 if (mRawSurface
->refCount() > internalRefs
) {
243 if (NS_IsMainThread()) {
244 // We should never be both decoding and recycling on the main thread. Sync
245 // decoding can only be used to produce the first set of frames. Those
246 // either never use recycling because advancing was blocked (main thread
247 // is busy) or we were auto-advancing (to seek to a frame) and the frames
248 // were never accessed (and thus cannot have recycle locks).
249 MOZ_ASSERT_UNREACHABLE("Recycling/decoding on the main thread?");
250 return NS_ERROR_NOT_AVAILABLE
;
253 // We don't want to wait forever to reclaim the frame because we have no
254 // idea why it is still held. It is possibly due to OMTP. Since we are off
255 // the main thread, and we generally have frames already buffered for the
256 // animation, we can afford to wait a short period of time to hopefully
257 // complete the transaction and reclaim the buffer.
259 // We choose to wait for, at most, the refresh driver interval, so that we
260 // won't skip more than one frame. If the frame is still in use due to
261 // outstanding transactions, we are already skipping frames. If the frame
262 // is still in use for some other purpose, it won't be returned to the pool
263 // and its owner can hold onto it forever without additional impact here.
264 int32_t refreshInterval
=
265 std::max(std::min(nsRefreshDriver::DefaultInterval(), 20), 4);
266 TimeDuration waitInterval
=
267 TimeDuration::FromMilliseconds(refreshInterval
>> 2);
269 TimeStamp::Now() + TimeDuration::FromMilliseconds(refreshInterval
);
271 mMonitor
.Wait(waitInterval
);
272 if (mRawSurface
->refCount() <= internalRefs
) {
276 if (timeout
<= TimeStamp::Now()) {
277 // We couldn't secure the frame for recycling. It will allocate a new
279 return NS_ERROR_NOT_AVAILABLE
;
284 mBlendRect
= aAnimParams
.mBlendRect
;
285 mTimeout
= aAnimParams
.mTimeout
;
286 mBlendMethod
= aAnimParams
.mBlendMethod
;
287 mDisposalMethod
= aAnimParams
.mDisposalMethod
;
288 mDirtyRect
= GetRect();
293 nsresult
imgFrame::InitWithDrawable(gfxDrawable
* aDrawable
,
294 const nsIntSize
& aSize
,
295 const SurfaceFormat aFormat
,
296 SamplingFilter aSamplingFilter
,
297 uint32_t aImageFlags
,
298 gfx::BackendType aBackend
) {
299 // Assert for properties that should be verified by decoders,
300 // warn for properties related to bad content.
301 if (!SurfaceCache::IsLegalSize(aSize
)) {
302 NS_WARNING("Should have legal image size");
303 MonitorAutoLock
lock(mMonitor
);
305 return NS_ERROR_FAILURE
;
311 RefPtr
<DrawTarget
> target
;
313 bool canUseDataSurface
= Factory::DoesBackendSupportDataDrawtarget(aBackend
);
314 if (canUseDataSurface
) {
315 MonitorAutoLock
lock(mMonitor
);
316 // It's safe to use data surfaces for content on this platform, so we can
317 // get away with using volatile buffers.
318 MOZ_ASSERT(!mRawSurface
, "Called imgFrame::InitWithDrawable() twice?");
320 mRawSurface
= AllocateBufferForImage(mImageSize
, mFormat
);
323 return NS_ERROR_OUT_OF_MEMORY
;
326 if (!ClearSurface(mRawSurface
, mImageSize
, mFormat
)) {
327 NS_WARNING("Could not clear allocated buffer");
329 return NS_ERROR_OUT_OF_MEMORY
;
332 target
= gfxPlatform::CreateDrawTargetForData(
333 mRawSurface
->GetData(), mImageSize
, mRawSurface
->Stride(), mFormat
);
335 // We can't use data surfaces for content, so we'll create an offscreen
336 // surface instead. This means if someone later calls RawAccessRef(), we
337 // may have to do an expensive readback, but we warned callers about that in
338 // the documentation for this method.
341 MonitorAutoLock
lock(mMonitor
);
342 MOZ_ASSERT(!mOptSurface
, "Called imgFrame::InitWithDrawable() twice?");
346 if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(aBackend
)) {
347 target
= gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
348 aBackend
, mImageSize
, mFormat
);
350 target
= gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
351 mImageSize
, mFormat
);
355 if (!target
|| !target
->IsValid()) {
356 MonitorAutoLock
lock(mMonitor
);
358 return NS_ERROR_OUT_OF_MEMORY
;
361 // Draw using the drawable the caller provided.
362 gfxContext
ctx(target
);
364 gfxUtils::DrawPixelSnapped(&ctx
, aDrawable
, SizeDouble(mImageSize
),
365 ImageRegion::Create(ThebesRect(GetRect())),
366 mFormat
, aSamplingFilter
, aImageFlags
);
368 MonitorAutoLock
lock(mMonitor
);
369 if (canUseDataSurface
&& !mRawSurface
) {
370 NS_WARNING("Failed to create SourceSurfaceSharedData");
372 return NS_ERROR_OUT_OF_MEMORY
;
375 if (!canUseDataSurface
) {
376 // We used an offscreen surface, which is an "optimized" surface from
377 // imgFrame's perspective.
378 mOptSurface
= target
->Snapshot();
380 FinalizeSurfaceInternal();
383 // If we reach this point, we should regard ourselves as complete.
384 mDecoded
= GetRect();
387 MOZ_ASSERT(AreAllPixelsWritten());
392 DrawableFrameRef
imgFrame::DrawableRef() { return DrawableFrameRef(this); }
394 RawAccessFrameRef
imgFrame::RawAccessRef() { return RawAccessFrameRef(this); }
396 imgFrame::SurfaceWithFormat
imgFrame::SurfaceForDrawing(
397 bool aDoPartialDecode
, bool aDoTile
, ImageRegion
& aRegion
,
398 SourceSurface
* aSurface
) {
399 MOZ_ASSERT(NS_IsMainThread());
400 mMonitor
.AssertCurrentThreadOwns();
402 if (!aDoPartialDecode
) {
403 return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface
, mImageSize
),
408 gfxRect(mDecoded
.X(), mDecoded
.Y(), mDecoded
.Width(), mDecoded
.Height());
411 // Create a temporary surface.
412 // Give this surface an alpha channel because there are
413 // transparent pixels in the padding or undecoded area
414 RefPtr
<DrawTarget
> target
=
415 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
416 mImageSize
, SurfaceFormat::OS_RGBA
);
418 return SurfaceWithFormat();
421 SurfacePattern
pattern(aSurface
, aRegion
.GetExtendMode(),
422 Matrix::Translation(mDecoded
.X(), mDecoded
.Y()));
423 target
->FillRect(ToRect(aRegion
.Intersect(available
).Rect()), pattern
);
425 RefPtr
<SourceSurface
> newsurf
= target
->Snapshot();
426 return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf
, mImageSize
),
427 target
->GetFormat());
430 // Not tiling, and we have a surface, so we can account for
431 // a partial decode just by twiddling parameters.
432 aRegion
= aRegion
.Intersect(available
);
433 IntSize
availableSize(mDecoded
.Width(), mDecoded
.Height());
435 return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface
, availableSize
),
439 bool imgFrame::Draw(gfxContext
* aContext
, const ImageRegion
& aRegion
,
440 SamplingFilter aSamplingFilter
, uint32_t aImageFlags
,
442 AUTO_PROFILER_LABEL("imgFrame::Draw", GRAPHICS
);
444 MOZ_ASSERT(NS_IsMainThread());
445 NS_ASSERTION(!aRegion
.Rect().IsEmpty(), "Drawing empty region!");
446 NS_ASSERTION(!aRegion
.IsRestricted() ||
447 !aRegion
.Rect().Intersect(aRegion
.Restriction()).IsEmpty(),
448 "We must be allowed to sample *some* source pixels!");
450 // Perform the draw and freeing of the surface outside the lock. We want to
451 // avoid contention with the decoder if we can. The surface may also attempt
452 // to relock the monitor if it is freed (e.g. RecyclingSourceSurface).
453 RefPtr
<SourceSurface
> surf
;
454 SurfaceWithFormat surfaceResult
;
455 ImageRegion
region(aRegion
);
456 gfxRect
imageRect(0, 0, mImageSize
.width
, mImageSize
.height
);
459 MonitorAutoLock
lock(mMonitor
);
461 bool doPartialDecode
= !AreAllPixelsWritten();
463 // Most draw targets will just use the surface only during DrawPixelSnapped
464 // but captures/recordings will retain a reference outside this stack
465 // context. While in theory a decoder thread could be trying to recycle this
466 // frame at this very moment, in practice the only way we can get here is if
467 // this frame is the current frame of the animation. Since we can only
468 // advance on the main thread, we know nothing else will try to use it.
469 DrawTarget
* drawTarget
= aContext
->GetDrawTarget();
470 bool recording
= drawTarget
->GetBackendType() == BackendType::RECORDING
;
471 RefPtr
<SourceSurface
> surf
= GetSourceSurfaceInternal();
476 bool doTile
= !imageRect
.Contains(aRegion
.Rect()) &&
477 !(aImageFlags
& imgIContainer::FLAG_CLAMP
);
479 surfaceResult
= SurfaceForDrawing(doPartialDecode
, doTile
, region
, surf
);
481 // If we are recording, then we cannot recycle the surface. The blob
482 // rasterizer is not properly synchronized for recycling in the compositor
483 // process. The easiest thing to do is just mark the frames it consumes as
485 if (recording
&& surfaceResult
.IsValid()) {
486 mShouldRecycle
= false;
490 if (surfaceResult
.IsValid()) {
491 gfxUtils::DrawPixelSnapped(aContext
, surfaceResult
.mDrawable
,
492 imageRect
.Size(), region
, surfaceResult
.mFormat
,
493 aSamplingFilter
, aImageFlags
, aOpacity
);
499 nsresult
imgFrame::ImageUpdated(const nsIntRect
& aUpdateRect
) {
500 MonitorAutoLock
lock(mMonitor
);
501 return ImageUpdatedInternal(aUpdateRect
);
504 nsresult
imgFrame::ImageUpdatedInternal(const nsIntRect
& aUpdateRect
) {
505 mMonitor
.AssertCurrentThreadOwns();
507 // Clamp to the frame rect to ensure that decoder bugs don't result in a
508 // decoded rect that extends outside the bounds of the frame rect.
509 IntRect updateRect
= aUpdateRect
.Intersect(GetRect());
510 if (updateRect
.IsEmpty()) {
514 mDecoded
.UnionRect(mDecoded
, updateRect
);
516 // Update our invalidation counters for any consumers watching for changes
519 mRawSurface
->Invalidate(updateRect
);
524 void imgFrame::Finish(Opacity aFrameOpacity
/* = Opacity::SOME_TRANSPARENCY */,
525 bool aFinalize
/* = true */,
526 bool aOrientationSwapsWidthAndHeight
/* = false */) {
527 MonitorAutoLock
lock(mMonitor
);
529 IntRect
frameRect(GetRect());
530 if (!mDecoded
.IsEqualEdges(frameRect
)) {
531 // The decoder should have produced rows starting from either the bottom or
532 // the top of the image. We need to calculate the region for which we have
533 // not yet invalidated. And if the orientation swaps width and height then
534 // its from the left or right.
535 IntRect
delta(0, 0, frameRect
.width
, 0);
536 if (!aOrientationSwapsWidthAndHeight
) {
537 delta
.width
= frameRect
.width
;
538 if (mDecoded
.y
== 0) {
539 delta
.y
= mDecoded
.height
;
540 delta
.height
= frameRect
.height
- mDecoded
.height
;
541 } else if (mDecoded
.y
+ mDecoded
.height
== frameRect
.height
) {
542 delta
.height
= frameRect
.height
- mDecoded
.y
;
544 MOZ_ASSERT_UNREACHABLE("Decoder only updated middle of image!");
548 delta
.height
= frameRect
.height
;
549 if (mDecoded
.x
== 0) {
550 delta
.x
= mDecoded
.width
;
551 delta
.width
= frameRect
.width
- mDecoded
.width
;
552 } else if (mDecoded
.x
+ mDecoded
.width
== frameRect
.width
) {
553 delta
.width
= frameRect
.width
- mDecoded
.x
;
555 MOZ_ASSERT_UNREACHABLE("Decoder only updated middle of image!");
560 ImageUpdatedInternal(delta
);
563 MOZ_ASSERT(mDecoded
.IsEqualEdges(frameRect
));
566 FinalizeSurfaceInternal();
571 // The image is now complete, wake up anyone who's waiting.
572 mMonitor
.NotifyAll();
575 uint32_t imgFrame::GetImageBytesPerRow() const {
576 mMonitor
.AssertCurrentThreadOwns();
579 return mImageSize
.width
* BytesPerPixel(mFormat
);
585 uint32_t imgFrame::GetImageDataLength() const {
586 return GetImageBytesPerRow() * mImageSize
.height
;
589 void imgFrame::GetImageData(uint8_t** aData
, uint32_t* aLength
) const {
590 MonitorAutoLock
lock(mMonitor
);
591 GetImageDataInternal(aData
, aLength
);
594 void imgFrame::GetImageDataInternal(uint8_t** aData
, uint32_t* aLength
) const {
595 mMonitor
.AssertCurrentThreadOwns();
596 MOZ_ASSERT(mRawSurface
);
599 // TODO: This is okay for now because we only realloc shared surfaces on
600 // the main thread after decoding has finished, but if animations want to
601 // read frame data off the main thread, we will need to reconsider this.
602 *aData
= mRawSurface
->GetData();
604 "mRawSurface is non-null, but GetData is null in GetImageData");
609 *aLength
= GetImageDataLength();
612 uint8_t* imgFrame::GetImageData() const {
615 GetImageData(&data
, &length
);
619 void imgFrame::FinalizeSurface() {
620 MonitorAutoLock
lock(mMonitor
);
621 FinalizeSurfaceInternal();
624 void imgFrame::FinalizeSurfaceInternal() {
625 mMonitor
.AssertCurrentThreadOwns();
627 // Not all images will have mRawSurface to finalize (i.e. paletted images).
628 if (mShouldRecycle
|| !mRawSurface
||
629 mRawSurface
->GetType() != SurfaceType::DATA_SHARED
) {
633 auto* sharedSurf
= static_cast<SourceSurfaceSharedData
*>(mRawSurface
.get());
634 sharedSurf
->Finalize();
637 already_AddRefed
<SourceSurface
> imgFrame::GetSourceSurface() {
638 MonitorAutoLock
lock(mMonitor
);
639 return GetSourceSurfaceInternal();
642 already_AddRefed
<SourceSurface
> imgFrame::GetSourceSurfaceInternal() {
643 mMonitor
.AssertCurrentThreadOwns();
646 if (mOptSurface
->IsValid()) {
647 RefPtr
<SourceSurface
> surf(mOptSurface
);
648 return surf
.forget();
650 mOptSurface
= nullptr;
653 if (mBlankRawSurface
) {
654 // We are going to return the blank surface because of the flags.
655 // We are including comments here that are copied from below
656 // just so that we are on the same page!
657 RefPtr
<SourceSurface
> surf(mBlankRawSurface
);
658 return surf
.forget();
661 RefPtr
<SourceSurface
> surf(mRawSurface
);
662 return surf
.forget();
665 void imgFrame::Abort() {
666 MonitorAutoLock
lock(mMonitor
);
670 // Wake up anyone who's waiting.
671 mMonitor
.NotifyAll();
674 bool imgFrame::IsAborted() const {
675 MonitorAutoLock
lock(mMonitor
);
679 bool imgFrame::IsFinished() const {
680 MonitorAutoLock
lock(mMonitor
);
684 void imgFrame::WaitUntilFinished() const {
685 MonitorAutoLock
lock(mMonitor
);
688 // Return if we're aborted or complete.
689 if (mAborted
|| mFinished
) {
693 // Not complete yet, so we'll have to wait.
698 bool imgFrame::AreAllPixelsWritten() const {
699 mMonitor
.AssertCurrentThreadOwns();
700 return mDecoded
.IsEqualInterior(GetRect());
703 void imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
704 const AddSizeOfCb
& aCallback
) const {
705 MonitorAutoLock
lock(mMonitor
);
707 AddSizeOfCbData metadata
;
708 metadata
.mFinished
= mFinished
;
711 metadata
.mHeapBytes
+= aMallocSizeOf(mOptSurface
);
713 SourceSurface::SizeOfInfo info
;
714 mOptSurface
->SizeOfExcludingThis(aMallocSizeOf
, info
);
715 metadata
.Accumulate(info
);
718 metadata
.mHeapBytes
+= aMallocSizeOf(mRawSurface
);
720 SourceSurface::SizeOfInfo info
;
721 mRawSurface
->SizeOfExcludingThis(aMallocSizeOf
, info
);
722 metadata
.Accumulate(info
);
729 } // namespace mozilla