Bug 1859742 [wpt PR 42597] - Update wpt metadata, a=testonly
[gecko.git] / image / RasterImage.cpp
blob775238b4609edbbc50703ee3b1291a39d651e9ef
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/. */
6 // Must #include ImageLogging.h before any IPDL-generated files or other files
7 // that #include prlog.h
8 #include "RasterImage.h"
10 #include <stdint.h>
12 #include <algorithm>
13 #include <utility>
15 #include "DecodePool.h"
16 #include "Decoder.h"
17 #include "FrameAnimator.h"
18 #include "GeckoProfiler.h"
19 #include "IDecodingTask.h"
20 #include "ImageLogging.h"
21 #include "ImageRegion.h"
22 #include "LookupResult.h"
23 #include "OrientedImage.h"
24 #include "SourceBuffer.h"
25 #include "SurfaceCache.h"
26 #include "gfx2DGlue.h"
27 #include "gfxContext.h"
28 #include "gfxPlatform.h"
29 #include "mozilla/ClearOnShutdown.h"
30 #include "mozilla/DebugOnly.h"
31 #include "mozilla/Likely.h"
32 #include "mozilla/MemoryReporting.h"
33 #include "mozilla/RefPtr.h"
34 #include "mozilla/SizeOfState.h"
35 #include "mozilla/StaticPrefs_image.h"
36 #include "mozilla/Telemetry.h"
37 #include "mozilla/TimeStamp.h"
39 #include "mozilla/gfx/2D.h"
40 #include "mozilla/gfx/Scale.h"
41 #include "nsComponentManagerUtils.h"
42 #include "nsError.h"
43 #include "nsIConsoleService.h"
44 #include "nsIInputStream.h"
45 #include "nsIScriptError.h"
46 #include "nsISupportsPrimitives.h"
47 #include "nsMemory.h"
48 #include "nsPresContext.h"
49 #include "nsProperties.h"
50 #include "prenv.h"
51 #include "prsystem.h"
52 #include "WindowRenderer.h"
54 namespace mozilla {
56 using namespace gfx;
57 using namespace layers;
59 namespace image {
61 using std::ceil;
62 using std::min;
64 #ifndef DEBUG
65 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer)
66 #else
67 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, imgIContainerDebug)
68 #endif
70 //******************************************************************************
71 RasterImage::RasterImage(nsIURI* aURI /* = nullptr */)
72 : ImageResource(aURI), // invoke superclass's constructor
73 mSize(0, 0),
74 mLockCount(0),
75 mDecoderType(DecoderType::UNKNOWN),
76 mDecodeCount(0),
77 #ifdef DEBUG
78 mFramesNotified(0),
79 #endif
80 mSourceBuffer(MakeNotNull<SourceBuffer*>()) {
83 //******************************************************************************
84 RasterImage::~RasterImage() {
85 // Make sure our SourceBuffer is marked as complete. This will ensure that any
86 // outstanding decoders terminate.
87 if (!mSourceBuffer->IsComplete()) {
88 mSourceBuffer->Complete(NS_ERROR_ABORT);
91 // Release all frames from the surface cache.
92 SurfaceCache::RemoveImage(ImageKey(this));
94 // Record Telemetry.
95 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_COUNT, mDecodeCount);
98 nsresult RasterImage::Init(const char* aMimeType, uint32_t aFlags) {
99 // We don't support re-initialization
100 if (mInitialized) {
101 return NS_ERROR_ILLEGAL_VALUE;
104 // Not sure an error can happen before init, but be safe
105 if (mError) {
106 return NS_ERROR_FAILURE;
109 // We want to avoid redecodes for transient images.
110 MOZ_ASSERT_IF(aFlags & INIT_FLAG_TRANSIENT,
111 !(aFlags & INIT_FLAG_DISCARDABLE));
113 // Store initialization data
114 StoreDiscardable(!!(aFlags & INIT_FLAG_DISCARDABLE));
115 StoreWantFullDecode(!!(aFlags & INIT_FLAG_DECODE_IMMEDIATELY));
116 StoreTransient(!!(aFlags & INIT_FLAG_TRANSIENT));
117 StoreSyncLoad(!!(aFlags & INIT_FLAG_SYNC_LOAD));
119 // Use the MIME type to select a decoder type, and make sure there *is* a
120 // decoder for this MIME type.
121 NS_ENSURE_ARG_POINTER(aMimeType);
122 mDecoderType = DecoderFactory::GetDecoderType(aMimeType);
123 if (mDecoderType == DecoderType::UNKNOWN) {
124 return NS_ERROR_FAILURE;
127 // Lock this image's surfaces in the SurfaceCache if we're not discardable.
128 if (!LoadDiscardable()) {
129 mLockCount++;
130 SurfaceCache::LockImage(ImageKey(this));
133 // Set the default flags according to the decoder type to allow preferences to
134 // be stored if necessary.
135 mDefaultDecoderFlags =
136 DecoderFactory::GetDefaultDecoderFlagsForType(mDecoderType);
138 // Mark us as initialized
139 mInitialized = true;
141 return NS_OK;
144 //******************************************************************************
145 NS_IMETHODIMP_(void)
146 RasterImage::RequestRefresh(const TimeStamp& aTime) {
147 if (HadRecentRefresh(aTime)) {
148 return;
151 EvaluateAnimation();
153 if (!mAnimating) {
154 return;
157 RefreshResult res;
158 if (mAnimationState) {
159 MOZ_ASSERT(mFrameAnimator);
160 res = mFrameAnimator->RequestRefresh(*mAnimationState, aTime);
163 #ifdef DEBUG
164 if (res.mFrameAdvanced) {
165 mFramesNotified++;
167 #endif
169 // Notify listeners that our frame has actually changed, but do this only
170 // once for all frames that we've now passed (if AdvanceFrame() was called
171 // more than once).
172 if (!res.mDirtyRect.IsEmpty() || res.mFrameAdvanced) {
173 auto dirtyRect = OrientedIntRect::FromUnknownRect(res.mDirtyRect);
174 NotifyProgress(NoProgress, dirtyRect);
177 if (res.mAnimationFinished) {
178 StoreAnimationFinished(true);
179 EvaluateAnimation();
183 //******************************************************************************
184 NS_IMETHODIMP
185 RasterImage::GetWidth(int32_t* aWidth) {
186 NS_ENSURE_ARG_POINTER(aWidth);
188 if (mError) {
189 *aWidth = 0;
190 return NS_ERROR_FAILURE;
193 *aWidth = mSize.width;
194 return NS_OK;
197 //******************************************************************************
198 NS_IMETHODIMP
199 RasterImage::GetHeight(int32_t* aHeight) {
200 NS_ENSURE_ARG_POINTER(aHeight);
202 if (mError) {
203 *aHeight = 0;
204 return NS_ERROR_FAILURE;
207 *aHeight = mSize.height;
208 return NS_OK;
211 //******************************************************************************
212 void RasterImage::MediaFeatureValuesChangedAllDocuments(
213 const mozilla::MediaFeatureChange& aChange) {}
215 //******************************************************************************
216 nsresult RasterImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) {
217 if (mError) {
218 return NS_ERROR_FAILURE;
221 aNativeSizes.Clear();
223 if (mNativeSizes.IsEmpty()) {
224 aNativeSizes.AppendElement(mSize.ToUnknownSize());
225 } else {
226 for (const auto& size : mNativeSizes) {
227 aNativeSizes.AppendElement(size.ToUnknownSize());
231 return NS_OK;
234 //******************************************************************************
235 size_t RasterImage::GetNativeSizesLength() {
236 if (mError || !LoadHasSize()) {
237 return 0;
240 if (mNativeSizes.IsEmpty()) {
241 return 1;
244 return mNativeSizes.Length();
247 //******************************************************************************
248 NS_IMETHODIMP
249 RasterImage::GetIntrinsicSize(nsSize* aSize) {
250 if (mError) {
251 return NS_ERROR_FAILURE;
254 *aSize = nsSize(nsPresContext::CSSPixelsToAppUnits(mSize.width),
255 nsPresContext::CSSPixelsToAppUnits(mSize.height));
256 return NS_OK;
259 //******************************************************************************
260 Maybe<AspectRatio> RasterImage::GetIntrinsicRatio() {
261 if (mError) {
262 return Nothing();
265 return Some(AspectRatio::FromSize(mSize.width, mSize.height));
268 NS_IMETHODIMP_(Orientation)
269 RasterImage::GetOrientation() { return mOrientation; }
271 NS_IMETHODIMP_(Resolution)
272 RasterImage::GetResolution() { return mResolution; }
274 //******************************************************************************
275 NS_IMETHODIMP
276 RasterImage::GetType(uint16_t* aType) {
277 NS_ENSURE_ARG_POINTER(aType);
279 *aType = imgIContainer::TYPE_RASTER;
280 return NS_OK;
283 NS_IMETHODIMP
284 RasterImage::GetProviderId(uint32_t* aId) {
285 NS_ENSURE_ARG_POINTER(aId);
287 *aId = ImageResource::GetImageProviderId();
288 return NS_OK;
291 LookupResult RasterImage::LookupFrameInternal(const OrientedIntSize& aSize,
292 uint32_t aFlags,
293 PlaybackType aPlaybackType,
294 bool aMarkUsed) {
295 if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
296 MOZ_ASSERT(mFrameAnimator);
297 MOZ_ASSERT(ToSurfaceFlags(aFlags) == DefaultSurfaceFlags(),
298 "Can't composite frames with non-default surface flags");
299 return mFrameAnimator->GetCompositedFrame(*mAnimationState, aMarkUsed);
302 SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
304 // We don't want any substitution for sync decodes, and substitution would be
305 // illegal when high quality downscaling is disabled, so we use
306 // SurfaceCache::Lookup in this case.
307 if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
308 return SurfaceCache::Lookup(
309 ImageKey(this),
310 RasterSurfaceKey(aSize.ToUnknownSize(), surfaceFlags,
311 PlaybackType::eStatic),
312 aMarkUsed);
315 // We'll return the best match we can find to the requested frame.
316 return SurfaceCache::LookupBestMatch(
317 ImageKey(this),
318 RasterSurfaceKey(aSize.ToUnknownSize(), surfaceFlags,
319 PlaybackType::eStatic),
320 aMarkUsed);
323 LookupResult RasterImage::LookupFrame(const OrientedIntSize& aSize,
324 uint32_t aFlags,
325 PlaybackType aPlaybackType,
326 bool aMarkUsed) {
327 MOZ_ASSERT(NS_IsMainThread());
329 // If we're opaque, we don't need to care about premultiplied alpha, because
330 // that can only matter for frames with transparency.
331 if (IsOpaque()) {
332 aFlags &= ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
335 OrientedIntSize requestedSize =
336 CanDownscaleDuringDecode(aSize, aFlags) ? aSize : mSize;
337 if (requestedSize.IsEmpty()) {
338 // Can't decode to a surface of zero size.
339 return LookupResult(MatchType::NOT_FOUND);
342 LookupResult result =
343 LookupFrameInternal(requestedSize, aFlags, aPlaybackType, aMarkUsed);
345 if (!result && !LoadHasSize()) {
346 // We can't request a decode without knowing our intrinsic size. Give up.
347 return LookupResult(MatchType::NOT_FOUND);
350 // We want to trigger a decode if and only if:
351 // 1) There is no pending decode
352 // 2) There is no acceptable size decoded
353 // 3) The pending decode has not produced a frame yet, a sync decode is
354 // requested, and we have all the source data. Without the source data, we
355 // will just trigger another async decode anyways.
357 // TODO(aosmond): We should better handle case 3. We should actually return
358 // TEMPORARY_ERROR or NOT_READY if we don't have all the source data and a
359 // sync decode is requested. If there is a pending decode and we have all the
360 // source data, we should always be able to block on the frame's monitor --
361 // perhaps this could be accomplished by preallocating the first frame buffer
362 // when we create the decoder.
363 const bool syncDecode = aFlags & FLAG_SYNC_DECODE;
364 const bool avoidRedecode = aFlags & FLAG_AVOID_REDECODE_FOR_SIZE;
365 if (result.Type() == MatchType::NOT_FOUND ||
366 (result.Type() == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND &&
367 !avoidRedecode) ||
368 (syncDecode && !avoidRedecode && !result && LoadAllSourceData())) {
369 // We don't have a copy of this frame, and there's no decoder working on
370 // one. (Or we're sync decoding and the existing decoder hasn't even started
371 // yet.) Trigger decoding so it'll be available next time.
372 MOZ_ASSERT(aPlaybackType != PlaybackType::eAnimated ||
373 StaticPrefs::image_mem_animated_discardable_AtStartup() ||
374 !mAnimationState || mAnimationState->KnownFrameCount() < 1,
375 "Animated frames should be locked");
377 // The surface cache may suggest the preferred size we are supposed to
378 // decode at. This should only happen if we accept substitutions.
379 if (!result.SuggestedSize().IsEmpty()) {
380 MOZ_ASSERT(!syncDecode && (aFlags & FLAG_HIGH_QUALITY_SCALING));
381 requestedSize = OrientedIntSize::FromUnknownSize(result.SuggestedSize());
384 bool ranSync = false, failed = false;
385 Decode(requestedSize, aFlags, aPlaybackType, ranSync, failed);
386 if (failed) {
387 result.SetFailedToRequestDecode();
390 // If we can or did sync decode, we should already have the frame.
391 if (ranSync || syncDecode) {
392 result =
393 LookupFrameInternal(requestedSize, aFlags, aPlaybackType, aMarkUsed);
397 if (!result) {
398 // We still weren't able to get a frame. Give up.
399 return result;
402 // Sync decoding guarantees that we got the frame, but if it's owned by an
403 // async decoder that's currently running, the contents of the frame may not
404 // be available yet. Make sure we get everything.
405 if (LoadAllSourceData() && syncDecode) {
406 result.Surface()->WaitUntilFinished();
409 // If we could have done some decoding in this function we need to check if
410 // that decoding encountered an error and hence aborted the surface. We want
411 // to avoid calling IsAborted if we weren't passed any sync decode flag
412 // because IsAborted acquires the monitor for the imgFrame.
413 if (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST) &&
414 result.Surface()->IsAborted()) {
415 DrawableSurface tmp = std::move(result.Surface());
416 return result;
419 return result;
422 bool RasterImage::IsOpaque() {
423 if (mError) {
424 return false;
427 Progress progress = mProgressTracker->GetProgress();
429 // If we haven't yet finished decoding, the safe answer is "not opaque".
430 if (!(progress & FLAG_DECODE_COMPLETE)) {
431 return false;
434 // Other, we're opaque if FLAG_HAS_TRANSPARENCY is not set.
435 return !(progress & FLAG_HAS_TRANSPARENCY);
438 NS_IMETHODIMP_(bool)
439 RasterImage::WillDrawOpaqueNow() {
440 if (!IsOpaque()) {
441 return false;
444 if (mAnimationState) {
445 if (!StaticPrefs::image_mem_animated_discardable_AtStartup()) {
446 // We never discard frames of animated images.
447 return true;
448 } else {
449 if (mAnimationState->GetCompositedFrameInvalid()) {
450 // We're not going to draw anything at all.
451 return false;
456 // If we are not locked our decoded data could get discard at any time (ie
457 // between the call to this function and when we are asked to draw), so we
458 // have to return false if we are unlocked.
459 if (mLockCount == 0) {
460 return false;
463 LookupResult result = SurfaceCache::LookupBestMatch(
464 ImageKey(this),
465 RasterSurfaceKey(mSize.ToUnknownSize(), DefaultSurfaceFlags(),
466 PlaybackType::eStatic),
467 /* aMarkUsed = */ false);
468 MatchType matchType = result.Type();
469 if (matchType == MatchType::NOT_FOUND || matchType == MatchType::PENDING ||
470 !result.Surface()->IsFinished()) {
471 return false;
474 return true;
477 void RasterImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) {
478 MOZ_ASSERT(mProgressTracker);
480 bool animatedFramesDiscarded =
481 mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated;
483 nsCOMPtr<nsIEventTarget> eventTarget;
484 if (mProgressTracker) {
485 eventTarget = mProgressTracker->GetEventTarget();
486 } else {
487 eventTarget = do_GetMainThread();
490 RefPtr<RasterImage> image = this;
491 nsCOMPtr<nsIRunnable> ev =
492 NS_NewRunnableFunction("RasterImage::OnSurfaceDiscarded", [=]() -> void {
493 image->OnSurfaceDiscardedInternal(animatedFramesDiscarded);
495 eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
498 void RasterImage::OnSurfaceDiscardedInternal(bool aAnimatedFramesDiscarded) {
499 MOZ_ASSERT(NS_IsMainThread());
501 if (aAnimatedFramesDiscarded && mAnimationState) {
502 MOZ_ASSERT(StaticPrefs::image_mem_animated_discardable_AtStartup());
504 IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize());
506 auto dirtyRect = OrientedIntRect::FromUnknownRect(rect);
507 NotifyProgress(NoProgress, dirtyRect);
510 if (mProgressTracker) {
511 mProgressTracker->OnDiscard();
515 //******************************************************************************
516 NS_IMETHODIMP
517 RasterImage::GetAnimated(bool* aAnimated) {
518 if (mError) {
519 return NS_ERROR_FAILURE;
522 NS_ENSURE_ARG_POINTER(aAnimated);
524 // If we have an AnimationState, we can know for sure.
525 if (mAnimationState) {
526 *aAnimated = true;
527 return NS_OK;
530 // Otherwise, we need to have been decoded to know for sure, since if we were
531 // decoded at least once mAnimationState would have been created for animated
532 // images. This is true even though we check for animation during the
533 // metadata decode, because we may still discover animation only during the
534 // full decode for corrupt images.
535 if (!LoadHasBeenDecoded()) {
536 return NS_ERROR_NOT_AVAILABLE;
539 // We know for sure
540 *aAnimated = false;
542 return NS_OK;
545 //******************************************************************************
546 NS_IMETHODIMP_(int32_t)
547 RasterImage::GetFirstFrameDelay() {
548 if (mError) {
549 return -1;
552 bool animated = false;
553 if (NS_FAILED(GetAnimated(&animated)) || !animated) {
554 return -1;
557 MOZ_ASSERT(mAnimationState, "Animated images should have an AnimationState");
558 return mAnimationState->FirstFrameTimeout().AsEncodedValueDeprecated();
561 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
562 RasterImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) {
563 return GetFrameAtSize(mSize.ToUnknownSize(), aWhichFrame, aFlags);
566 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
567 RasterImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame,
568 uint32_t aFlags) {
569 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
571 AutoProfilerImagePaintMarker PROFILER_RAII(this);
572 #ifdef DEBUG
573 NotifyDrawingObservers();
574 #endif
576 if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE || mError) {
577 return nullptr;
580 auto size = OrientedIntSize::FromUnknownSize(aSize);
582 // Get the frame. If it's not there, it's probably the caller's fault for
583 // not waiting for the data to be loaded from the network or not passing
584 // FLAG_SYNC_DECODE.
585 LookupResult result = LookupFrame(size, aFlags, ToPlaybackType(aWhichFrame),
586 /* aMarkUsed = */ true);
587 if (!result) {
588 // The OS threw this frame away and we couldn't redecode it.
589 return nullptr;
592 return result.Surface()->GetSourceSurface();
595 NS_IMETHODIMP_(bool)
596 RasterImage::IsImageContainerAvailable(WindowRenderer* aRenderer,
597 uint32_t aFlags) {
598 return LoadHasSize();
601 NS_IMETHODIMP_(ImgDrawResult)
602 RasterImage::GetImageProvider(WindowRenderer* aRenderer,
603 const gfx::IntSize& aSize,
604 const SVGImageContext& aSVGContext,
605 const Maybe<ImageIntRegion>& aRegion,
606 uint32_t aFlags,
607 WebRenderImageProvider** aProvider) {
608 MOZ_ASSERT(NS_IsMainThread());
609 MOZ_ASSERT(aRenderer);
611 if (mError) {
612 return ImgDrawResult::BAD_IMAGE;
615 if (!LoadHasSize()) {
616 return ImgDrawResult::NOT_READY;
619 if (aSize.IsEmpty()) {
620 return ImgDrawResult::BAD_ARGS;
623 // We check the minimum size because while we support downscaling, we do not
624 // support upscaling. If aRequestedSize > mSize, we will never give a larger
625 // surface than mSize. If mSize > aRequestedSize, and mSize > maxTextureSize,
626 // we still want to use image containers if aRequestedSize <= maxTextureSize.
627 int32_t maxTextureSize = aRenderer->GetMaxTextureSize();
628 if (min(mSize.width, aSize.width) > maxTextureSize ||
629 min(mSize.height, aSize.height) > maxTextureSize) {
630 return ImgDrawResult::NOT_SUPPORTED;
633 AutoProfilerImagePaintMarker PROFILER_RAII(this);
634 #ifdef DEBUG
635 NotifyDrawingObservers();
636 #endif
638 // Get the frame. If it's not there, it's probably the caller's fault for
639 // not waiting for the data to be loaded from the network or not passing
640 // FLAG_SYNC_DECODE.
641 LookupResult result = LookupFrame(OrientedIntSize::FromUnknownSize(aSize),
642 aFlags, PlaybackType::eAnimated,
643 /* aMarkUsed = */ true);
644 if (!result) {
645 // The OS threw this frame away and we couldn't redecode it.
646 return ImgDrawResult::NOT_READY;
649 if (!result.Surface()->IsFinished()) {
650 result.Surface().TakeProvider(aProvider);
651 return ImgDrawResult::INCOMPLETE;
654 result.Surface().TakeProvider(aProvider);
655 switch (result.Type()) {
656 case MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND:
657 case MatchType::SUBSTITUTE_BECAUSE_PENDING:
658 return ImgDrawResult::WRONG_SIZE;
659 default:
660 return ImgDrawResult::SUCCESS;
664 size_t RasterImage::SizeOfSourceWithComputedFallback(
665 SizeOfState& aState) const {
666 return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(
667 aState.mMallocSizeOf);
670 bool RasterImage::SetMetadata(const ImageMetadata& aMetadata,
671 bool aFromMetadataDecode) {
672 MOZ_ASSERT(NS_IsMainThread());
674 if (mError) {
675 return true;
678 mResolution = aMetadata.GetResolution();
680 if (aMetadata.HasSize()) {
681 auto metadataSize = aMetadata.GetSize();
682 if (metadataSize.width < 0 || metadataSize.height < 0) {
683 NS_WARNING("Image has negative intrinsic size");
684 DoError();
685 return true;
688 MOZ_ASSERT(aMetadata.HasOrientation());
689 Orientation orientation = aMetadata.GetOrientation();
691 // If we already have a size, check the new size against the old one.
692 if (LoadHasSize() &&
693 (metadataSize != mSize || orientation != mOrientation)) {
694 NS_WARNING(
695 "Image changed size or orientation on redecode! "
696 "This should not happen!");
697 DoError();
698 return true;
701 // Set the size and flag that we have it.
702 mOrientation = orientation;
703 mSize = metadataSize;
704 mNativeSizes.Clear();
705 for (const auto& nativeSize : aMetadata.GetNativeSizes()) {
706 mNativeSizes.AppendElement(nativeSize);
708 StoreHasSize(true);
711 if (LoadHasSize() && aMetadata.HasAnimation() && !mAnimationState) {
712 // We're becoming animated, so initialize animation stuff.
713 mAnimationState.emplace(mAnimationMode);
714 mFrameAnimator = MakeUnique<FrameAnimator>(this, mSize.ToUnknownSize());
716 if (!StaticPrefs::image_mem_animated_discardable_AtStartup()) {
717 // We don't support discarding animated images (See bug 414259).
718 // Lock the image and throw away the key.
719 LockImage();
722 if (!aFromMetadataDecode) {
723 // The metadata decode reported that this image isn't animated, but we
724 // discovered that it actually was during the full decode. This is a
725 // rare failure that only occurs for corrupt images. To recover, we need
726 // to discard all existing surfaces and redecode.
727 return false;
731 if (mAnimationState) {
732 mAnimationState->SetLoopCount(aMetadata.GetLoopCount());
733 mAnimationState->SetFirstFrameTimeout(aMetadata.GetFirstFrameTimeout());
735 if (aMetadata.HasLoopLength()) {
736 mAnimationState->SetLoopLength(aMetadata.GetLoopLength());
738 if (aMetadata.HasFirstFrameRefreshArea()) {
739 mAnimationState->SetFirstFrameRefreshArea(
740 aMetadata.GetFirstFrameRefreshArea());
744 if (aMetadata.HasHotspot()) {
745 // NOTE(heycam): We shouldn't have any image formats that support both
746 // orientation and hotspots, so we assert that rather than add code
747 // to orient the hotspot point correctly.
748 MOZ_ASSERT(mOrientation.IsIdentity(), "Would need to orient hotspot point");
750 auto hotspot = aMetadata.GetHotspot();
751 mHotspot.x = std::max(std::min(hotspot.x.value, mSize.width - 1), 0);
752 mHotspot.y = std::max(std::min(hotspot.y.value, mSize.height - 1), 0);
755 return true;
758 NS_IMETHODIMP
759 RasterImage::SetAnimationMode(uint16_t aAnimationMode) {
760 if (mAnimationState) {
761 mAnimationState->SetAnimationMode(aAnimationMode);
763 return SetAnimationModeInternal(aAnimationMode);
766 //******************************************************************************
768 nsresult RasterImage::StartAnimation() {
769 if (mError) {
770 return NS_ERROR_FAILURE;
773 MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
775 // If we're not ready to animate, then set mPendingAnimation, which will cause
776 // us to start animating if and when we do become ready.
777 StorePendingAnimation(!mAnimationState ||
778 mAnimationState->KnownFrameCount() < 1);
779 if (LoadPendingAnimation()) {
780 return NS_OK;
783 // Don't bother to animate if we're displaying the first frame forever.
784 if (mAnimationState->GetCurrentAnimationFrameIndex() == 0 &&
785 mAnimationState->FirstFrameTimeout() == FrameTimeout::Forever()) {
786 StoreAnimationFinished(true);
787 return NS_ERROR_ABORT;
790 // We need to set the time that this initial frame was first displayed, as
791 // this is used in AdvanceFrame().
792 mAnimationState->InitAnimationFrameTimeIfNecessary();
794 return NS_OK;
797 //******************************************************************************
798 nsresult RasterImage::StopAnimation() {
799 MOZ_ASSERT(mAnimating, "Should be animating!");
801 nsresult rv = NS_OK;
802 if (mError) {
803 rv = NS_ERROR_FAILURE;
804 } else {
805 mAnimationState->SetAnimationFrameTime(TimeStamp());
808 mAnimating = false;
809 return rv;
812 //******************************************************************************
813 NS_IMETHODIMP
814 RasterImage::ResetAnimation() {
815 if (mError) {
816 return NS_ERROR_FAILURE;
819 StorePendingAnimation(false);
821 if (mAnimationMode == kDontAnimMode || !mAnimationState ||
822 mAnimationState->GetCurrentAnimationFrameIndex() == 0) {
823 return NS_OK;
826 StoreAnimationFinished(false);
828 if (mAnimating) {
829 StopAnimation();
832 MOZ_ASSERT(mAnimationState, "Should have AnimationState");
833 MOZ_ASSERT(mFrameAnimator, "Should have FrameAnimator");
834 mFrameAnimator->ResetAnimation(*mAnimationState);
836 IntRect area = mAnimationState->FirstFrameRefreshArea();
837 NotifyProgress(NoProgress, OrientedIntRect::FromUnknownRect(area));
839 // Start the animation again. It may not have been running before, if
840 // mAnimationFinished was true before entering this function.
841 EvaluateAnimation();
843 return NS_OK;
846 //******************************************************************************
847 NS_IMETHODIMP_(void)
848 RasterImage::SetAnimationStartTime(const TimeStamp& aTime) {
849 if (mError || mAnimationMode == kDontAnimMode || mAnimating ||
850 !mAnimationState) {
851 return;
854 mAnimationState->SetAnimationFrameTime(aTime);
857 NS_IMETHODIMP_(float)
858 RasterImage::GetFrameIndex(uint32_t aWhichFrame) {
859 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
860 return (aWhichFrame == FRAME_FIRST || !mAnimationState)
861 ? 0.0f
862 : mAnimationState->GetCurrentAnimationFrameIndex();
865 NS_IMETHODIMP_(IntRect)
866 RasterImage::GetImageSpaceInvalidationRect(const IntRect& aRect) {
867 // Note that we do not transform aRect into an UnorientedIntRect, since
868 // RasterImage::NotifyProgress notifies all consumers of the image using
869 // OrientedIntRect values. (This is unlike OrientedImage, which notifies
870 // using inner image coordinates.)
871 return aRect;
874 nsresult RasterImage::OnImageDataComplete(nsIRequest*, nsresult aStatus,
875 bool aLastPart) {
876 MOZ_ASSERT(NS_IsMainThread());
878 // Record that we have all the data we're going to get now.
879 StoreAllSourceData(true);
881 // Let decoders know that there won't be any more data coming.
882 mSourceBuffer->Complete(aStatus);
884 // Allow a synchronous metadata decode if mSyncLoad was set, or if we're
885 // running on a single thread (in which case waiting for the async metadata
886 // decoder could delay this image's load event quite a bit), or if this image
887 // is transient.
888 bool canSyncDecodeMetadata =
889 LoadSyncLoad() || LoadTransient() || DecodePool::NumberOfCores() < 2;
891 if (canSyncDecodeMetadata && !LoadHasSize()) {
892 // We're loading this image synchronously, so it needs to be usable after
893 // this call returns. Since we haven't gotten our size yet, we need to do a
894 // synchronous metadata decode here.
895 DecodeMetadata(FLAG_SYNC_DECODE);
898 // Determine our final status, giving precedence to Necko failure codes. We
899 // check after running the metadata decode in case it triggered an error.
900 nsresult finalStatus = mError ? NS_ERROR_FAILURE : NS_OK;
901 if (NS_FAILED(aStatus)) {
902 finalStatus = aStatus;
905 // If loading failed, report an error.
906 if (NS_FAILED(finalStatus)) {
907 DoError();
910 Progress loadProgress = LoadCompleteProgress(aLastPart, mError, finalStatus);
912 if (!LoadHasSize() && !mError) {
913 // We don't have our size yet, so we'll fire the load event in SetSize().
914 MOZ_ASSERT(!canSyncDecodeMetadata,
915 "Firing load async after metadata sync decode?");
916 mLoadProgress = Some(loadProgress);
917 return finalStatus;
920 NotifyForLoadEvent(loadProgress);
922 return finalStatus;
925 void RasterImage::NotifyForLoadEvent(Progress aProgress) {
926 MOZ_ASSERT(LoadHasSize() || mError,
927 "Need to know size before firing load event");
928 MOZ_ASSERT(
929 !LoadHasSize() || (mProgressTracker->GetProgress() & FLAG_SIZE_AVAILABLE),
930 "Should have notified that the size is available if we have it");
932 // If we encountered an error, make sure we notify for that as well.
933 if (mError) {
934 aProgress |= FLAG_HAS_ERROR;
937 // Notify our listeners, which will fire this image's load event.
938 NotifyProgress(aProgress);
941 nsresult RasterImage::OnImageDataAvailable(nsIRequest*,
942 nsIInputStream* aInputStream,
943 uint64_t, uint32_t aCount) {
944 nsresult rv = mSourceBuffer->AppendFromInputStream(aInputStream, aCount);
945 if (NS_SUCCEEDED(rv) && !LoadSomeSourceData()) {
946 StoreSomeSourceData(true);
947 if (!LoadSyncLoad()) {
948 // Create an async metadata decoder and verify we succeed in doing so.
949 rv = DecodeMetadata(DECODE_FLAGS_DEFAULT);
953 if (NS_FAILED(rv)) {
954 DoError();
956 return rv;
959 nsresult RasterImage::SetSourceSizeHint(uint32_t aSizeHint) {
960 if (aSizeHint == 0) {
961 return NS_OK;
964 nsresult rv = mSourceBuffer->ExpectLength(aSizeHint);
965 if (rv == NS_ERROR_OUT_OF_MEMORY) {
966 // Flush memory, try to get some back, and try again.
967 rv = nsMemory::HeapMinimize(true);
968 if (NS_SUCCEEDED(rv)) {
969 rv = mSourceBuffer->ExpectLength(aSizeHint);
973 return rv;
976 nsresult RasterImage::GetHotspotX(int32_t* aX) {
977 *aX = mHotspot.x;
978 return NS_OK;
981 nsresult RasterImage::GetHotspotY(int32_t* aY) {
982 *aY = mHotspot.y;
983 return NS_OK;
986 void RasterImage::Discard() {
987 MOZ_ASSERT(NS_IsMainThread());
988 MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
989 MOZ_ASSERT(!mAnimationState ||
990 StaticPrefs::image_mem_animated_discardable_AtStartup(),
991 "Asked to discard for animated image");
993 // Delete all the decoded frames.
994 SurfaceCache::RemoveImage(ImageKey(this));
996 if (mAnimationState) {
997 IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize());
999 auto dirtyRect = OrientedIntRect::FromUnknownRect(rect);
1000 NotifyProgress(NoProgress, dirtyRect);
1003 // Notify that we discarded.
1004 if (mProgressTracker) {
1005 mProgressTracker->OnDiscard();
1009 bool RasterImage::CanDiscard() {
1010 return LoadAllSourceData() &&
1011 // Can discard animated images if the pref is set
1012 (!mAnimationState ||
1013 StaticPrefs::image_mem_animated_discardable_AtStartup());
1016 NS_IMETHODIMP
1017 RasterImage::StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) {
1018 if (mError) {
1019 return NS_ERROR_FAILURE;
1022 if (!LoadHasSize()) {
1023 StoreWantFullDecode(true);
1024 return NS_OK;
1027 uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
1028 FLAG_HIGH_QUALITY_SCALING;
1029 return RequestDecodeForSize(mSize.ToUnknownSize(), flags, aWhichFrame);
1032 bool RasterImage::StartDecodingWithResult(uint32_t aFlags,
1033 uint32_t aWhichFrame) {
1034 if (mError) {
1035 return false;
1038 if (!LoadHasSize()) {
1039 StoreWantFullDecode(true);
1040 return false;
1043 uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
1044 FLAG_HIGH_QUALITY_SCALING;
1045 LookupResult result = RequestDecodeForSizeInternal(mSize, flags, aWhichFrame);
1046 DrawableSurface surface = std::move(result.Surface());
1047 return surface && surface->IsFinished();
1050 bool RasterImage::HasDecodedPixels() {
1051 LookupResult result = SurfaceCache::LookupBestMatch(
1052 ImageKey(this),
1053 RasterSurfaceKey(mSize.ToUnknownSize(), DefaultSurfaceFlags(),
1054 PlaybackType::eStatic),
1055 /* aMarkUsed = */ false);
1056 MatchType matchType = result.Type();
1057 if (matchType == MatchType::NOT_FOUND || matchType == MatchType::PENDING ||
1058 !bool(result.Surface())) {
1059 return false;
1062 return !result.Surface()->GetDecodedRect().IsEmpty();
1065 imgIContainer::DecodeResult RasterImage::RequestDecodeWithResult(
1066 uint32_t aFlags, uint32_t aWhichFrame) {
1067 MOZ_ASSERT(NS_IsMainThread());
1069 if (mError) {
1070 return imgIContainer::DECODE_REQUEST_FAILED;
1073 uint32_t flags = aFlags | FLAG_ASYNC_NOTIFY;
1074 LookupResult result = RequestDecodeForSizeInternal(mSize, flags, aWhichFrame);
1075 DrawableSurface surface = std::move(result.Surface());
1076 if (surface && surface->IsFinished()) {
1077 return imgIContainer::DECODE_SURFACE_AVAILABLE;
1079 if (result.GetFailedToRequestDecode()) {
1080 return imgIContainer::DECODE_REQUEST_FAILED;
1082 return imgIContainer::DECODE_REQUESTED;
1085 NS_IMETHODIMP
1086 RasterImage::RequestDecodeForSize(const IntSize& aSize, uint32_t aFlags,
1087 uint32_t aWhichFrame) {
1088 MOZ_ASSERT(NS_IsMainThread());
1090 if (mError) {
1091 return NS_ERROR_FAILURE;
1094 RequestDecodeForSizeInternal(OrientedIntSize::FromUnknownSize(aSize), aFlags,
1095 aWhichFrame);
1097 return NS_OK;
1100 LookupResult RasterImage::RequestDecodeForSizeInternal(
1101 const OrientedIntSize& aSize, uint32_t aFlags, uint32_t aWhichFrame) {
1102 MOZ_ASSERT(NS_IsMainThread());
1104 if (aWhichFrame > FRAME_MAX_VALUE) {
1105 return LookupResult(MatchType::NOT_FOUND);
1108 if (mError) {
1109 LookupResult result = LookupResult(MatchType::NOT_FOUND);
1110 result.SetFailedToRequestDecode();
1111 return result;
1114 if (!LoadHasSize()) {
1115 StoreWantFullDecode(true);
1116 return LookupResult(MatchType::NOT_FOUND);
1119 // Decide whether to sync decode images we can decode quickly. Here we are
1120 // explicitly trading off flashing for responsiveness in the case that we're
1121 // redecoding an image (see bug 845147).
1122 bool shouldSyncDecodeIfFast =
1123 !LoadHasBeenDecoded() && (aFlags & FLAG_SYNC_DECODE_IF_FAST);
1125 uint32_t flags =
1126 shouldSyncDecodeIfFast ? aFlags : aFlags & ~FLAG_SYNC_DECODE_IF_FAST;
1128 // Perform a frame lookup, which will implicitly start decoding if needed.
1129 return LookupFrame(aSize, flags, ToPlaybackType(aWhichFrame),
1130 /* aMarkUsed = */ false);
1133 static bool LaunchDecodingTask(IDecodingTask* aTask, RasterImage* aImage,
1134 uint32_t aFlags, bool aHaveSourceData) {
1135 if (aHaveSourceData) {
1136 nsCString uri(aImage->GetURIString());
1138 // If we have all the data, we can sync decode if requested.
1139 if (aFlags & imgIContainer::FLAG_SYNC_DECODE) {
1140 DecodePool::Singleton()->SyncRunIfPossible(aTask, uri);
1141 return true;
1144 if (aFlags & imgIContainer::FLAG_SYNC_DECODE_IF_FAST) {
1145 return DecodePool::Singleton()->SyncRunIfPreferred(aTask, uri);
1149 // Perform an async decode. We also take this path if we don't have all the
1150 // source data yet, since sync decoding is impossible in that situation.
1151 DecodePool::Singleton()->AsyncRun(aTask);
1152 return false;
1155 void RasterImage::Decode(const OrientedIntSize& aSize, uint32_t aFlags,
1156 PlaybackType aPlaybackType, bool& aOutRanSync,
1157 bool& aOutFailed) {
1158 MOZ_ASSERT(NS_IsMainThread());
1160 if (mError) {
1161 aOutFailed = true;
1162 return;
1165 // If we don't have a size yet, we can't do any other decoding.
1166 if (!LoadHasSize()) {
1167 StoreWantFullDecode(true);
1168 return;
1171 // We're about to decode again, which may mean that some of the previous sizes
1172 // we've decoded at aren't useful anymore. We can allow them to expire from
1173 // the cache by unlocking them here. When the decode finishes, it will send an
1174 // invalidation that will cause all instances of this image to redraw. If this
1175 // image is locked, any surfaces that are still useful will become locked
1176 // again when LookupFrame touches them, and the remainder will eventually
1177 // expire.
1178 SurfaceCache::UnlockEntries(ImageKey(this));
1180 // Determine which flags we need to decode this image with.
1181 DecoderFlags decoderFlags = mDefaultDecoderFlags;
1182 if (aFlags & FLAG_ASYNC_NOTIFY) {
1183 decoderFlags |= DecoderFlags::ASYNC_NOTIFY;
1185 if (LoadTransient()) {
1186 decoderFlags |= DecoderFlags::IMAGE_IS_TRANSIENT;
1188 if (LoadHasBeenDecoded()) {
1189 decoderFlags |= DecoderFlags::IS_REDECODE;
1191 if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
1192 // Used SurfaceCache::Lookup instead of SurfaceCache::LookupBestMatch. That
1193 // means the caller can handle a differently sized surface to be returned
1194 // at any point.
1195 decoderFlags |= DecoderFlags::CANNOT_SUBSTITUTE;
1198 SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
1199 if (IsOpaque()) {
1200 // If there's no transparency, it doesn't matter whether we premultiply
1201 // alpha or not.
1202 surfaceFlags &= ~SurfaceFlags::NO_PREMULTIPLY_ALPHA;
1205 // Create a decoder.
1206 RefPtr<IDecodingTask> task;
1207 nsresult rv;
1208 bool animated = mAnimationState && aPlaybackType == PlaybackType::eAnimated;
1209 if (animated) {
1210 size_t currentFrame = mAnimationState->GetCurrentAnimationFrameIndex();
1211 rv = DecoderFactory::CreateAnimationDecoder(
1212 mDecoderType, WrapNotNull(this), mSourceBuffer, mSize.ToUnknownSize(),
1213 decoderFlags, surfaceFlags, currentFrame, getter_AddRefs(task));
1214 } else {
1215 rv = DecoderFactory::CreateDecoder(mDecoderType, WrapNotNull(this),
1216 mSourceBuffer, mSize.ToUnknownSize(),
1217 aSize.ToUnknownSize(), decoderFlags,
1218 surfaceFlags, getter_AddRefs(task));
1221 if (rv == NS_ERROR_ALREADY_INITIALIZED) {
1222 // We raced with an already pending decoder, and it finished before we
1223 // managed to insert the new decoder. Pretend we did a sync call to make
1224 // the caller lookup in the surface cache again.
1225 MOZ_ASSERT(!task);
1226 aOutRanSync = true;
1227 return;
1230 if (animated) {
1231 // We pass false for aAllowInvalidation because we may be asked to use
1232 // async notifications. Any potential invalidation here will be sent when
1233 // RequestRefresh is called, or NotifyDecodeComplete.
1234 #ifdef DEBUG
1235 IntRect rect =
1236 #endif
1237 mAnimationState->UpdateState(this, mSize.ToUnknownSize(), false);
1238 MOZ_ASSERT(rect.IsEmpty());
1241 // Make sure DecoderFactory was able to create a decoder successfully.
1242 if (NS_FAILED(rv)) {
1243 MOZ_ASSERT(!task);
1244 aOutFailed = true;
1245 return;
1248 MOZ_ASSERT(task);
1249 mDecodeCount++;
1251 // We're ready to decode; start the decoder.
1252 aOutRanSync = LaunchDecodingTask(task, this, aFlags, LoadAllSourceData());
1255 NS_IMETHODIMP
1256 RasterImage::DecodeMetadata(uint32_t aFlags) {
1257 if (mError) {
1258 return NS_ERROR_FAILURE;
1261 MOZ_ASSERT(!LoadHasSize(), "Should not do unnecessary metadata decodes");
1263 // Create a decoder.
1264 RefPtr<IDecodingTask> task = DecoderFactory::CreateMetadataDecoder(
1265 mDecoderType, WrapNotNull(this), mDefaultDecoderFlags, mSourceBuffer);
1267 // Make sure DecoderFactory was able to create a decoder successfully.
1268 if (!task) {
1269 return NS_ERROR_FAILURE;
1272 // We're ready to decode; start the decoder.
1273 LaunchDecodingTask(task, this, aFlags, LoadAllSourceData());
1274 return NS_OK;
1277 void RasterImage::RecoverFromInvalidFrames(const OrientedIntSize& aSize,
1278 uint32_t aFlags) {
1279 if (!LoadHasSize()) {
1280 return;
1283 NS_WARNING("A RasterImage's frames became invalid. Attempting to recover...");
1285 // Discard all existing frames, since they're probably all now invalid.
1286 SurfaceCache::RemoveImage(ImageKey(this));
1288 // Relock the image if it's supposed to be locked.
1289 if (mLockCount > 0) {
1290 SurfaceCache::LockImage(ImageKey(this));
1293 bool unused1, unused2;
1295 // Animated images require some special handling, because we normally require
1296 // that they never be discarded.
1297 if (mAnimationState) {
1298 Decode(mSize, aFlags | FLAG_SYNC_DECODE, PlaybackType::eAnimated, unused1,
1299 unused2);
1300 ResetAnimation();
1301 return;
1304 // For non-animated images, it's fine to recover using an async decode.
1305 Decode(aSize, aFlags, PlaybackType::eStatic, unused1, unused2);
1308 bool RasterImage::CanDownscaleDuringDecode(const OrientedIntSize& aSize,
1309 uint32_t aFlags) {
1310 // Check basic requirements: downscale-during-decode is enabled, Skia is
1311 // available, this image isn't transient, we have all the source data and know
1312 // our size, and the flags allow us to do it.
1313 if (!LoadHasSize() || LoadTransient() ||
1314 !StaticPrefs::image_downscale_during_decode_enabled() ||
1315 !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
1316 return false;
1319 // We don't downscale animated images during decode.
1320 if (mAnimationState) {
1321 return false;
1324 // Never upscale.
1325 if (aSize.width >= mSize.width || aSize.height >= mSize.height) {
1326 return false;
1329 // Zero or negative width or height is unacceptable.
1330 if (aSize.width < 1 || aSize.height < 1) {
1331 return false;
1334 // There's no point in scaling if we can't store the result.
1335 if (!SurfaceCache::CanHold(aSize.ToUnknownSize())) {
1336 return false;
1339 return true;
1342 ImgDrawResult RasterImage::DrawInternal(DrawableSurface&& aSurface,
1343 gfxContext* aContext,
1344 const OrientedIntSize& aSize,
1345 const ImageRegion& aRegion,
1346 SamplingFilter aSamplingFilter,
1347 uint32_t aFlags, float aOpacity) {
1348 gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
1349 ImageRegion region(aRegion);
1350 bool frameIsFinished = aSurface->IsFinished();
1352 AutoProfilerImagePaintMarker PROFILER_RAII(this);
1353 #ifdef DEBUG
1354 NotifyDrawingObservers();
1355 #endif
1357 // By now we may have a frame with the requested size. If not, we need to
1358 // adjust the drawing parameters accordingly.
1359 IntSize finalSize = aSurface->GetSize();
1360 bool couldRedecodeForBetterFrame = false;
1361 if (finalSize != aSize.ToUnknownSize()) {
1362 gfx::MatrixScales scale(double(aSize.width) / finalSize.width,
1363 double(aSize.height) / finalSize.height);
1364 aContext->Multiply(gfx::Matrix::Scaling(scale));
1365 region.Scale(1.0 / scale.xScale, 1.0 / scale.yScale);
1367 couldRedecodeForBetterFrame = CanDownscaleDuringDecode(aSize, aFlags);
1370 if (!aSurface->Draw(aContext, region, aSamplingFilter, aFlags, aOpacity)) {
1371 RecoverFromInvalidFrames(aSize, aFlags);
1372 return ImgDrawResult::TEMPORARY_ERROR;
1374 if (!frameIsFinished) {
1375 return ImgDrawResult::INCOMPLETE;
1377 if (couldRedecodeForBetterFrame) {
1378 return ImgDrawResult::WRONG_SIZE;
1380 return ImgDrawResult::SUCCESS;
1383 //******************************************************************************
1384 NS_IMETHODIMP_(ImgDrawResult)
1385 RasterImage::Draw(gfxContext* aContext, const IntSize& aSize,
1386 const ImageRegion& aRegion, uint32_t aWhichFrame,
1387 SamplingFilter aSamplingFilter,
1388 const SVGImageContext& /*aSVGContext - ignored*/,
1389 uint32_t aFlags, float aOpacity) {
1390 if (aWhichFrame > FRAME_MAX_VALUE) {
1391 return ImgDrawResult::BAD_ARGS;
1394 if (mError) {
1395 return ImgDrawResult::BAD_IMAGE;
1398 // Illegal -- you can't draw with non-default decode flags.
1399 // (Disabling colorspace conversion might make sense to allow, but
1400 // we don't currently.)
1401 if (ToSurfaceFlags(aFlags) != DefaultSurfaceFlags()) {
1402 return ImgDrawResult::BAD_ARGS;
1405 if (!aContext) {
1406 return ImgDrawResult::BAD_ARGS;
1409 if (mAnimationConsumers == 0 && mAnimationState) {
1410 SendOnUnlockedDraw(aFlags);
1413 // If we're not using SamplingFilter::GOOD, we shouldn't high-quality scale or
1414 // downscale during decode.
1415 uint32_t flags = aSamplingFilter == SamplingFilter::GOOD
1416 ? aFlags
1417 : aFlags & ~FLAG_HIGH_QUALITY_SCALING;
1419 auto size = OrientedIntSize::FromUnknownSize(aSize);
1420 LookupResult result = LookupFrame(size, flags, ToPlaybackType(aWhichFrame),
1421 /* aMarkUsed = */ true);
1422 if (!result) {
1423 // Getting the frame (above) touches the image and kicks off decoding.
1424 if (mDrawStartTime.IsNull()) {
1425 mDrawStartTime = TimeStamp::Now();
1427 return ImgDrawResult::NOT_READY;
1430 bool shouldRecordTelemetry =
1431 !mDrawStartTime.IsNull() && result.Surface()->IsFinished();
1433 ImgDrawResult drawResult =
1434 DrawInternal(std::move(result.Surface()), aContext, size, aRegion,
1435 aSamplingFilter, flags, aOpacity);
1437 if (shouldRecordTelemetry) {
1438 TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
1439 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY,
1440 int32_t(drawLatency.ToMicroseconds()));
1441 mDrawStartTime = TimeStamp();
1444 return drawResult;
1447 //******************************************************************************
1449 NS_IMETHODIMP
1450 RasterImage::LockImage() {
1451 MOZ_ASSERT(NS_IsMainThread(),
1452 "Main thread to encourage serialization with UnlockImage");
1453 if (mError) {
1454 return NS_ERROR_FAILURE;
1457 // Increment the lock count
1458 mLockCount++;
1460 // Lock this image's surfaces in the SurfaceCache.
1461 if (mLockCount == 1) {
1462 SurfaceCache::LockImage(ImageKey(this));
1465 return NS_OK;
1468 //******************************************************************************
1470 NS_IMETHODIMP
1471 RasterImage::UnlockImage() {
1472 MOZ_ASSERT(NS_IsMainThread(),
1473 "Main thread to encourage serialization with LockImage");
1474 if (mError) {
1475 return NS_ERROR_FAILURE;
1478 // It's an error to call this function if the lock count is 0
1479 MOZ_ASSERT(mLockCount > 0, "Calling UnlockImage with mLockCount == 0!");
1480 if (mLockCount == 0) {
1481 return NS_ERROR_ABORT;
1484 // Decrement our lock count
1485 mLockCount--;
1487 // Unlock this image's surfaces in the SurfaceCache.
1488 if (mLockCount == 0) {
1489 SurfaceCache::UnlockImage(ImageKey(this));
1492 return NS_OK;
1495 //******************************************************************************
1497 NS_IMETHODIMP
1498 RasterImage::RequestDiscard() {
1499 if (LoadDiscardable() && // Enabled at creation time...
1500 mLockCount == 0 && // ...not temporarily disabled...
1501 CanDiscard()) {
1502 Discard();
1505 return NS_OK;
1508 // Idempotent error flagging routine. If a decoder is open, shuts it down.
1509 void RasterImage::DoError() {
1510 // If we've flagged an error before, we have nothing to do
1511 if (mError) {
1512 return;
1515 // We can't safely handle errors off-main-thread, so dispatch a worker to
1516 // do it.
1517 if (!NS_IsMainThread()) {
1518 HandleErrorWorker::DispatchIfNeeded(this);
1519 return;
1522 // Put the container in an error state.
1523 mError = true;
1525 // Stop animation and release our FrameAnimator.
1526 if (mAnimating) {
1527 StopAnimation();
1529 mAnimationState = Nothing();
1530 mFrameAnimator = nullptr;
1532 // Release all locks.
1533 mLockCount = 0;
1534 SurfaceCache::UnlockImage(ImageKey(this));
1536 // Release all frames from the surface cache.
1537 SurfaceCache::RemoveImage(ImageKey(this));
1539 // Invalidate to get rid of any partially-drawn image content.
1540 auto dirtyRect = OrientedIntRect({0, 0}, mSize);
1541 NotifyProgress(NoProgress, dirtyRect);
1543 MOZ_LOG(gImgLog, LogLevel::Error,
1544 ("RasterImage: [this=%p] Error detected for image\n", this));
1547 /* static */
1548 void RasterImage::HandleErrorWorker::DispatchIfNeeded(RasterImage* aImage) {
1549 RefPtr<HandleErrorWorker> worker = new HandleErrorWorker(aImage);
1550 NS_DispatchToMainThread(worker);
1553 RasterImage::HandleErrorWorker::HandleErrorWorker(RasterImage* aImage)
1554 : Runnable("image::RasterImage::HandleErrorWorker"), mImage(aImage) {
1555 MOZ_ASSERT(mImage, "Should have image");
1558 NS_IMETHODIMP
1559 RasterImage::HandleErrorWorker::Run() {
1560 mImage->DoError();
1562 return NS_OK;
1565 bool RasterImage::ShouldAnimate() {
1566 return ImageResource::ShouldAnimate() && mAnimationState &&
1567 mAnimationState->KnownFrameCount() >= 1 && !LoadAnimationFinished();
1570 #ifdef DEBUG
1571 NS_IMETHODIMP
1572 RasterImage::GetFramesNotified(uint32_t* aFramesNotified) {
1573 NS_ENSURE_ARG_POINTER(aFramesNotified);
1575 *aFramesNotified = mFramesNotified;
1577 return NS_OK;
1579 #endif
1581 void RasterImage::NotifyProgress(
1582 Progress aProgress,
1583 const OrientedIntRect& aInvalidRect /* = OrientedIntRect() */,
1584 const Maybe<uint32_t>& aFrameCount /* = Nothing() */,
1585 DecoderFlags aDecoderFlags /* = DefaultDecoderFlags() */,
1586 SurfaceFlags aSurfaceFlags /* = DefaultSurfaceFlags() */) {
1587 MOZ_ASSERT(NS_IsMainThread());
1589 // Ensure that we stay alive long enough to finish notifying.
1590 RefPtr<RasterImage> image = this;
1592 OrientedIntRect invalidRect = aInvalidRect;
1594 if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY)) {
1595 // We may have decoded new animation frames; update our animation state.
1596 MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError);
1597 if (mAnimationState && aFrameCount) {
1598 mAnimationState->UpdateKnownFrameCount(*aFrameCount);
1601 // If we should start animating right now, do so.
1602 if (mAnimationState && aFrameCount == Some(1u) && LoadPendingAnimation() &&
1603 ShouldAnimate()) {
1604 StartAnimation();
1607 if (mAnimationState) {
1608 IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize());
1610 invalidRect.UnionRect(invalidRect,
1611 OrientedIntRect::FromUnknownRect(rect));
1615 // Tell the observers what happened.
1616 image->mProgressTracker->SyncNotifyProgress(aProgress,
1617 invalidRect.ToUnknownRect());
1620 void RasterImage::NotifyDecodeComplete(
1621 const DecoderFinalStatus& aStatus, const ImageMetadata& aMetadata,
1622 const DecoderTelemetry& aTelemetry, Progress aProgress,
1623 const OrientedIntRect& aInvalidRect, const Maybe<uint32_t>& aFrameCount,
1624 DecoderFlags aDecoderFlags, SurfaceFlags aSurfaceFlags) {
1625 MOZ_ASSERT(NS_IsMainThread());
1627 // If the decoder detected an error, log it to the error console.
1628 if (aStatus.mShouldReportError) {
1629 ReportDecoderError();
1632 // Record all the metadata the decoder gathered about this image.
1633 bool metadataOK = SetMetadata(aMetadata, aStatus.mWasMetadataDecode);
1634 if (!metadataOK) {
1635 // This indicates a serious error that requires us to discard all existing
1636 // surfaces and redecode to recover. We'll drop the results from this
1637 // decoder on the floor, since they aren't valid.
1638 RecoverFromInvalidFrames(mSize, FromSurfaceFlags(aSurfaceFlags));
1639 return;
1642 MOZ_ASSERT(mError || LoadHasSize() || !aMetadata.HasSize(),
1643 "SetMetadata should've gotten a size");
1645 if (!aStatus.mWasMetadataDecode && aStatus.mFinished) {
1646 // Flag that we've been decoded before.
1647 StoreHasBeenDecoded(true);
1650 // Send out any final notifications.
1651 NotifyProgress(aProgress, aInvalidRect, aFrameCount, aDecoderFlags,
1652 aSurfaceFlags);
1654 if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY)) {
1655 // We may have decoded new animation frames; update our animation state.
1656 MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError);
1657 if (mAnimationState && aFrameCount) {
1658 mAnimationState->UpdateKnownFrameCount(*aFrameCount);
1661 // If we should start animating right now, do so.
1662 if (mAnimationState && aFrameCount == Some(1u) && LoadPendingAnimation() &&
1663 ShouldAnimate()) {
1664 StartAnimation();
1667 if (mAnimationState && LoadHasBeenDecoded()) {
1668 // We've finished a full decode of all animation frames and our
1669 // AnimationState has been notified about them all, so let it know not to
1670 // expect anymore.
1671 mAnimationState->NotifyDecodeComplete();
1673 IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize());
1675 if (!rect.IsEmpty()) {
1676 auto dirtyRect = OrientedIntRect::FromUnknownRect(rect);
1677 NotifyProgress(NoProgress, dirtyRect);
1682 // Do some telemetry if this isn't a metadata decode.
1683 if (!aStatus.mWasMetadataDecode) {
1684 if (aTelemetry.mChunkCount) {
1685 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS,
1686 aTelemetry.mChunkCount);
1689 if (aStatus.mFinished) {
1690 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
1691 int32_t(aTelemetry.mDecodeTime.ToMicroseconds()));
1693 if (aTelemetry.mSpeedHistogram && aTelemetry.mBytesDecoded) {
1694 Telemetry::Accumulate(*aTelemetry.mSpeedHistogram, aTelemetry.Speed());
1699 // Only act on errors if we have no usable frames from the decoder.
1700 if (aStatus.mHadError &&
1701 (!mAnimationState || mAnimationState->KnownFrameCount() == 0)) {
1702 DoError();
1703 } else if (aStatus.mWasMetadataDecode && !LoadHasSize()) {
1704 DoError();
1707 // XXX(aosmond): Can we get this far without mFinished == true?
1708 if (aStatus.mFinished && aStatus.mWasMetadataDecode) {
1709 // If we were waiting to fire the load event, go ahead and fire it now.
1710 if (mLoadProgress) {
1711 NotifyForLoadEvent(*mLoadProgress);
1712 mLoadProgress = Nothing();
1715 // If we were a metadata decode and a full decode was requested, do it.
1716 if (LoadWantFullDecode()) {
1717 StoreWantFullDecode(false);
1718 RequestDecodeForSizeInternal(mSize,
1719 DECODE_FLAGS_DEFAULT |
1720 FLAG_HIGH_QUALITY_SCALING |
1721 FLAG_AVOID_REDECODE_FOR_SIZE,
1722 FRAME_CURRENT);
1727 void RasterImage::ReportDecoderError() {
1728 nsCOMPtr<nsIConsoleService> consoleService =
1729 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1730 nsCOMPtr<nsIScriptError> errorObject =
1731 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
1733 if (consoleService && errorObject) {
1734 nsAutoString msg(u"Image corrupt or truncated."_ns);
1735 nsAutoString src;
1736 if (GetURI()) {
1737 nsAutoCString uri;
1738 if (!GetSpecTruncatedTo1k(uri)) {
1739 msg += u" URI in this note truncated due to length."_ns;
1741 CopyUTF8toUTF16(uri, src);
1743 if (NS_SUCCEEDED(errorObject->InitWithWindowID(msg, src, u""_ns, 0, 0,
1744 nsIScriptError::errorFlag,
1745 "Image", InnerWindowID()))) {
1746 consoleService->LogMessage(errorObject);
1751 already_AddRefed<imgIContainer> RasterImage::Unwrap() {
1752 nsCOMPtr<imgIContainer> self(this);
1753 return self.forget();
1756 void RasterImage::PropagateUseCounters(dom::Document*) {
1757 // No use counters.
1760 IntSize RasterImage::OptimalImageSizeForDest(const gfxSize& aDest,
1761 uint32_t aWhichFrame,
1762 SamplingFilter aSamplingFilter,
1763 uint32_t aFlags) {
1764 MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
1765 aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
1766 "Unexpected destination size");
1768 if (mSize.IsEmpty() || aDest.IsEmpty()) {
1769 return IntSize(0, 0);
1772 auto dest = OrientedIntSize::FromUnknownSize(
1773 IntSize::Ceil(aDest.width, aDest.height));
1775 if (aSamplingFilter == SamplingFilter::GOOD &&
1776 CanDownscaleDuringDecode(dest, aFlags)) {
1777 return dest.ToUnknownSize();
1780 // We can't scale to this size. Use our intrinsic size for now.
1781 return mSize.ToUnknownSize();
1784 } // namespace image
1785 } // namespace mozilla