Bug 1567650 [wpt PR 17950] - [ElementTiming] Replace responseEnd with loadTime, a...
[gecko.git] / image / RasterImage.cpp
blob125244f420ebb5ec62bc39cfcc3518cee57b12f0
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 "ImageLogging.h"
10 #include "RasterImage.h"
12 #include "gfxPlatform.h"
13 #include "nsComponentManagerUtils.h"
14 #include "nsError.h"
15 #include "DecodePool.h"
16 #include "Decoder.h"
17 #include "prenv.h"
18 #include "prsystem.h"
19 #include "IDecodingTask.h"
20 #include "ImageRegion.h"
21 #include "Layers.h"
22 #include "LookupResult.h"
23 #include "nsIConsoleService.h"
24 #include "nsIInputStream.h"
25 #include "nsIScriptError.h"
26 #include "nsISupportsPrimitives.h"
27 #include "nsMemory.h"
28 #include "nsPresContext.h"
29 #include "SourceBuffer.h"
30 #include "SurfaceCache.h"
31 #include "FrameAnimator.h"
33 #include "gfxContext.h"
35 #include "mozilla/gfx/2D.h"
36 #include "mozilla/DebugOnly.h"
37 #include "mozilla/Likely.h"
38 #include "mozilla/RefPtr.h"
39 #include "mozilla/Move.h"
40 #include "mozilla/MemoryReporting.h"
41 #include "mozilla/SizeOfState.h"
42 #include "mozilla/StaticPrefs_image.h"
43 #include <stdint.h>
44 #include "mozilla/Telemetry.h"
45 #include "mozilla/TimeStamp.h"
46 #include "mozilla/Tuple.h"
47 #include "mozilla/ClearOnShutdown.h"
48 #include "mozilla/gfx/Scale.h"
50 #include "GeckoProfiler.h"
51 #include "gfx2DGlue.h"
52 #include "nsProperties.h"
53 #include <algorithm>
55 namespace mozilla {
57 using namespace gfx;
58 using namespace layers;
60 namespace image {
62 using std::ceil;
63 using std::min;
65 #ifndef DEBUG
66 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties)
67 #else
68 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties, imgIContainerDebug)
69 #endif
71 //******************************************************************************
72 RasterImage::RasterImage(nsIURI* aURI /* = nullptr */)
73 : ImageResource(aURI), // invoke superclass's constructor
74 mSize(0, 0),
75 mLockCount(0),
76 mDecoderType(DecoderType::UNKNOWN),
77 mDecodeCount(0),
78 #ifdef DEBUG
79 mFramesNotified(0),
80 #endif
81 mSourceBuffer(MakeNotNull<SourceBuffer*>()),
82 mHasSize(false),
83 mTransient(false),
84 mSyncLoad(false),
85 mDiscardable(false),
86 mSomeSourceData(false),
87 mAllSourceData(false),
88 mHasBeenDecoded(false),
89 mPendingAnimation(false),
90 mAnimationFinished(false),
91 mWantFullDecode(false) {
94 //******************************************************************************
95 RasterImage::~RasterImage() {
96 // Make sure our SourceBuffer is marked as complete. This will ensure that any
97 // outstanding decoders terminate.
98 if (!mSourceBuffer->IsComplete()) {
99 mSourceBuffer->Complete(NS_ERROR_ABORT);
102 // Release all frames from the surface cache.
103 SurfaceCache::RemoveImage(ImageKey(this));
105 // Record Telemetry.
106 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_COUNT, mDecodeCount);
109 nsresult RasterImage::Init(const char* aMimeType, uint32_t aFlags) {
110 // We don't support re-initialization
111 if (mInitialized) {
112 return NS_ERROR_ILLEGAL_VALUE;
115 // Not sure an error can happen before init, but be safe
116 if (mError) {
117 return NS_ERROR_FAILURE;
120 // We want to avoid redecodes for transient images.
121 MOZ_ASSERT_IF(aFlags & INIT_FLAG_TRANSIENT,
122 !(aFlags & INIT_FLAG_DISCARDABLE));
124 // Store initialization data
125 mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
126 mWantFullDecode = !!(aFlags & INIT_FLAG_DECODE_IMMEDIATELY);
127 mTransient = !!(aFlags & INIT_FLAG_TRANSIENT);
128 mSyncLoad = !!(aFlags & INIT_FLAG_SYNC_LOAD);
130 // Use the MIME type to select a decoder type, and make sure there *is* a
131 // decoder for this MIME type.
132 NS_ENSURE_ARG_POINTER(aMimeType);
133 mDecoderType = DecoderFactory::GetDecoderType(aMimeType);
134 if (mDecoderType == DecoderType::UNKNOWN) {
135 return NS_ERROR_FAILURE;
138 // Lock this image's surfaces in the SurfaceCache if we're not discardable.
139 if (!mDiscardable) {
140 mLockCount++;
141 SurfaceCache::LockImage(ImageKey(this));
144 // Mark us as initialized
145 mInitialized = true;
147 return NS_OK;
150 //******************************************************************************
151 NS_IMETHODIMP_(void)
152 RasterImage::RequestRefresh(const TimeStamp& aTime) {
153 if (HadRecentRefresh(aTime)) {
154 return;
157 EvaluateAnimation();
159 if (!mAnimating) {
160 return;
163 RefreshResult res;
164 if (mAnimationState) {
165 MOZ_ASSERT(mFrameAnimator);
166 res = mFrameAnimator->RequestRefresh(*mAnimationState, aTime,
167 mAnimationFinished);
170 if (res.mFrameAdvanced) {
171 // Notify listeners that our frame has actually changed, but do this only
172 // once for all frames that we've now passed (if AdvanceFrame() was called
173 // more than once).
174 #ifdef DEBUG
175 mFramesNotified++;
176 #endif
178 NotifyProgress(NoProgress, res.mDirtyRect);
181 if (res.mAnimationFinished) {
182 mAnimationFinished = true;
183 EvaluateAnimation();
187 //******************************************************************************
188 NS_IMETHODIMP
189 RasterImage::GetWidth(int32_t* aWidth) {
190 NS_ENSURE_ARG_POINTER(aWidth);
192 if (mError) {
193 *aWidth = 0;
194 return NS_ERROR_FAILURE;
197 *aWidth = mSize.width;
198 return NS_OK;
201 //******************************************************************************
202 NS_IMETHODIMP
203 RasterImage::GetHeight(int32_t* aHeight) {
204 NS_ENSURE_ARG_POINTER(aHeight);
206 if (mError) {
207 *aHeight = 0;
208 return NS_ERROR_FAILURE;
211 *aHeight = mSize.height;
212 return NS_OK;
215 //******************************************************************************
216 nsresult RasterImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const {
217 if (mError) {
218 return NS_ERROR_FAILURE;
221 if (mNativeSizes.IsEmpty()) {
222 aNativeSizes.Clear();
223 aNativeSizes.AppendElement(mSize);
224 } else {
225 aNativeSizes = mNativeSizes;
228 return NS_OK;
231 //******************************************************************************
232 size_t RasterImage::GetNativeSizesLength() const {
233 if (mError || !mHasSize) {
234 return 0;
237 if (mNativeSizes.IsEmpty()) {
238 return 1;
241 return mNativeSizes.Length();
244 //******************************************************************************
245 NS_IMETHODIMP
246 RasterImage::GetIntrinsicSize(nsSize* aSize) {
247 if (mError) {
248 return NS_ERROR_FAILURE;
251 *aSize = nsSize(nsPresContext::CSSPixelsToAppUnits(mSize.width),
252 nsPresContext::CSSPixelsToAppUnits(mSize.height));
253 return NS_OK;
256 //******************************************************************************
257 Maybe<AspectRatio> RasterImage::GetIntrinsicRatio() {
258 if (mError) {
259 return Nothing();
262 return Some(AspectRatio::FromSize(mSize.width, mSize.height));
265 NS_IMETHODIMP_(Orientation)
266 RasterImage::GetOrientation() { return mOrientation; }
268 //******************************************************************************
269 NS_IMETHODIMP
270 RasterImage::GetType(uint16_t* aType) {
271 NS_ENSURE_ARG_POINTER(aType);
273 *aType = imgIContainer::TYPE_RASTER;
274 return NS_OK;
277 NS_IMETHODIMP
278 RasterImage::GetProducerId(uint32_t* aId) {
279 NS_ENSURE_ARG_POINTER(aId);
281 *aId = ImageResource::GetImageProducerId();
282 return NS_OK;
285 LookupResult RasterImage::LookupFrameInternal(const IntSize& aSize,
286 uint32_t aFlags,
287 PlaybackType aPlaybackType,
288 bool aMarkUsed) {
289 if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
290 MOZ_ASSERT(mFrameAnimator);
291 MOZ_ASSERT(ToSurfaceFlags(aFlags) == DefaultSurfaceFlags(),
292 "Can't composite frames with non-default surface flags");
293 return mFrameAnimator->GetCompositedFrame(*mAnimationState, aMarkUsed);
296 SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
298 // We don't want any substitution for sync decodes, and substitution would be
299 // illegal when high quality downscaling is disabled, so we use
300 // SurfaceCache::Lookup in this case.
301 if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
302 return SurfaceCache::Lookup(
303 ImageKey(this),
304 RasterSurfaceKey(aSize, surfaceFlags, PlaybackType::eStatic),
305 aMarkUsed);
308 // We'll return the best match we can find to the requested frame.
309 return SurfaceCache::LookupBestMatch(
310 ImageKey(this),
311 RasterSurfaceKey(aSize, surfaceFlags, PlaybackType::eStatic), aMarkUsed);
314 LookupResult RasterImage::LookupFrame(const IntSize& aSize, uint32_t aFlags,
315 PlaybackType aPlaybackType,
316 bool aMarkUsed) {
317 MOZ_ASSERT(NS_IsMainThread());
319 // If we're opaque, we don't need to care about premultiplied alpha, because
320 // that can only matter for frames with transparency.
321 if (IsOpaque()) {
322 aFlags &= ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
325 IntSize requestedSize =
326 CanDownscaleDuringDecode(aSize, aFlags) ? aSize : mSize;
327 if (requestedSize.IsEmpty()) {
328 // Can't decode to a surface of zero size.
329 return LookupResult(MatchType::NOT_FOUND);
332 LookupResult result =
333 LookupFrameInternal(requestedSize, aFlags, aPlaybackType, aMarkUsed);
335 if (!result && !mHasSize) {
336 // We can't request a decode without knowing our intrinsic size. Give up.
337 return LookupResult(MatchType::NOT_FOUND);
340 const bool syncDecode = aFlags & FLAG_SYNC_DECODE;
341 const bool avoidRedecode = aFlags & FLAG_AVOID_REDECODE_FOR_SIZE;
342 if (result.Type() == MatchType::NOT_FOUND ||
343 (result.Type() == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND &&
344 !avoidRedecode) ||
345 (syncDecode && !avoidRedecode && !result)) {
346 // We don't have a copy of this frame, and there's no decoder working on
347 // one. (Or we're sync decoding and the existing decoder hasn't even started
348 // yet.) Trigger decoding so it'll be available next time.
349 MOZ_ASSERT(aPlaybackType != PlaybackType::eAnimated ||
350 StaticPrefs::image_mem_animated_discardable_AtStartup() ||
351 !mAnimationState || mAnimationState->KnownFrameCount() < 1,
352 "Animated frames should be locked");
354 // The surface cache may suggest the preferred size we are supposed to
355 // decode at. This should only happen if we accept substitutions.
356 if (!result.SuggestedSize().IsEmpty()) {
357 MOZ_ASSERT(!syncDecode && (aFlags & FLAG_HIGH_QUALITY_SCALING));
358 requestedSize = result.SuggestedSize();
361 bool ranSync = Decode(requestedSize, aFlags, aPlaybackType);
363 // If we can or did sync decode, we should already have the frame.
364 if (ranSync || syncDecode) {
365 result =
366 LookupFrameInternal(requestedSize, aFlags, aPlaybackType, aMarkUsed);
370 if (!result) {
371 // We still weren't able to get a frame. Give up.
372 return result;
375 // Sync decoding guarantees that we got the frame, but if it's owned by an
376 // async decoder that's currently running, the contents of the frame may not
377 // be available yet. Make sure we get everything.
378 if (mAllSourceData && syncDecode) {
379 result.Surface()->WaitUntilFinished();
382 // If we could have done some decoding in this function we need to check if
383 // that decoding encountered an error and hence aborted the surface. We want
384 // to avoid calling IsAborted if we weren't passed any sync decode flag
385 // because IsAborted acquires the monitor for the imgFrame.
386 if (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST) &&
387 result.Surface()->IsAborted()) {
388 DrawableSurface tmp = std::move(result.Surface());
389 return result;
392 return result;
395 bool RasterImage::IsOpaque() {
396 if (mError) {
397 return false;
400 Progress progress = mProgressTracker->GetProgress();
402 // If we haven't yet finished decoding, the safe answer is "not opaque".
403 if (!(progress & FLAG_DECODE_COMPLETE)) {
404 return false;
407 // Other, we're opaque if FLAG_HAS_TRANSPARENCY is not set.
408 return !(progress & FLAG_HAS_TRANSPARENCY);
411 NS_IMETHODIMP_(bool)
412 RasterImage::WillDrawOpaqueNow() {
413 if (!IsOpaque()) {
414 return false;
417 if (mAnimationState) {
418 if (!StaticPrefs::image_mem_animated_discardable_AtStartup()) {
419 // We never discard frames of animated images.
420 return true;
421 } else {
422 if (mAnimationState->GetCompositedFrameInvalid()) {
423 // We're not going to draw anything at all.
424 return false;
429 // If we are not locked our decoded data could get discard at any time (ie
430 // between the call to this function and when we are asked to draw), so we
431 // have to return false if we are unlocked.
432 if (mLockCount == 0) {
433 return false;
436 LookupResult result = SurfaceCache::LookupBestMatch(
437 ImageKey(this),
438 RasterSurfaceKey(mSize, DefaultSurfaceFlags(), PlaybackType::eStatic),
439 /* aMarkUsed = */ false);
440 MatchType matchType = result.Type();
441 if (matchType == MatchType::NOT_FOUND || matchType == MatchType::PENDING ||
442 !result.Surface()->IsFinished()) {
443 return false;
446 return true;
449 void RasterImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) {
450 MOZ_ASSERT(mProgressTracker);
452 bool animatedFramesDiscarded =
453 mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated;
455 nsCOMPtr<nsIEventTarget> eventTarget;
456 if (mProgressTracker) {
457 eventTarget = mProgressTracker->GetEventTarget();
458 } else {
459 eventTarget = do_GetMainThread();
462 RefPtr<RasterImage> image = this;
463 nsCOMPtr<nsIRunnable> ev =
464 NS_NewRunnableFunction("RasterImage::OnSurfaceDiscarded", [=]() -> void {
465 image->OnSurfaceDiscardedInternal(animatedFramesDiscarded);
467 eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
470 void RasterImage::OnSurfaceDiscardedInternal(bool aAnimatedFramesDiscarded) {
471 MOZ_ASSERT(NS_IsMainThread());
473 if (aAnimatedFramesDiscarded && mAnimationState) {
474 MOZ_ASSERT(StaticPrefs::image_mem_animated_discardable_AtStartup());
475 ReleaseImageContainer();
476 gfx::IntRect rect =
477 mAnimationState->UpdateState(mAnimationFinished, this, mSize);
478 NotifyProgress(NoProgress, rect);
481 if (mProgressTracker) {
482 mProgressTracker->OnDiscard();
486 //******************************************************************************
487 NS_IMETHODIMP
488 RasterImage::GetAnimated(bool* aAnimated) {
489 if (mError) {
490 return NS_ERROR_FAILURE;
493 NS_ENSURE_ARG_POINTER(aAnimated);
495 // If we have an AnimationState, we can know for sure.
496 if (mAnimationState) {
497 *aAnimated = true;
498 return NS_OK;
501 // Otherwise, we need to have been decoded to know for sure, since if we were
502 // decoded at least once mAnimationState would have been created for animated
503 // images. This is true even though we check for animation during the
504 // metadata decode, because we may still discover animation only during the
505 // full decode for corrupt images.
506 if (!mHasBeenDecoded) {
507 return NS_ERROR_NOT_AVAILABLE;
510 // We know for sure
511 *aAnimated = false;
513 return NS_OK;
516 //******************************************************************************
517 NS_IMETHODIMP_(int32_t)
518 RasterImage::GetFirstFrameDelay() {
519 if (mError) {
520 return -1;
523 bool animated = false;
524 if (NS_FAILED(GetAnimated(&animated)) || !animated) {
525 return -1;
528 MOZ_ASSERT(mAnimationState, "Animated images should have an AnimationState");
529 return mAnimationState->FirstFrameTimeout().AsEncodedValueDeprecated();
532 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
533 RasterImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) {
534 return GetFrameAtSize(mSize, aWhichFrame, aFlags);
537 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
538 RasterImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame,
539 uint32_t aFlags) {
540 #ifdef DEBUG
541 NotifyDrawingObservers();
542 #endif
544 auto result = GetFrameInternal(aSize, Nothing(), aWhichFrame, aFlags);
545 return mozilla::Get<2>(result).forget();
548 Tuple<ImgDrawResult, IntSize, RefPtr<SourceSurface>>
549 RasterImage::GetFrameInternal(const IntSize& aSize,
550 const Maybe<SVGImageContext>& aSVGContext,
551 uint32_t aWhichFrame, uint32_t aFlags) {
552 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
554 if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE) {
555 return MakeTuple(ImgDrawResult::BAD_ARGS, aSize, RefPtr<SourceSurface>());
558 if (mError) {
559 return MakeTuple(ImgDrawResult::BAD_IMAGE, aSize, RefPtr<SourceSurface>());
562 // Get the frame. If it's not there, it's probably the caller's fault for
563 // not waiting for the data to be loaded from the network or not passing
564 // FLAG_SYNC_DECODE.
565 LookupResult result = LookupFrame(aSize, aFlags, ToPlaybackType(aWhichFrame),
566 /* aMarkUsed = */ true);
568 // The surface cache may have suggested we use a different size than the
569 // given size in the future. This may or may not be accompanied by an
570 // actual surface, depending on what it has in its cache.
571 IntSize suggestedSize =
572 result.SuggestedSize().IsEmpty() ? aSize : result.SuggestedSize();
573 MOZ_ASSERT_IF(result.Type() == MatchType::SUBSTITUTE_BECAUSE_BEST,
574 suggestedSize != aSize);
576 if (!result) {
577 // The OS threw this frame away and we couldn't redecode it.
578 return MakeTuple(ImgDrawResult::TEMPORARY_ERROR, suggestedSize,
579 RefPtr<SourceSurface>());
582 RefPtr<SourceSurface> surface = result.Surface()->GetSourceSurface();
583 if (!result.Surface()->IsFinished()) {
584 return MakeTuple(ImgDrawResult::INCOMPLETE, suggestedSize,
585 std::move(surface));
588 return MakeTuple(ImgDrawResult::SUCCESS, suggestedSize, std::move(surface));
591 Tuple<ImgDrawResult, IntSize> RasterImage::GetImageContainerSize(
592 LayerManager* aManager, const IntSize& aSize, uint32_t aFlags) {
593 if (!mHasSize) {
594 return MakeTuple(ImgDrawResult::NOT_READY, IntSize(0, 0));
597 if (aSize.IsEmpty()) {
598 return MakeTuple(ImgDrawResult::BAD_ARGS, IntSize(0, 0));
601 // We check the minimum size because while we support downscaling, we do not
602 // support upscaling. If aSize > mSize, we will never give a larger surface
603 // than mSize. If mSize > aSize, and mSize > maxTextureSize, we still want to
604 // use image containers if aSize <= maxTextureSize.
605 int32_t maxTextureSize = aManager->GetMaxTextureSize();
606 if (min(mSize.width, aSize.width) > maxTextureSize ||
607 min(mSize.height, aSize.height) > maxTextureSize) {
608 return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0));
611 if (!CanDownscaleDuringDecode(aSize, aFlags)) {
612 return MakeTuple(ImgDrawResult::SUCCESS, mSize);
615 return MakeTuple(ImgDrawResult::SUCCESS, aSize);
618 NS_IMETHODIMP_(bool)
619 RasterImage::IsImageContainerAvailable(LayerManager* aManager,
620 uint32_t aFlags) {
621 return IsImageContainerAvailableAtSize(aManager, mSize, aFlags);
624 NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
625 RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags) {
626 RefPtr<ImageContainer> container;
627 ImgDrawResult drawResult = GetImageContainerImpl(
628 aManager, mSize, Nothing(), aFlags, getter_AddRefs(container));
630 // We silence the unused warning here because anything that needs the draw
631 // result should be using GetImageContainerAtSize, not GetImageContainer.
632 (void)drawResult;
633 return container.forget();
636 NS_IMETHODIMP_(bool)
637 RasterImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
638 const IntSize& aSize,
639 uint32_t aFlags) {
640 // We check the minimum size because while we support downscaling, we do not
641 // support upscaling. If aSize > mSize, we will never give a larger surface
642 // than mSize. If mSize > aSize, and mSize > maxTextureSize, we still want to
643 // use image containers if aSize <= maxTextureSize.
644 int32_t maxTextureSize = aManager->GetMaxTextureSize();
645 if (!mHasSize || aSize.IsEmpty() ||
646 min(mSize.width, aSize.width) > maxTextureSize ||
647 min(mSize.height, aSize.height) > maxTextureSize) {
648 return false;
651 return true;
654 NS_IMETHODIMP_(ImgDrawResult)
655 RasterImage::GetImageContainerAtSize(layers::LayerManager* aManager,
656 const gfx::IntSize& aSize,
657 const Maybe<SVGImageContext>& aSVGContext,
658 uint32_t aFlags,
659 layers::ImageContainer** aOutContainer) {
660 // We do not pass in the given SVG context because in theory it could differ
661 // between calls, but actually have no impact on the actual contents of the
662 // image container.
663 return GetImageContainerImpl(aManager, aSize, Nothing(), aFlags,
664 aOutContainer);
667 size_t RasterImage::SizeOfSourceWithComputedFallback(
668 SizeOfState& aState) const {
669 return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(
670 aState.mMallocSizeOf);
673 void RasterImage::CollectSizeOfSurfaces(
674 nsTArray<SurfaceMemoryCounter>& aCounters,
675 MallocSizeOf aMallocSizeOf) const {
676 SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
679 bool RasterImage::SetMetadata(const ImageMetadata& aMetadata,
680 bool aFromMetadataDecode) {
681 MOZ_ASSERT(NS_IsMainThread());
683 if (mError) {
684 return true;
687 if (aMetadata.HasSize()) {
688 IntSize size = aMetadata.GetSize();
689 if (size.width < 0 || size.height < 0) {
690 NS_WARNING("Image has negative intrinsic size");
691 DoError();
692 return true;
695 MOZ_ASSERT(aMetadata.HasOrientation());
696 Orientation orientation = aMetadata.GetOrientation();
698 // If we already have a size, check the new size against the old one.
699 if (mHasSize && (size != mSize || orientation != mOrientation)) {
700 NS_WARNING(
701 "Image changed size or orientation on redecode! "
702 "This should not happen!");
703 DoError();
704 return true;
707 // Set the size and flag that we have it.
708 mSize = size;
709 mOrientation = orientation;
710 mNativeSizes = aMetadata.GetNativeSizes();
711 mHasSize = true;
714 if (mHasSize && aMetadata.HasAnimation() && !mAnimationState) {
715 // We're becoming animated, so initialize animation stuff.
716 mAnimationState.emplace(mAnimationMode);
717 mFrameAnimator = MakeUnique<FrameAnimator>(this, mSize);
719 if (!StaticPrefs::image_mem_animated_discardable_AtStartup()) {
720 // We don't support discarding animated images (See bug 414259).
721 // Lock the image and throw away the key.
722 LockImage();
725 if (!aFromMetadataDecode) {
726 // The metadata decode reported that this image isn't animated, but we
727 // discovered that it actually was during the full decode. This is a
728 // rare failure that only occurs for corrupt images. To recover, we need
729 // to discard all existing surfaces and redecode.
730 return false;
734 if (mAnimationState) {
735 mAnimationState->SetLoopCount(aMetadata.GetLoopCount());
736 mAnimationState->SetFirstFrameTimeout(aMetadata.GetFirstFrameTimeout());
738 if (aMetadata.HasLoopLength()) {
739 mAnimationState->SetLoopLength(aMetadata.GetLoopLength());
741 if (aMetadata.HasFirstFrameRefreshArea()) {
742 mAnimationState->SetFirstFrameRefreshArea(
743 aMetadata.GetFirstFrameRefreshArea());
747 if (aMetadata.HasHotspot()) {
748 IntPoint hotspot = aMetadata.GetHotspot();
750 nsCOMPtr<nsISupportsPRUint32> intwrapx =
751 do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
752 nsCOMPtr<nsISupportsPRUint32> intwrapy =
753 do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
754 intwrapx->SetData(hotspot.x);
755 intwrapy->SetData(hotspot.y);
757 Set("hotspotX", intwrapx);
758 Set("hotspotY", intwrapy);
761 return true;
764 NS_IMETHODIMP
765 RasterImage::SetAnimationMode(uint16_t aAnimationMode) {
766 if (mAnimationState) {
767 mAnimationState->SetAnimationMode(aAnimationMode);
769 return SetAnimationModeInternal(aAnimationMode);
772 //******************************************************************************
774 nsresult RasterImage::StartAnimation() {
775 if (mError) {
776 return NS_ERROR_FAILURE;
779 MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
781 // If we're not ready to animate, then set mPendingAnimation, which will cause
782 // us to start animating if and when we do become ready.
783 mPendingAnimation =
784 !mAnimationState || mAnimationState->KnownFrameCount() < 1;
785 if (mPendingAnimation) {
786 return NS_OK;
789 // Don't bother to animate if we're displaying the first frame forever.
790 if (mAnimationState->GetCurrentAnimationFrameIndex() == 0 &&
791 mAnimationState->FirstFrameTimeout() == FrameTimeout::Forever()) {
792 mAnimationFinished = true;
793 return NS_ERROR_ABORT;
796 // We need to set the time that this initial frame was first displayed, as
797 // this is used in AdvanceFrame().
798 mAnimationState->InitAnimationFrameTimeIfNecessary();
800 return NS_OK;
803 //******************************************************************************
804 nsresult RasterImage::StopAnimation() {
805 MOZ_ASSERT(mAnimating, "Should be animating!");
807 nsresult rv = NS_OK;
808 if (mError) {
809 rv = NS_ERROR_FAILURE;
810 } else {
811 mAnimationState->SetAnimationFrameTime(TimeStamp());
814 mAnimating = false;
815 return rv;
818 //******************************************************************************
819 NS_IMETHODIMP
820 RasterImage::ResetAnimation() {
821 if (mError) {
822 return NS_ERROR_FAILURE;
825 mPendingAnimation = false;
827 if (mAnimationMode == kDontAnimMode || !mAnimationState ||
828 mAnimationState->GetCurrentAnimationFrameIndex() == 0) {
829 return NS_OK;
832 mAnimationFinished = false;
834 if (mAnimating) {
835 StopAnimation();
838 MOZ_ASSERT(mAnimationState, "Should have AnimationState");
839 MOZ_ASSERT(mFrameAnimator, "Should have FrameAnimator");
840 mFrameAnimator->ResetAnimation(*mAnimationState);
842 NotifyProgress(NoProgress, mAnimationState->FirstFrameRefreshArea());
844 // Start the animation again. It may not have been running before, if
845 // mAnimationFinished was true before entering this function.
846 EvaluateAnimation();
848 return NS_OK;
851 //******************************************************************************
852 NS_IMETHODIMP_(void)
853 RasterImage::SetAnimationStartTime(const TimeStamp& aTime) {
854 if (mError || mAnimationMode == kDontAnimMode || mAnimating ||
855 !mAnimationState) {
856 return;
859 mAnimationState->SetAnimationFrameTime(aTime);
862 NS_IMETHODIMP_(float)
863 RasterImage::GetFrameIndex(uint32_t aWhichFrame) {
864 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
865 return (aWhichFrame == FRAME_FIRST || !mAnimationState)
866 ? 0.0f
867 : mAnimationState->GetCurrentAnimationFrameIndex();
870 NS_IMETHODIMP_(IntRect)
871 RasterImage::GetImageSpaceInvalidationRect(const IntRect& aRect) {
872 return aRect;
875 nsresult RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*,
876 nsresult aStatus, bool aLastPart) {
877 MOZ_ASSERT(NS_IsMainThread());
879 // Record that we have all the data we're going to get now.
880 mAllSourceData = true;
882 // Let decoders know that there won't be any more data coming.
883 mSourceBuffer->Complete(aStatus);
885 // Allow a synchronous metadata decode if mSyncLoad was set, or if we're
886 // running on a single thread (in which case waiting for the async metadata
887 // decoder could delay this image's load event quite a bit), or if this image
888 // is transient.
889 bool canSyncDecodeMetadata =
890 mSyncLoad || mTransient || DecodePool::NumberOfCores() < 2;
892 if (canSyncDecodeMetadata && !mHasSize) {
893 // We're loading this image synchronously, so it needs to be usable after
894 // this call returns. Since we haven't gotten our size yet, we need to do a
895 // synchronous metadata decode here.
896 DecodeMetadata(FLAG_SYNC_DECODE);
899 // Determine our final status, giving precedence to Necko failure codes. We
900 // check after running the metadata decode in case it triggered an error.
901 nsresult finalStatus = mError ? NS_ERROR_FAILURE : NS_OK;
902 if (NS_FAILED(aStatus)) {
903 finalStatus = aStatus;
906 // If loading failed, report an error.
907 if (NS_FAILED(finalStatus)) {
908 DoError();
911 Progress loadProgress = LoadCompleteProgress(aLastPart, mError, finalStatus);
913 if (!mHasSize && !mError) {
914 // We don't have our size yet, so we'll fire the load event in SetSize().
915 MOZ_ASSERT(!canSyncDecodeMetadata,
916 "Firing load async after metadata sync decode?");
917 mLoadProgress = Some(loadProgress);
918 return finalStatus;
921 NotifyForLoadEvent(loadProgress);
923 return finalStatus;
926 void RasterImage::NotifyForLoadEvent(Progress aProgress) {
927 MOZ_ASSERT(mHasSize || mError, "Need to know size before firing load event");
928 MOZ_ASSERT(
929 !mHasSize || (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*, nsISupports*,
942 nsIInputStream* aInputStream,
943 uint64_t, uint32_t aCount) {
944 nsresult rv = mSourceBuffer->AppendFromInputStream(aInputStream, aCount);
945 if (NS_SUCCEEDED(rv) && !mSomeSourceData) {
946 mSomeSourceData = true;
947 if (!mSyncLoad) {
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 /********* Methods to implement lazy allocation of nsIProperties object *******/
977 NS_IMETHODIMP
978 RasterImage::Get(const char* prop, const nsIID& iid, void** result) {
979 if (!mProperties) {
980 return NS_ERROR_FAILURE;
982 return mProperties->Get(prop, iid, result);
985 NS_IMETHODIMP
986 RasterImage::Set(const char* prop, nsISupports* value) {
987 if (!mProperties) {
988 mProperties = new nsProperties();
990 return mProperties->Set(prop, value);
993 NS_IMETHODIMP
994 RasterImage::Has(const char* prop, bool* _retval) {
995 NS_ENSURE_ARG_POINTER(_retval);
996 if (!mProperties) {
997 *_retval = false;
998 return NS_OK;
1000 return mProperties->Has(prop, _retval);
1003 NS_IMETHODIMP
1004 RasterImage::Undefine(const char* prop) {
1005 if (!mProperties) {
1006 return NS_ERROR_FAILURE;
1008 return mProperties->Undefine(prop);
1011 NS_IMETHODIMP
1012 RasterImage::GetKeys(nsTArray<nsCString>& keys) {
1013 if (!mProperties) {
1014 keys.Clear();
1015 return NS_OK;
1017 return mProperties->GetKeys(keys);
1020 void RasterImage::Discard() {
1021 MOZ_ASSERT(NS_IsMainThread());
1022 MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
1023 MOZ_ASSERT(!mAnimationState ||
1024 StaticPrefs::image_mem_animated_discardable_AtStartup(),
1025 "Asked to discard for animated image");
1027 // Delete all the decoded frames.
1028 SurfaceCache::RemoveImage(ImageKey(this));
1030 if (mAnimationState) {
1031 ReleaseImageContainer();
1032 gfx::IntRect rect =
1033 mAnimationState->UpdateState(mAnimationFinished, this, mSize);
1034 NotifyProgress(NoProgress, rect);
1037 // Notify that we discarded.
1038 if (mProgressTracker) {
1039 mProgressTracker->OnDiscard();
1043 bool RasterImage::CanDiscard() {
1044 return mAllSourceData &&
1045 // Can discard animated images if the pref is set
1046 (!mAnimationState ||
1047 StaticPrefs::image_mem_animated_discardable_AtStartup());
1050 NS_IMETHODIMP
1051 RasterImage::StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) {
1052 if (mError) {
1053 return NS_ERROR_FAILURE;
1056 if (!mHasSize) {
1057 mWantFullDecode = true;
1058 return NS_OK;
1061 uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
1062 FLAG_HIGH_QUALITY_SCALING;
1063 return RequestDecodeForSize(mSize, flags, aWhichFrame);
1066 bool RasterImage::StartDecodingWithResult(uint32_t aFlags,
1067 uint32_t aWhichFrame) {
1068 if (mError) {
1069 return false;
1072 if (!mHasSize) {
1073 mWantFullDecode = true;
1074 return false;
1077 uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
1078 FLAG_HIGH_QUALITY_SCALING;
1079 DrawableSurface surface =
1080 RequestDecodeForSizeInternal(mSize, flags, aWhichFrame);
1081 return surface && surface->IsFinished();
1084 bool RasterImage::RequestDecodeWithResult(uint32_t aFlags,
1085 uint32_t aWhichFrame) {
1086 MOZ_ASSERT(NS_IsMainThread());
1088 if (mError) {
1089 return false;
1092 uint32_t flags = aFlags | FLAG_ASYNC_NOTIFY;
1093 DrawableSurface surface =
1094 RequestDecodeForSizeInternal(mSize, flags, aWhichFrame);
1095 return surface && surface->IsFinished();
1098 NS_IMETHODIMP
1099 RasterImage::RequestDecodeForSize(const IntSize& aSize, uint32_t aFlags,
1100 uint32_t aWhichFrame) {
1101 MOZ_ASSERT(NS_IsMainThread());
1103 if (mError) {
1104 return NS_ERROR_FAILURE;
1107 RequestDecodeForSizeInternal(aSize, aFlags, aWhichFrame);
1109 return NS_OK;
1112 DrawableSurface RasterImage::RequestDecodeForSizeInternal(
1113 const IntSize& aSize, uint32_t aFlags, uint32_t aWhichFrame) {
1114 MOZ_ASSERT(NS_IsMainThread());
1116 if (aWhichFrame > FRAME_MAX_VALUE) {
1117 return DrawableSurface();
1120 if (mError) {
1121 return DrawableSurface();
1124 if (!mHasSize) {
1125 mWantFullDecode = true;
1126 return DrawableSurface();
1129 // Decide whether to sync decode images we can decode quickly. Here we are
1130 // explicitly trading off flashing for responsiveness in the case that we're
1131 // redecoding an image (see bug 845147).
1132 bool shouldSyncDecodeIfFast =
1133 !mHasBeenDecoded && (aFlags & FLAG_SYNC_DECODE_IF_FAST);
1135 uint32_t flags =
1136 shouldSyncDecodeIfFast ? aFlags : aFlags & ~FLAG_SYNC_DECODE_IF_FAST;
1138 // Perform a frame lookup, which will implicitly start decoding if needed.
1139 LookupResult result = LookupFrame(aSize, flags, ToPlaybackType(aWhichFrame),
1140 /* aMarkUsed = */ false);
1141 return std::move(result.Surface());
1144 static bool LaunchDecodingTask(IDecodingTask* aTask, RasterImage* aImage,
1145 uint32_t aFlags, bool aHaveSourceData) {
1146 if (aHaveSourceData) {
1147 nsCString uri(aImage->GetURIString());
1149 // If we have all the data, we can sync decode if requested.
1150 if (aFlags & imgIContainer::FLAG_SYNC_DECODE) {
1151 DecodePool::Singleton()->SyncRunIfPossible(aTask, uri);
1152 return true;
1155 if (aFlags & imgIContainer::FLAG_SYNC_DECODE_IF_FAST) {
1156 return DecodePool::Singleton()->SyncRunIfPreferred(aTask, uri);
1160 // Perform an async decode. We also take this path if we don't have all the
1161 // source data yet, since sync decoding is impossible in that situation.
1162 DecodePool::Singleton()->AsyncRun(aTask);
1163 return false;
1166 bool RasterImage::Decode(const IntSize& aSize, uint32_t aFlags,
1167 PlaybackType aPlaybackType) {
1168 MOZ_ASSERT(NS_IsMainThread());
1170 if (mError) {
1171 return false;
1174 // If we don't have a size yet, we can't do any other decoding.
1175 if (!mHasSize) {
1176 mWantFullDecode = true;
1177 return false;
1180 // We're about to decode again, which may mean that some of the previous sizes
1181 // we've decoded at aren't useful anymore. We can allow them to expire from
1182 // the cache by unlocking them here. When the decode finishes, it will send an
1183 // invalidation that will cause all instances of this image to redraw. If this
1184 // image is locked, any surfaces that are still useful will become locked
1185 // again when LookupFrame touches them, and the remainder will eventually
1186 // expire.
1187 SurfaceCache::UnlockEntries(ImageKey(this));
1189 // Determine which flags we need to decode this image with.
1190 DecoderFlags decoderFlags = DefaultDecoderFlags();
1191 if (aFlags & FLAG_ASYNC_NOTIFY) {
1192 decoderFlags |= DecoderFlags::ASYNC_NOTIFY;
1194 if (mTransient) {
1195 decoderFlags |= DecoderFlags::IMAGE_IS_TRANSIENT;
1197 if (mHasBeenDecoded) {
1198 decoderFlags |= DecoderFlags::IS_REDECODE;
1200 if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
1201 // Used SurfaceCache::Lookup instead of SurfaceCache::LookupBestMatch. That
1202 // means the caller can handle a differently sized surface to be returned
1203 // at any point.
1204 decoderFlags |= DecoderFlags::CANNOT_SUBSTITUTE;
1207 SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
1208 if (IsOpaque()) {
1209 // If there's no transparency, it doesn't matter whether we premultiply
1210 // alpha or not.
1211 surfaceFlags &= ~SurfaceFlags::NO_PREMULTIPLY_ALPHA;
1214 // Create a decoder.
1215 RefPtr<IDecodingTask> task;
1216 nsresult rv;
1217 bool animated = mAnimationState && aPlaybackType == PlaybackType::eAnimated;
1218 if (animated) {
1219 size_t currentFrame = mAnimationState->GetCurrentAnimationFrameIndex();
1220 rv = DecoderFactory::CreateAnimationDecoder(
1221 mDecoderType, WrapNotNull(this), mSourceBuffer, mSize, decoderFlags,
1222 surfaceFlags, currentFrame, getter_AddRefs(task));
1223 } else {
1224 rv = DecoderFactory::CreateDecoder(
1225 mDecoderType, WrapNotNull(this), mSourceBuffer, mSize, aSize,
1226 decoderFlags, surfaceFlags, getter_AddRefs(task));
1229 if (rv == NS_ERROR_ALREADY_INITIALIZED) {
1230 // We raced with an already pending decoder, and it finished before we
1231 // managed to insert the new decoder. Pretend we did a sync call to make
1232 // the caller lookup in the surface cache again.
1233 MOZ_ASSERT(!task);
1234 return true;
1237 if (animated) {
1238 // We pass false for aAllowInvalidation because we may be asked to use
1239 // async notifications. Any potential invalidation here will be sent when
1240 // RequestRefresh is called, or NotifyDecodeComplete.
1241 #ifdef DEBUG
1242 gfx::IntRect rect =
1243 #endif
1244 mAnimationState->UpdateState(mAnimationFinished, this, mSize, false);
1245 MOZ_ASSERT(rect.IsEmpty());
1248 // Make sure DecoderFactory was able to create a decoder successfully.
1249 if (NS_FAILED(rv)) {
1250 MOZ_ASSERT(!task);
1251 return false;
1254 MOZ_ASSERT(task);
1255 mDecodeCount++;
1257 // We're ready to decode; start the decoder.
1258 return LaunchDecodingTask(task, this, aFlags, mAllSourceData);
1261 NS_IMETHODIMP
1262 RasterImage::DecodeMetadata(uint32_t aFlags) {
1263 if (mError) {
1264 return NS_ERROR_FAILURE;
1267 MOZ_ASSERT(!mHasSize, "Should not do unnecessary metadata decodes");
1269 // Create a decoder.
1270 RefPtr<IDecodingTask> task = DecoderFactory::CreateMetadataDecoder(
1271 mDecoderType, WrapNotNull(this), mSourceBuffer);
1273 // Make sure DecoderFactory was able to create a decoder successfully.
1274 if (!task) {
1275 return NS_ERROR_FAILURE;
1278 // We're ready to decode; start the decoder.
1279 LaunchDecodingTask(task, this, aFlags, mAllSourceData);
1280 return NS_OK;
1283 void RasterImage::RecoverFromInvalidFrames(const IntSize& aSize,
1284 uint32_t aFlags) {
1285 if (!mHasSize) {
1286 return;
1289 NS_WARNING("A RasterImage's frames became invalid. Attempting to recover...");
1291 // Discard all existing frames, since they're probably all now invalid.
1292 SurfaceCache::RemoveImage(ImageKey(this));
1294 // Relock the image if it's supposed to be locked.
1295 if (mLockCount > 0) {
1296 SurfaceCache::LockImage(ImageKey(this));
1299 // Animated images require some special handling, because we normally require
1300 // that they never be discarded.
1301 if (mAnimationState) {
1302 Decode(mSize, aFlags | FLAG_SYNC_DECODE, PlaybackType::eAnimated);
1303 ResetAnimation();
1304 return;
1307 // For non-animated images, it's fine to recover using an async decode.
1308 Decode(aSize, aFlags, PlaybackType::eStatic);
1311 static bool HaveSkia() {
1312 #ifdef MOZ_ENABLE_SKIA
1313 return true;
1314 #else
1315 return false;
1316 #endif
1319 bool RasterImage::CanDownscaleDuringDecode(const IntSize& aSize,
1320 uint32_t aFlags) {
1321 // Check basic requirements: downscale-during-decode is enabled, Skia is
1322 // available, this image isn't transient, we have all the source data and know
1323 // our size, and the flags allow us to do it.
1324 if (!mHasSize || mTransient || !HaveSkia() ||
1325 !StaticPrefs::image_downscale_during_decode_enabled() ||
1326 !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
1327 return false;
1330 // We don't downscale animated images during decode.
1331 if (mAnimationState) {
1332 return false;
1335 // Never upscale.
1336 if (aSize.width >= mSize.width || aSize.height >= mSize.height) {
1337 return false;
1340 // Zero or negative width or height is unacceptable.
1341 if (aSize.width < 1 || aSize.height < 1) {
1342 return false;
1345 // There's no point in scaling if we can't store the result.
1346 if (!SurfaceCache::CanHold(aSize)) {
1347 return false;
1350 return true;
1353 ImgDrawResult RasterImage::DrawInternal(DrawableSurface&& aSurface,
1354 gfxContext* aContext,
1355 const IntSize& aSize,
1356 const ImageRegion& aRegion,
1357 SamplingFilter aSamplingFilter,
1358 uint32_t aFlags, float aOpacity) {
1359 gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
1360 ImageRegion region(aRegion);
1361 bool frameIsFinished = aSurface->IsFinished();
1363 #ifdef DEBUG
1364 NotifyDrawingObservers();
1365 #endif
1367 // By now we may have a frame with the requested size. If not, we need to
1368 // adjust the drawing parameters accordingly.
1369 IntSize finalSize = aSurface->GetSize();
1370 bool couldRedecodeForBetterFrame = false;
1371 if (finalSize != aSize) {
1372 gfx::Size scale(double(aSize.width) / finalSize.width,
1373 double(aSize.height) / finalSize.height);
1374 aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
1375 region.Scale(1.0 / scale.width, 1.0 / scale.height);
1377 couldRedecodeForBetterFrame = CanDownscaleDuringDecode(aSize, aFlags);
1380 if (!aSurface->Draw(aContext, region, aSamplingFilter, aFlags, aOpacity)) {
1381 RecoverFromInvalidFrames(aSize, aFlags);
1382 return ImgDrawResult::TEMPORARY_ERROR;
1384 if (!frameIsFinished) {
1385 return ImgDrawResult::INCOMPLETE;
1387 if (couldRedecodeForBetterFrame) {
1388 return ImgDrawResult::WRONG_SIZE;
1390 return ImgDrawResult::SUCCESS;
1393 //******************************************************************************
1394 NS_IMETHODIMP_(ImgDrawResult)
1395 RasterImage::Draw(gfxContext* aContext, const IntSize& aSize,
1396 const ImageRegion& aRegion, uint32_t aWhichFrame,
1397 SamplingFilter aSamplingFilter,
1398 const Maybe<SVGImageContext>& /*aSVGContext - ignored*/,
1399 uint32_t aFlags, float aOpacity) {
1400 if (aWhichFrame > FRAME_MAX_VALUE) {
1401 return ImgDrawResult::BAD_ARGS;
1404 if (mError) {
1405 return ImgDrawResult::BAD_IMAGE;
1408 // Illegal -- you can't draw with non-default decode flags.
1409 // (Disabling colorspace conversion might make sense to allow, but
1410 // we don't currently.)
1411 if (ToSurfaceFlags(aFlags) != DefaultSurfaceFlags()) {
1412 return ImgDrawResult::BAD_ARGS;
1415 if (!aContext) {
1416 return ImgDrawResult::BAD_ARGS;
1419 if (mAnimationConsumers == 0) {
1420 SendOnUnlockedDraw(aFlags);
1423 // If we're not using SamplingFilter::GOOD, we shouldn't high-quality scale or
1424 // downscale during decode.
1425 uint32_t flags = aSamplingFilter == SamplingFilter::GOOD
1426 ? aFlags
1427 : aFlags & ~FLAG_HIGH_QUALITY_SCALING;
1429 LookupResult result = LookupFrame(aSize, flags, ToPlaybackType(aWhichFrame),
1430 /* aMarkUsed = */ true);
1431 if (!result) {
1432 // Getting the frame (above) touches the image and kicks off decoding.
1433 if (mDrawStartTime.IsNull()) {
1434 mDrawStartTime = TimeStamp::Now();
1436 return ImgDrawResult::NOT_READY;
1439 bool shouldRecordTelemetry =
1440 !mDrawStartTime.IsNull() && result.Surface()->IsFinished();
1442 auto drawResult = DrawInternal(std::move(result.Surface()), aContext, aSize,
1443 aRegion, aSamplingFilter, flags, aOpacity);
1445 if (shouldRecordTelemetry) {
1446 TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
1447 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY,
1448 int32_t(drawLatency.ToMicroseconds()));
1449 mDrawStartTime = TimeStamp();
1452 return drawResult;
1455 //******************************************************************************
1457 NS_IMETHODIMP
1458 RasterImage::LockImage() {
1459 MOZ_ASSERT(NS_IsMainThread(),
1460 "Main thread to encourage serialization with UnlockImage");
1461 if (mError) {
1462 return NS_ERROR_FAILURE;
1465 // Increment the lock count
1466 mLockCount++;
1468 // Lock this image's surfaces in the SurfaceCache.
1469 if (mLockCount == 1) {
1470 SurfaceCache::LockImage(ImageKey(this));
1473 return NS_OK;
1476 //******************************************************************************
1478 NS_IMETHODIMP
1479 RasterImage::UnlockImage() {
1480 MOZ_ASSERT(NS_IsMainThread(),
1481 "Main thread to encourage serialization with LockImage");
1482 if (mError) {
1483 return NS_ERROR_FAILURE;
1486 // It's an error to call this function if the lock count is 0
1487 MOZ_ASSERT(mLockCount > 0, "Calling UnlockImage with mLockCount == 0!");
1488 if (mLockCount == 0) {
1489 return NS_ERROR_ABORT;
1492 // Decrement our lock count
1493 mLockCount--;
1495 // Unlock this image's surfaces in the SurfaceCache.
1496 if (mLockCount == 0) {
1497 SurfaceCache::UnlockImage(ImageKey(this));
1500 return NS_OK;
1503 //******************************************************************************
1505 NS_IMETHODIMP
1506 RasterImage::RequestDiscard() {
1507 if (mDiscardable && // Enabled at creation time...
1508 mLockCount == 0 && // ...not temporarily disabled...
1509 CanDiscard()) {
1510 Discard();
1513 return NS_OK;
1516 // Indempotent error flagging routine. If a decoder is open, shuts it down.
1517 void RasterImage::DoError() {
1518 // If we've flagged an error before, we have nothing to do
1519 if (mError) {
1520 return;
1523 // We can't safely handle errors off-main-thread, so dispatch a worker to
1524 // do it.
1525 if (!NS_IsMainThread()) {
1526 HandleErrorWorker::DispatchIfNeeded(this);
1527 return;
1530 // Put the container in an error state.
1531 mError = true;
1533 // Stop animation and release our FrameAnimator.
1534 if (mAnimating) {
1535 StopAnimation();
1537 mAnimationState = Nothing();
1538 mFrameAnimator = nullptr;
1540 // Release all locks.
1541 mLockCount = 0;
1542 SurfaceCache::UnlockImage(ImageKey(this));
1544 // Release all frames from the surface cache.
1545 SurfaceCache::RemoveImage(ImageKey(this));
1547 // Invalidate to get rid of any partially-drawn image content.
1548 NotifyProgress(NoProgress, IntRect(0, 0, mSize.width, mSize.height));
1550 MOZ_LOG(gImgLog, LogLevel::Error,
1551 ("RasterImage: [this=%p] Error detected for image\n", this));
1554 /* static */
1555 void RasterImage::HandleErrorWorker::DispatchIfNeeded(RasterImage* aImage) {
1556 RefPtr<HandleErrorWorker> worker = new HandleErrorWorker(aImage);
1557 NS_DispatchToMainThread(worker);
1560 RasterImage::HandleErrorWorker::HandleErrorWorker(RasterImage* aImage)
1561 : Runnable("image::RasterImage::HandleErrorWorker"), mImage(aImage) {
1562 MOZ_ASSERT(mImage, "Should have image");
1565 NS_IMETHODIMP
1566 RasterImage::HandleErrorWorker::Run() {
1567 mImage->DoError();
1569 return NS_OK;
1572 bool RasterImage::ShouldAnimate() {
1573 return ImageResource::ShouldAnimate() && mAnimationState &&
1574 mAnimationState->KnownFrameCount() >= 1 && !mAnimationFinished;
1577 #ifdef DEBUG
1578 NS_IMETHODIMP
1579 RasterImage::GetFramesNotified(uint32_t* aFramesNotified) {
1580 NS_ENSURE_ARG_POINTER(aFramesNotified);
1582 *aFramesNotified = mFramesNotified;
1584 return NS_OK;
1586 #endif
1588 void RasterImage::NotifyProgress(
1589 Progress aProgress, const IntRect& aInvalidRect /* = IntRect() */,
1590 const Maybe<uint32_t>& aFrameCount /* = Nothing() */,
1591 DecoderFlags aDecoderFlags
1592 /* = DefaultDecoderFlags() */,
1593 SurfaceFlags aSurfaceFlags
1594 /* = DefaultSurfaceFlags() */) {
1595 MOZ_ASSERT(NS_IsMainThread());
1597 // Ensure that we stay alive long enough to finish notifying.
1598 RefPtr<RasterImage> image = this;
1600 const bool wasDefaultFlags = aSurfaceFlags == DefaultSurfaceFlags();
1602 if (!aInvalidRect.IsEmpty() && wasDefaultFlags) {
1603 // Update our image container since we're invalidating.
1604 UpdateImageContainer(Some(aInvalidRect));
1607 if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY)) {
1608 // We may have decoded new animation frames; update our animation state.
1609 MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError);
1610 if (mAnimationState && aFrameCount) {
1611 mAnimationState->UpdateKnownFrameCount(*aFrameCount);
1614 // If we should start animating right now, do so.
1615 if (mAnimationState && aFrameCount == Some(1u) && mPendingAnimation &&
1616 ShouldAnimate()) {
1617 StartAnimation();
1621 // Tell the observers what happened.
1622 image->mProgressTracker->SyncNotifyProgress(aProgress, aInvalidRect);
1625 void RasterImage::NotifyDecodeComplete(
1626 const DecoderFinalStatus& aStatus, const ImageMetadata& aMetadata,
1627 const DecoderTelemetry& aTelemetry, Progress aProgress,
1628 const IntRect& aInvalidRect, const Maybe<uint32_t>& aFrameCount,
1629 DecoderFlags aDecoderFlags, SurfaceFlags aSurfaceFlags) {
1630 MOZ_ASSERT(NS_IsMainThread());
1632 // If the decoder detected an error, log it to the error console.
1633 if (aStatus.mShouldReportError) {
1634 ReportDecoderError();
1637 // Record all the metadata the decoder gathered about this image.
1638 bool metadataOK = SetMetadata(aMetadata, aStatus.mWasMetadataDecode);
1639 if (!metadataOK) {
1640 // This indicates a serious error that requires us to discard all existing
1641 // surfaces and redecode to recover. We'll drop the results from this
1642 // decoder on the floor, since they aren't valid.
1643 RecoverFromInvalidFrames(mSize, FromSurfaceFlags(aSurfaceFlags));
1644 return;
1647 MOZ_ASSERT(mError || mHasSize || !aMetadata.HasSize(),
1648 "SetMetadata should've gotten a size");
1650 if (!aStatus.mWasMetadataDecode && aStatus.mFinished) {
1651 // Flag that we've been decoded before.
1652 mHasBeenDecoded = true;
1655 // Send out any final notifications.
1656 NotifyProgress(aProgress, aInvalidRect, aFrameCount, aDecoderFlags,
1657 aSurfaceFlags);
1659 if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY) && mHasBeenDecoded &&
1660 mAnimationState) {
1661 // We've finished a full decode of all animation frames and our
1662 // AnimationState has been notified about them all, so let it know not to
1663 // expect anymore.
1664 mAnimationState->NotifyDecodeComplete();
1665 gfx::IntRect rect =
1666 mAnimationState->UpdateState(mAnimationFinished, this, mSize);
1667 if (!rect.IsEmpty()) {
1668 NotifyProgress(NoProgress, rect);
1672 // Do some telemetry if this isn't a metadata decode.
1673 if (!aStatus.mWasMetadataDecode) {
1674 if (aTelemetry.mChunkCount) {
1675 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS,
1676 aTelemetry.mChunkCount);
1679 if (aStatus.mFinished) {
1680 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
1681 int32_t(aTelemetry.mDecodeTime.ToMicroseconds()));
1683 if (aTelemetry.mSpeedHistogram && aTelemetry.mBytesDecoded) {
1684 Telemetry::Accumulate(*aTelemetry.mSpeedHistogram, aTelemetry.Speed());
1689 // Only act on errors if we have no usable frames from the decoder.
1690 if (aStatus.mHadError &&
1691 (!mAnimationState || mAnimationState->KnownFrameCount() == 0)) {
1692 DoError();
1693 } else if (aStatus.mWasMetadataDecode && !mHasSize) {
1694 DoError();
1697 // XXX(aosmond): Can we get this far without mFinished == true?
1698 if (aStatus.mFinished && aStatus.mWasMetadataDecode) {
1699 // If we were waiting to fire the load event, go ahead and fire it now.
1700 if (mLoadProgress) {
1701 NotifyForLoadEvent(*mLoadProgress);
1702 mLoadProgress = Nothing();
1705 // If we were a metadata decode and a full decode was requested, do it.
1706 if (mWantFullDecode) {
1707 mWantFullDecode = false;
1708 RequestDecodeForSize(mSize,
1709 DECODE_FLAGS_DEFAULT | FLAG_HIGH_QUALITY_SCALING,
1710 FRAME_CURRENT);
1715 void RasterImage::ReportDecoderError() {
1716 nsCOMPtr<nsIConsoleService> consoleService =
1717 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1718 nsCOMPtr<nsIScriptError> errorObject =
1719 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
1721 if (consoleService && errorObject) {
1722 nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated."));
1723 nsAutoString src;
1724 if (GetURI()) {
1725 nsAutoCString uri;
1726 if (!GetSpecTruncatedTo1k(uri)) {
1727 msg += NS_LITERAL_STRING(" URI in this note truncated due to length.");
1729 src = NS_ConvertUTF8toUTF16(uri);
1731 if (NS_SUCCEEDED(errorObject->InitWithWindowID(msg, src, EmptyString(), 0,
1732 0, nsIScriptError::errorFlag,
1733 "Image", InnerWindowID()))) {
1734 consoleService->LogMessage(errorObject);
1739 already_AddRefed<imgIContainer> RasterImage::Unwrap() {
1740 nsCOMPtr<imgIContainer> self(this);
1741 return self.forget();
1744 void RasterImage::PropagateUseCounters(dom::Document*) {
1745 // No use counters.
1748 IntSize RasterImage::OptimalImageSizeForDest(const gfxSize& aDest,
1749 uint32_t aWhichFrame,
1750 SamplingFilter aSamplingFilter,
1751 uint32_t aFlags) {
1752 MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
1753 aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
1754 "Unexpected destination size");
1756 if (mSize.IsEmpty() || aDest.IsEmpty()) {
1757 return IntSize(0, 0);
1760 IntSize destSize = IntSize::Ceil(aDest.width, aDest.height);
1762 if (aSamplingFilter == SamplingFilter::GOOD &&
1763 CanDownscaleDuringDecode(destSize, aFlags)) {
1764 return destSize;
1767 // We can't scale to this size. Use our intrinsic size for now.
1768 return mSize;
1771 } // namespace image
1772 } // namespace mozilla