Bug 1679927 Part 1: Make AppleVTDecoder check color depth and use 10-bit YUV420 when...
[gecko.git] / image / imgFrame.cpp
blobe7e1192acb07348432d5e8d81d16f6fc2dc8406e
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/. */
7 #include "imgFrame.h"
8 #include "ImageRegion.h"
9 #include "SurfaceCache.h"
11 #include "prenv.h"
13 #include "gfx2DGlue.h"
14 #include "gfxContext.h"
15 #include "gfxPlatform.h"
17 #include "gfxUtils.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"
26 #include "nsMargin.h"
27 #include "nsRefreshDriver.h"
28 #include "nsThreadUtils.h"
30 #include <algorithm> // for min, max
32 namespace mozilla {
34 using namespace gfx;
36 namespace image {
38 /**
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 {
44 public:
45 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(RecyclingSourceSurfaceSharedData,
46 override)
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;
59 if (aShouldRecycle) {
60 newSurf = new RecyclingSourceSurfaceSharedData();
61 } else {
62 newSurf = new SourceSurfaceSharedData();
64 if (!newSurf->Init(size, stride, format)) {
65 return nullptr;
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;
100 return true;
103 static bool ClearSurface(SourceSurfaceSharedData* aSurface,
104 const IntSize& aSize, SurfaceFormat aFormat) {
105 int32_t stride = aSurface->Stride();
106 uint8_t* data = aSurface->GetData();
107 MOZ_ASSERT(data);
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);
122 return true;
125 imgFrame::imgFrame()
126 : mMonitor("imgFrame"),
127 mDecoded(0, 0, 0, 0),
128 mAborted(false),
129 mFinished(false),
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() {
138 #ifdef DEBUG
139 MonitorAutoLock lock(mMonitor);
140 MOZ_ASSERT(mAborted || AreAllPixelsWritten());
141 MOZ_ASSERT(mAborted || mFinished);
142 #endif
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 mAborted = true;
154 return NS_ERROR_FAILURE;
157 mImageSize = aImageSize;
159 // May be updated shortly after InitForDecoder by BlendAnimationFilter
160 // because it needs to take into consideration the previous frames to
161 // properly calculate. We start with the whole frame as dirty.
162 mDirtyRect = GetRect();
164 if (aAnimParams) {
165 mBlendRect = aAnimParams->mBlendRect;
166 mTimeout = aAnimParams->mTimeout;
167 mBlendMethod = aAnimParams->mBlendMethod;
168 mDisposalMethod = aAnimParams->mDisposalMethod;
169 } else {
170 mBlendRect = GetRect();
173 if (aShouldRecycle) {
174 // If we are recycling then we should always use BGRA for the underlying
175 // surface because if we use BGRX, the next frame composited into the
176 // surface could be BGRA and cause rendering problems.
177 MOZ_ASSERT(aAnimParams);
178 mFormat = SurfaceFormat::OS_RGBA;
179 } else {
180 mFormat = aFormat;
183 mNonPremult = aNonPremult;
184 mShouldRecycle = aShouldRecycle;
186 MOZ_ASSERT(!mRawSurface, "Called imgFrame::InitForDecoder() twice?");
188 mRawSurface = AllocateBufferForImage(mImageSize, mFormat, mShouldRecycle);
189 if (!mRawSurface) {
190 mAborted = true;
191 return NS_ERROR_OUT_OF_MEMORY;
194 if (StaticPrefs::browser_measurement_render_anims_and_video_solid() &&
195 aAnimParams) {
196 mBlankRawSurface = AllocateBufferForImage(mImageSize, mFormat);
197 if (!mBlankRawSurface) {
198 mAborted = true;
199 return NS_ERROR_OUT_OF_MEMORY;
203 if (!ClearSurface(mRawSurface, mImageSize, mFormat)) {
204 NS_WARNING("Could not clear allocated buffer");
205 mAborted = true;
206 return NS_ERROR_OUT_OF_MEMORY;
209 if (mBlankRawSurface) {
210 if (!GreenSurface(mBlankRawSurface, mImageSize, mFormat)) {
211 NS_WARNING("Could not clear allocated blank buffer");
212 mAborted = true;
213 return NS_ERROR_OUT_OF_MEMORY;
217 return NS_OK;
220 nsresult imgFrame::InitForDecoderRecycle(const AnimationParams& aAnimParams) {
221 // We want to recycle this frame, but there is no guarantee that consumers are
222 // done with it in a timely manner. Let's ensure they are done with it first.
223 MonitorAutoLock lock(mMonitor);
225 MOZ_ASSERT(mRawSurface);
227 if (!mShouldRecycle) {
228 // This frame either was never marked as recyclable, or the flag was cleared
229 // for a caller which does not support recycling.
230 return NS_ERROR_NOT_AVAILABLE;
233 // Ensure we account for all internal references to the surface.
234 MozRefCountType internalRefs = 1;
235 if (mOptSurface == mRawSurface) {
236 ++internalRefs;
239 if (mRawSurface->refCount() > internalRefs) {
240 if (NS_IsMainThread()) {
241 // We should never be both decoding and recycling on the main thread. Sync
242 // decoding can only be used to produce the first set of frames. Those
243 // either never use recycling because advancing was blocked (main thread
244 // is busy) or we were auto-advancing (to seek to a frame) and the frames
245 // were never accessed (and thus cannot have recycle locks).
246 MOZ_ASSERT_UNREACHABLE("Recycling/decoding on the main thread?");
247 return NS_ERROR_NOT_AVAILABLE;
250 // We don't want to wait forever to reclaim the frame because we have no
251 // idea why it is still held. It is possibly due to OMTP. Since we are off
252 // the main thread, and we generally have frames already buffered for the
253 // animation, we can afford to wait a short period of time to hopefully
254 // complete the transaction and reclaim the buffer.
256 // We choose to wait for, at most, the refresh driver interval, so that we
257 // won't skip more than one frame. If the frame is still in use due to
258 // outstanding transactions, we are already skipping frames. If the frame
259 // is still in use for some other purpose, it won't be returned to the pool
260 // and its owner can hold onto it forever without additional impact here.
261 int32_t refreshInterval =
262 std::max(std::min(nsRefreshDriver::DefaultInterval(), 20), 4);
263 TimeDuration waitInterval =
264 TimeDuration::FromMilliseconds(refreshInterval >> 2);
265 TimeStamp timeout =
266 TimeStamp::Now() + TimeDuration::FromMilliseconds(refreshInterval);
267 while (true) {
268 mMonitor.Wait(waitInterval);
269 if (mRawSurface->refCount() <= internalRefs) {
270 break;
273 if (timeout <= TimeStamp::Now()) {
274 // We couldn't secure the frame for recycling. It will allocate a new
275 // frame instead.
276 return NS_ERROR_NOT_AVAILABLE;
281 mBlendRect = aAnimParams.mBlendRect;
282 mTimeout = aAnimParams.mTimeout;
283 mBlendMethod = aAnimParams.mBlendMethod;
284 mDisposalMethod = aAnimParams.mDisposalMethod;
285 mDirtyRect = GetRect();
287 return NS_OK;
290 nsresult imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
291 const nsIntSize& aSize,
292 const SurfaceFormat aFormat,
293 SamplingFilter aSamplingFilter,
294 uint32_t aImageFlags,
295 gfx::BackendType aBackend) {
296 // Assert for properties that should be verified by decoders,
297 // warn for properties related to bad content.
298 if (!SurfaceCache::IsLegalSize(aSize)) {
299 NS_WARNING("Should have legal image size");
300 mAborted = true;
301 return NS_ERROR_FAILURE;
304 mImageSize = aSize;
305 mFormat = aFormat;
307 RefPtr<DrawTarget> target;
309 bool canUseDataSurface = Factory::DoesBackendSupportDataDrawtarget(aBackend);
310 if (canUseDataSurface) {
311 // It's safe to use data surfaces for content on this platform, so we can
312 // get away with using volatile buffers.
313 MOZ_ASSERT(!mRawSurface, "Called imgFrame::InitWithDrawable() twice?");
315 mRawSurface = AllocateBufferForImage(mImageSize, mFormat);
316 if (!mRawSurface) {
317 mAborted = true;
318 return NS_ERROR_OUT_OF_MEMORY;
321 if (!ClearSurface(mRawSurface, mImageSize, mFormat)) {
322 NS_WARNING("Could not clear allocated buffer");
323 mAborted = true;
324 return NS_ERROR_OUT_OF_MEMORY;
327 target = gfxPlatform::CreateDrawTargetForData(
328 mRawSurface->GetData(), mImageSize, mRawSurface->Stride(), mFormat);
329 } else {
330 // We can't use data surfaces for content, so we'll create an offscreen
331 // surface instead. This means if someone later calls RawAccessRef(), we
332 // may have to do an expensive readback, but we warned callers about that in
333 // the documentation for this method.
334 MOZ_ASSERT(!mOptSurface, "Called imgFrame::InitWithDrawable() twice?");
336 if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(aBackend)) {
337 target = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
338 aBackend, mImageSize, mFormat);
339 } else {
340 target = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
341 mImageSize, mFormat);
345 if (!target || !target->IsValid()) {
346 mAborted = true;
347 return NS_ERROR_OUT_OF_MEMORY;
350 // Draw using the drawable the caller provided.
351 RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(target);
352 MOZ_ASSERT(ctx); // Already checked the draw target above.
353 gfxUtils::DrawPixelSnapped(ctx, aDrawable, SizeDouble(mImageSize),
354 ImageRegion::Create(ThebesRect(GetRect())),
355 mFormat, aSamplingFilter, aImageFlags);
357 if (canUseDataSurface && !mRawSurface) {
358 NS_WARNING("Failed to create SourceSurfaceSharedData");
359 mAborted = true;
360 return NS_ERROR_OUT_OF_MEMORY;
363 if (!canUseDataSurface) {
364 // We used an offscreen surface, which is an "optimized" surface from
365 // imgFrame's perspective.
366 mOptSurface = target->Snapshot();
367 } else {
368 FinalizeSurface();
371 // If we reach this point, we should regard ourselves as complete.
372 mDecoded = GetRect();
373 mFinished = true;
375 #ifdef DEBUG
376 MonitorAutoLock lock(mMonitor);
377 MOZ_ASSERT(AreAllPixelsWritten());
378 #endif
380 return NS_OK;
383 DrawableFrameRef imgFrame::DrawableRef() { return DrawableFrameRef(this); }
385 RawAccessFrameRef imgFrame::RawAccessRef() { return RawAccessFrameRef(this); }
387 imgFrame::SurfaceWithFormat imgFrame::SurfaceForDrawing(
388 bool aDoPartialDecode, bool aDoTile, ImageRegion& aRegion,
389 SourceSurface* aSurface) {
390 MOZ_ASSERT(NS_IsMainThread());
391 mMonitor.AssertCurrentThreadOwns();
393 if (!aDoPartialDecode) {
394 return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, mImageSize),
395 mFormat);
398 gfxRect available =
399 gfxRect(mDecoded.X(), mDecoded.Y(), mDecoded.Width(), mDecoded.Height());
401 if (aDoTile) {
402 // Create a temporary surface.
403 // Give this surface an alpha channel because there are
404 // transparent pixels in the padding or undecoded area
405 RefPtr<DrawTarget> target =
406 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
407 mImageSize, SurfaceFormat::OS_RGBA);
408 if (!target) {
409 return SurfaceWithFormat();
412 SurfacePattern pattern(aSurface, aRegion.GetExtendMode(),
413 Matrix::Translation(mDecoded.X(), mDecoded.Y()));
414 target->FillRect(ToRect(aRegion.Intersect(available).Rect()), pattern);
416 RefPtr<SourceSurface> newsurf = target->Snapshot();
417 return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf, mImageSize),
418 target->GetFormat());
421 // Not tiling, and we have a surface, so we can account for
422 // a partial decode just by twiddling parameters.
423 aRegion = aRegion.Intersect(available);
424 IntSize availableSize(mDecoded.Width(), mDecoded.Height());
426 return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, availableSize),
427 mFormat);
430 bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
431 SamplingFilter aSamplingFilter, uint32_t aImageFlags,
432 float aOpacity) {
433 AUTO_PROFILER_LABEL("imgFrame::Draw", GRAPHICS);
435 MOZ_ASSERT(NS_IsMainThread());
436 NS_ASSERTION(!aRegion.Rect().IsEmpty(), "Drawing empty region!");
437 NS_ASSERTION(!aRegion.IsRestricted() ||
438 !aRegion.Rect().Intersect(aRegion.Restriction()).IsEmpty(),
439 "We must be allowed to sample *some* source pixels!");
441 // Perform the draw and freeing of the surface outside the lock. We want to
442 // avoid contention with the decoder if we can. The surface may also attempt
443 // to relock the monitor if it is freed (e.g. RecyclingSourceSurface).
444 RefPtr<SourceSurface> surf;
445 SurfaceWithFormat surfaceResult;
446 ImageRegion region(aRegion);
447 gfxRect imageRect(0, 0, mImageSize.width, mImageSize.height);
450 MonitorAutoLock lock(mMonitor);
452 bool doPartialDecode = !AreAllPixelsWritten();
454 // Most draw targets will just use the surface only during DrawPixelSnapped
455 // but captures/recordings will retain a reference outside this stack
456 // context. While in theory a decoder thread could be trying to recycle this
457 // frame at this very moment, in practice the only way we can get here is if
458 // this frame is the current frame of the animation. Since we can only
459 // advance on the main thread, we know nothing else will try to use it.
460 DrawTarget* drawTarget = aContext->GetDrawTarget();
461 bool recording = drawTarget->GetBackendType() == BackendType::RECORDING;
462 RefPtr<SourceSurface> surf = GetSourceSurfaceInternal();
463 if (!surf) {
464 return false;
467 bool doTile = !imageRect.Contains(aRegion.Rect()) &&
468 !(aImageFlags & imgIContainer::FLAG_CLAMP);
470 surfaceResult = SurfaceForDrawing(doPartialDecode, doTile, region, surf);
472 // If we are recording, then we cannot recycle the surface. The blob
473 // rasterizer is not properly synchronized for recycling in the compositor
474 // process. The easiest thing to do is just mark the frames it consumes as
475 // non-recyclable.
476 if (recording && surfaceResult.IsValid()) {
477 mShouldRecycle = false;
481 if (surfaceResult.IsValid()) {
482 gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
483 imageRect.Size(), region, surfaceResult.mFormat,
484 aSamplingFilter, aImageFlags, aOpacity);
487 return true;
490 nsresult imgFrame::ImageUpdated(const nsIntRect& aUpdateRect) {
491 MonitorAutoLock lock(mMonitor);
492 return ImageUpdatedInternal(aUpdateRect);
495 nsresult imgFrame::ImageUpdatedInternal(const nsIntRect& aUpdateRect) {
496 mMonitor.AssertCurrentThreadOwns();
498 // Clamp to the frame rect to ensure that decoder bugs don't result in a
499 // decoded rect that extends outside the bounds of the frame rect.
500 IntRect updateRect = aUpdateRect.Intersect(GetRect());
501 if (updateRect.IsEmpty()) {
502 return NS_OK;
505 mDecoded.UnionRect(mDecoded, updateRect);
507 // Update our invalidation counters for any consumers watching for changes
508 // in the surface.
509 if (mRawSurface) {
510 mRawSurface->Invalidate(updateRect);
512 return NS_OK;
515 void imgFrame::Finish(Opacity aFrameOpacity /* = Opacity::SOME_TRANSPARENCY */,
516 bool aFinalize /* = true */) {
517 MonitorAutoLock lock(mMonitor);
519 IntRect frameRect(GetRect());
520 if (!mDecoded.IsEqualEdges(frameRect)) {
521 // The decoder should have produced rows starting from either the bottom or
522 // the top of the image. We need to calculate the region for which we have
523 // not yet invalidated.
524 IntRect delta(0, 0, frameRect.width, 0);
525 if (mDecoded.y == 0) {
526 delta.y = mDecoded.height;
527 delta.height = frameRect.height - mDecoded.height;
528 } else if (mDecoded.y + mDecoded.height == frameRect.height) {
529 delta.height = frameRect.height - mDecoded.y;
530 } else {
531 MOZ_ASSERT_UNREACHABLE("Decoder only updated middle of image!");
532 delta = frameRect;
535 ImageUpdatedInternal(delta);
538 MOZ_ASSERT(mDecoded.IsEqualEdges(frameRect));
540 if (aFinalize) {
541 FinalizeSurfaceInternal();
544 mFinished = true;
546 // The image is now complete, wake up anyone who's waiting.
547 mMonitor.NotifyAll();
550 uint32_t imgFrame::GetImageBytesPerRow() const {
551 mMonitor.AssertCurrentThreadOwns();
553 if (mRawSurface) {
554 return mImageSize.width * BytesPerPixel(mFormat);
557 return 0;
560 uint32_t imgFrame::GetImageDataLength() const {
561 return GetImageBytesPerRow() * mImageSize.height;
564 void imgFrame::GetImageData(uint8_t** aData, uint32_t* aLength) const {
565 MonitorAutoLock lock(mMonitor);
566 GetImageDataInternal(aData, aLength);
569 void imgFrame::GetImageDataInternal(uint8_t** aData, uint32_t* aLength) const {
570 mMonitor.AssertCurrentThreadOwns();
571 MOZ_ASSERT(mRawSurface);
573 if (mRawSurface) {
574 // TODO: This is okay for now because we only realloc shared surfaces on
575 // the main thread after decoding has finished, but if animations want to
576 // read frame data off the main thread, we will need to reconsider this.
577 *aData = mRawSurface->GetData();
578 MOZ_ASSERT(*aData,
579 "mRawSurface is non-null, but GetData is null in GetImageData");
580 } else {
581 *aData = nullptr;
584 *aLength = GetImageDataLength();
587 uint8_t* imgFrame::GetImageData() const {
588 uint8_t* data;
589 uint32_t length;
590 GetImageData(&data, &length);
591 return data;
594 void imgFrame::FinalizeSurface() {
595 MonitorAutoLock lock(mMonitor);
596 FinalizeSurfaceInternal();
599 void imgFrame::FinalizeSurfaceInternal() {
600 mMonitor.AssertCurrentThreadOwns();
602 // Not all images will have mRawSurface to finalize (i.e. paletted images).
603 if (mShouldRecycle || !mRawSurface ||
604 mRawSurface->GetType() != SurfaceType::DATA_SHARED) {
605 return;
608 auto* sharedSurf = static_cast<SourceSurfaceSharedData*>(mRawSurface.get());
609 sharedSurf->Finalize();
612 already_AddRefed<SourceSurface> imgFrame::GetSourceSurface() {
613 MonitorAutoLock lock(mMonitor);
614 return GetSourceSurfaceInternal();
617 already_AddRefed<SourceSurface> imgFrame::GetSourceSurfaceInternal() {
618 mMonitor.AssertCurrentThreadOwns();
620 if (mOptSurface) {
621 if (mOptSurface->IsValid()) {
622 RefPtr<SourceSurface> surf(mOptSurface);
623 return surf.forget();
625 mOptSurface = nullptr;
628 if (mBlankRawSurface) {
629 // We are going to return the blank surface because of the flags.
630 // We are including comments here that are copied from below
631 // just so that we are on the same page!
632 RefPtr<SourceSurface> surf(mBlankRawSurface);
633 return surf.forget();
636 RefPtr<SourceSurface> surf(mRawSurface);
637 return surf.forget();
640 void imgFrame::Abort() {
641 MonitorAutoLock lock(mMonitor);
643 mAborted = true;
645 // Wake up anyone who's waiting.
646 mMonitor.NotifyAll();
649 bool imgFrame::IsAborted() const {
650 MonitorAutoLock lock(mMonitor);
651 return mAborted;
654 bool imgFrame::IsFinished() const {
655 MonitorAutoLock lock(mMonitor);
656 return mFinished;
659 void imgFrame::WaitUntilFinished() const {
660 MonitorAutoLock lock(mMonitor);
662 while (true) {
663 // Return if we're aborted or complete.
664 if (mAborted || mFinished) {
665 return;
668 // Not complete yet, so we'll have to wait.
669 mMonitor.Wait();
673 bool imgFrame::AreAllPixelsWritten() const {
674 mMonitor.AssertCurrentThreadOwns();
675 return mDecoded.IsEqualInterior(GetRect());
678 void imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
679 const AddSizeOfCb& aCallback) const {
680 MonitorAutoLock lock(mMonitor);
682 AddSizeOfCbData metadata;
683 metadata.mFinished = mFinished;
685 if (mOptSurface) {
686 metadata.mHeapBytes += aMallocSizeOf(mOptSurface);
688 SourceSurface::SizeOfInfo info;
689 mOptSurface->SizeOfExcludingThis(aMallocSizeOf, info);
690 metadata.Accumulate(info);
692 if (mRawSurface) {
693 metadata.mHeapBytes += aMallocSizeOf(mRawSurface);
695 SourceSurface::SizeOfInfo info;
696 mRawSurface->SizeOfExcludingThis(aMallocSizeOf, info);
697 metadata.Accumulate(info);
700 aCallback(metadata);
703 } // namespace image
704 } // namespace mozilla