Bug 1704628 Part 1: Make selectContextMenuItem use .activateItem() semantics. r=ochameau
[gecko.git] / image / RasterImage.cpp
blob9c2f8447044a0b3ec7daa058e0cdd44bef055b64
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 "Layers.h"
23 #include "LookupResult.h"
24 #include "OrientedImage.h"
25 #include "SourceBuffer.h"
26 #include "SurfaceCache.h"
27 #include "gfx2DGlue.h"
28 #include "gfxContext.h"
29 #include "gfxPlatform.h"
30 #include "mozilla/ClearOnShutdown.h"
31 #include "mozilla/DebugOnly.h"
32 #include "mozilla/Likely.h"
33 #include "mozilla/MemoryReporting.h"
34 #include "mozilla/RefPtr.h"
35 #include "mozilla/SizeOfState.h"
36 #include "mozilla/StaticPrefs_image.h"
37 #include "mozilla/Telemetry.h"
38 #include "mozilla/TimeStamp.h"
39 #include "mozilla/Tuple.h"
40 #include "mozilla/gfx/2D.h"
41 #include "mozilla/gfx/Scale.h"
42 #include "nsComponentManagerUtils.h"
43 #include "nsError.h"
44 #include "nsIConsoleService.h"
45 #include "nsIInputStream.h"
46 #include "nsIScriptError.h"
47 #include "nsISupportsPrimitives.h"
48 #include "nsMemory.h"
49 #include "nsPresContext.h"
50 #include "nsProperties.h"
51 #include "prenv.h"
52 #include "prsystem.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 // Mark us as initialized
134 mInitialized = true;
136 return NS_OK;
139 //******************************************************************************
140 NS_IMETHODIMP_(void)
141 RasterImage::RequestRefresh(const TimeStamp& aTime) {
142 if (HadRecentRefresh(aTime)) {
143 return;
146 EvaluateAnimation();
148 if (!mAnimating) {
149 return;
152 RefreshResult res;
153 if (mAnimationState) {
154 MOZ_ASSERT(mFrameAnimator);
155 res = mFrameAnimator->RequestRefresh(*mAnimationState, aTime);
158 #ifdef DEBUG
159 if (res.mFrameAdvanced) {
160 mFramesNotified++;
162 #endif
164 // Notify listeners that our frame has actually changed, but do this only
165 // once for all frames that we've now passed (if AdvanceFrame() was called
166 // more than once).
167 if (!res.mDirtyRect.IsEmpty() || res.mFrameAdvanced) {
168 auto dirtyRect = UnorientedIntRect::FromUnknownRect(res.mDirtyRect);
169 NotifyProgress(NoProgress, dirtyRect);
172 if (res.mAnimationFinished) {
173 StoreAnimationFinished(true);
174 EvaluateAnimation();
178 //******************************************************************************
179 NS_IMETHODIMP
180 RasterImage::GetWidth(int32_t* aWidth) {
181 NS_ENSURE_ARG_POINTER(aWidth);
183 if (mError) {
184 *aWidth = 0;
185 return NS_ERROR_FAILURE;
188 *aWidth = mSize.width;
189 return NS_OK;
192 //******************************************************************************
193 NS_IMETHODIMP
194 RasterImage::GetHeight(int32_t* aHeight) {
195 NS_ENSURE_ARG_POINTER(aHeight);
197 if (mError) {
198 *aHeight = 0;
199 return NS_ERROR_FAILURE;
202 *aHeight = mSize.height;
203 return NS_OK;
206 //******************************************************************************
207 nsresult RasterImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const {
208 if (mError) {
209 return NS_ERROR_FAILURE;
212 aNativeSizes.Clear();
214 if (mNativeSizes.IsEmpty()) {
215 aNativeSizes.AppendElement(mSize.ToUnknownSize());
216 } else {
217 for (const auto& size : mNativeSizes) {
218 aNativeSizes.AppendElement(size.ToUnknownSize());
222 return NS_OK;
225 //******************************************************************************
226 size_t RasterImage::GetNativeSizesLength() const {
227 if (mError || !LoadHasSize()) {
228 return 0;
231 if (mNativeSizes.IsEmpty()) {
232 return 1;
235 return mNativeSizes.Length();
238 //******************************************************************************
239 NS_IMETHODIMP
240 RasterImage::GetIntrinsicSize(nsSize* aSize) {
241 if (mError) {
242 return NS_ERROR_FAILURE;
245 *aSize = nsSize(nsPresContext::CSSPixelsToAppUnits(mSize.width),
246 nsPresContext::CSSPixelsToAppUnits(mSize.height));
247 return NS_OK;
250 //******************************************************************************
251 Maybe<AspectRatio> RasterImage::GetIntrinsicRatio() {
252 if (mError) {
253 return Nothing();
256 return Some(AspectRatio::FromSize(mSize.width, mSize.height));
259 NS_IMETHODIMP_(Orientation)
260 RasterImage::GetOrientation() { return mOrientation; }
262 //******************************************************************************
263 NS_IMETHODIMP
264 RasterImage::GetType(uint16_t* aType) {
265 NS_ENSURE_ARG_POINTER(aType);
267 *aType = imgIContainer::TYPE_RASTER;
268 return NS_OK;
271 NS_IMETHODIMP
272 RasterImage::GetProducerId(uint32_t* aId) {
273 NS_ENSURE_ARG_POINTER(aId);
275 *aId = ImageResource::GetImageProducerId();
276 return NS_OK;
279 LookupResult RasterImage::LookupFrameInternal(const UnorientedIntSize& aSize,
280 uint32_t aFlags,
281 PlaybackType aPlaybackType,
282 bool aMarkUsed) {
283 if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
284 MOZ_ASSERT(mFrameAnimator);
285 MOZ_ASSERT(ToSurfaceFlags(aFlags) == DefaultSurfaceFlags(),
286 "Can't composite frames with non-default surface flags");
287 return mFrameAnimator->GetCompositedFrame(*mAnimationState, aMarkUsed);
290 SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
292 // We don't want any substitution for sync decodes, and substitution would be
293 // illegal when high quality downscaling is disabled, so we use
294 // SurfaceCache::Lookup in this case.
295 if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
296 return SurfaceCache::Lookup(
297 ImageKey(this),
298 RasterSurfaceKey(aSize.ToUnknownSize(), surfaceFlags,
299 PlaybackType::eStatic),
300 aMarkUsed);
303 // We'll return the best match we can find to the requested frame.
304 return SurfaceCache::LookupBestMatch(
305 ImageKey(this),
306 RasterSurfaceKey(aSize.ToUnknownSize(), surfaceFlags,
307 PlaybackType::eStatic),
308 aMarkUsed);
311 LookupResult RasterImage::LookupFrame(const UnorientedIntSize& aSize,
312 uint32_t aFlags,
313 PlaybackType aPlaybackType,
314 bool aMarkUsed) {
315 MOZ_ASSERT(NS_IsMainThread());
317 // If we're opaque, we don't need to care about premultiplied alpha, because
318 // that can only matter for frames with transparency.
319 if (IsOpaque()) {
320 aFlags &= ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
323 UnorientedIntSize requestedSize =
324 CanDownscaleDuringDecode(aSize, aFlags) ? aSize : ToUnoriented(mSize);
325 if (requestedSize.IsEmpty()) {
326 // Can't decode to a surface of zero size.
327 return LookupResult(MatchType::NOT_FOUND);
330 LookupResult result =
331 LookupFrameInternal(requestedSize, aFlags, aPlaybackType, aMarkUsed);
333 if (!result && !LoadHasSize()) {
334 // We can't request a decode without knowing our intrinsic size. Give up.
335 return LookupResult(MatchType::NOT_FOUND);
338 const bool syncDecode = aFlags & FLAG_SYNC_DECODE;
339 const bool avoidRedecode = aFlags & FLAG_AVOID_REDECODE_FOR_SIZE;
340 if (result.Type() == MatchType::NOT_FOUND ||
341 (result.Type() == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND &&
342 !avoidRedecode) ||
343 (syncDecode && !avoidRedecode && !result)) {
344 // We don't have a copy of this frame, and there's no decoder working on
345 // one. (Or we're sync decoding and the existing decoder hasn't even started
346 // yet.) Trigger decoding so it'll be available next time.
347 MOZ_ASSERT(aPlaybackType != PlaybackType::eAnimated ||
348 StaticPrefs::image_mem_animated_discardable_AtStartup() ||
349 !mAnimationState || mAnimationState->KnownFrameCount() < 1,
350 "Animated frames should be locked");
352 // The surface cache may suggest the preferred size we are supposed to
353 // decode at. This should only happen if we accept substitutions.
354 if (!result.SuggestedSize().IsEmpty()) {
355 MOZ_ASSERT(!syncDecode && (aFlags & FLAG_HIGH_QUALITY_SCALING));
356 requestedSize =
357 UnorientedIntSize::FromUnknownSize(result.SuggestedSize());
360 bool ranSync = false, failed = false;
361 Decode(requestedSize, aFlags, aPlaybackType, ranSync, failed);
362 if (failed) {
363 result.SetFailedToRequestDecode();
366 // If we can or did sync decode, we should already have the frame.
367 if (ranSync || syncDecode) {
368 result =
369 LookupFrameInternal(requestedSize, aFlags, aPlaybackType, aMarkUsed);
373 if (!result) {
374 // We still weren't able to get a frame. Give up.
375 return result;
378 // Sync decoding guarantees that we got the frame, but if it's owned by an
379 // async decoder that's currently running, the contents of the frame may not
380 // be available yet. Make sure we get everything.
381 if (LoadAllSourceData() && syncDecode) {
382 result.Surface()->WaitUntilFinished();
385 // If we could have done some decoding in this function we need to check if
386 // that decoding encountered an error and hence aborted the surface. We want
387 // to avoid calling IsAborted if we weren't passed any sync decode flag
388 // because IsAborted acquires the monitor for the imgFrame.
389 if (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST) &&
390 result.Surface()->IsAborted()) {
391 DrawableSurface tmp = std::move(result.Surface());
392 return result;
395 return result;
398 bool RasterImage::IsOpaque() {
399 if (mError) {
400 return false;
403 Progress progress = mProgressTracker->GetProgress();
405 // If we haven't yet finished decoding, the safe answer is "not opaque".
406 if (!(progress & FLAG_DECODE_COMPLETE)) {
407 return false;
410 // Other, we're opaque if FLAG_HAS_TRANSPARENCY is not set.
411 return !(progress & FLAG_HAS_TRANSPARENCY);
414 NS_IMETHODIMP_(bool)
415 RasterImage::WillDrawOpaqueNow() {
416 if (!IsOpaque()) {
417 return false;
420 if (mAnimationState) {
421 if (!StaticPrefs::image_mem_animated_discardable_AtStartup()) {
422 // We never discard frames of animated images.
423 return true;
424 } else {
425 if (mAnimationState->GetCompositedFrameInvalid()) {
426 // We're not going to draw anything at all.
427 return false;
432 // If we are not locked our decoded data could get discard at any time (ie
433 // between the call to this function and when we are asked to draw), so we
434 // have to return false if we are unlocked.
435 if (mLockCount == 0) {
436 return false;
439 auto size = ToUnoriented(mSize);
440 LookupResult result = SurfaceCache::LookupBestMatch(
441 ImageKey(this),
442 RasterSurfaceKey(size.ToUnknownSize(), DefaultSurfaceFlags(),
443 PlaybackType::eStatic),
444 /* aMarkUsed = */ false);
445 MatchType matchType = result.Type();
446 if (matchType == MatchType::NOT_FOUND || matchType == MatchType::PENDING ||
447 !result.Surface()->IsFinished()) {
448 return false;
451 return true;
454 void RasterImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) {
455 MOZ_ASSERT(mProgressTracker);
457 bool animatedFramesDiscarded =
458 mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated;
460 nsCOMPtr<nsIEventTarget> eventTarget;
461 if (mProgressTracker) {
462 eventTarget = mProgressTracker->GetEventTarget();
463 } else {
464 eventTarget = do_GetMainThread();
467 RefPtr<RasterImage> image = this;
468 nsCOMPtr<nsIRunnable> ev =
469 NS_NewRunnableFunction("RasterImage::OnSurfaceDiscarded", [=]() -> void {
470 image->OnSurfaceDiscardedInternal(animatedFramesDiscarded);
472 eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
475 void RasterImage::OnSurfaceDiscardedInternal(bool aAnimatedFramesDiscarded) {
476 MOZ_ASSERT(NS_IsMainThread());
478 if (aAnimatedFramesDiscarded && mAnimationState) {
479 MOZ_ASSERT(StaticPrefs::image_mem_animated_discardable_AtStartup());
480 ReleaseImageContainer();
482 auto size = ToUnoriented(mSize);
483 IntRect rect = mAnimationState->UpdateState(this, size.ToUnknownSize());
485 auto dirtyRect = UnorientedIntRect::FromUnknownRect(rect);
486 NotifyProgress(NoProgress, dirtyRect);
489 if (mProgressTracker) {
490 mProgressTracker->OnDiscard();
494 //******************************************************************************
495 NS_IMETHODIMP
496 RasterImage::GetAnimated(bool* aAnimated) {
497 if (mError) {
498 return NS_ERROR_FAILURE;
501 NS_ENSURE_ARG_POINTER(aAnimated);
503 // If we have an AnimationState, we can know for sure.
504 if (mAnimationState) {
505 *aAnimated = true;
506 return NS_OK;
509 // Otherwise, we need to have been decoded to know for sure, since if we were
510 // decoded at least once mAnimationState would have been created for animated
511 // images. This is true even though we check for animation during the
512 // metadata decode, because we may still discover animation only during the
513 // full decode for corrupt images.
514 if (!LoadHasBeenDecoded()) {
515 return NS_ERROR_NOT_AVAILABLE;
518 // We know for sure
519 *aAnimated = false;
521 return NS_OK;
524 //******************************************************************************
525 NS_IMETHODIMP_(int32_t)
526 RasterImage::GetFirstFrameDelay() {
527 if (mError) {
528 return -1;
531 bool animated = false;
532 if (NS_FAILED(GetAnimated(&animated)) || !animated) {
533 return -1;
536 MOZ_ASSERT(mAnimationState, "Animated images should have an AnimationState");
537 return mAnimationState->FirstFrameTimeout().AsEncodedValueDeprecated();
540 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
541 RasterImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) {
542 return GetFrameAtSize(mSize.ToUnknownSize(), aWhichFrame, aFlags);
545 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
546 RasterImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame,
547 uint32_t aFlags) {
548 AutoProfilerImagePaintMarker PROFILER_RAII(this);
549 #ifdef DEBUG
550 NotifyDrawingObservers();
551 #endif
553 auto result = GetFrameInternal(aSize, Nothing(), aWhichFrame, aFlags);
554 return mozilla::Get<2>(result).forget();
557 Tuple<ImgDrawResult, IntSize, RefPtr<SourceSurface>>
558 RasterImage::GetFrameInternal(const IntSize& aSize,
559 const Maybe<SVGImageContext>& aSVGContext,
560 uint32_t aWhichFrame, uint32_t aFlags) {
561 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
563 auto size = OrientedIntSize::FromUnknownSize(aSize);
565 if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE) {
566 return MakeTuple(ImgDrawResult::BAD_ARGS, aSize, RefPtr<SourceSurface>());
569 if (mError) {
570 return MakeTuple(ImgDrawResult::BAD_IMAGE, aSize, RefPtr<SourceSurface>());
573 // Get the frame. If it's not there, it's probably the caller's fault for
574 // not waiting for the data to be loaded from the network or not passing
575 // FLAG_SYNC_DECODE.
576 LookupResult result =
577 LookupFrame(ToUnoriented(size), aFlags, ToPlaybackType(aWhichFrame),
578 /* aMarkUsed = */ true);
579 auto resultSuggestedSize =
580 UnorientedIntSize::FromUnknownSize(result.SuggestedSize());
582 // The surface cache may have suggested we use a different size than the
583 // given size in the future. This may or may not be accompanied by an
584 // actual surface, depending on what it has in its cache.
585 OrientedIntSize suggestedSize = ToOriented(resultSuggestedSize);
586 if (suggestedSize.IsEmpty()) {
587 suggestedSize = size;
589 MOZ_ASSERT_IF(result.Type() == MatchType::SUBSTITUTE_BECAUSE_BEST,
590 suggestedSize != size);
592 if (!result) {
593 // The OS threw this frame away and we couldn't redecode it.
594 return MakeTuple(ImgDrawResult::TEMPORARY_ERROR,
595 suggestedSize.ToUnknownSize(), RefPtr<SourceSurface>());
598 RefPtr<SourceSurface> surface = result.Surface()->GetSourceSurface();
600 // If this RasterImage requires orientation, we must return a newly created
601 // surface with the oriented image instead of returning the frame's surface
602 // directly.
603 surface = OrientedImage::OrientSurface(mOrientation, surface);
605 if (!result.Surface()->IsFinished()) {
606 return MakeTuple(ImgDrawResult::INCOMPLETE, suggestedSize.ToUnknownSize(),
607 std::move(surface));
610 return MakeTuple(ImgDrawResult::SUCCESS, suggestedSize.ToUnknownSize(),
611 std::move(surface));
614 Tuple<ImgDrawResult, IntSize> RasterImage::GetImageContainerSize(
615 LayerManager* aManager, const IntSize& aRequestedSize, uint32_t aFlags) {
616 if (!LoadHasSize()) {
617 return MakeTuple(ImgDrawResult::NOT_READY, IntSize(0, 0));
620 if (aRequestedSize.IsEmpty()) {
621 return MakeTuple(ImgDrawResult::BAD_ARGS, IntSize(0, 0));
624 // We check the minimum size because while we support downscaling, we do not
625 // support upscaling. If aRequestedSize > mSize, we will never give a larger
626 // surface than mSize. If mSize > aRequestedSize, and mSize > maxTextureSize,
627 // we still want to use image containers if aRequestedSize <= maxTextureSize.
628 int32_t maxTextureSize = aManager->GetMaxTextureSize();
629 if (min(mSize.width, aRequestedSize.width) > maxTextureSize ||
630 min(mSize.height, aRequestedSize.height) > maxTextureSize) {
631 return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0));
634 auto requestedSize = OrientedIntSize::FromUnknownSize(aRequestedSize);
635 if (!CanDownscaleDuringDecode(ToUnoriented(requestedSize), aFlags)) {
636 return MakeTuple(ImgDrawResult::SUCCESS, mSize.ToUnknownSize());
639 return MakeTuple(ImgDrawResult::SUCCESS, aRequestedSize);
642 NS_IMETHODIMP_(bool)
643 RasterImage::IsImageContainerAvailable(LayerManager* aManager,
644 uint32_t aFlags) {
645 return IsImageContainerAvailableAtSize(aManager, mSize.ToUnknownSize(),
646 aFlags);
649 NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
650 RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags) {
651 RefPtr<ImageContainer> container;
652 ImgDrawResult drawResult =
653 GetImageContainerImpl(aManager, mSize.ToUnknownSize(), Nothing(), aFlags,
654 getter_AddRefs(container));
656 // We silence the unused warning here because anything that needs the draw
657 // result should be using GetImageContainerAtSize, not GetImageContainer.
658 (void)drawResult;
659 return container.forget();
662 NS_IMETHODIMP_(bool)
663 RasterImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
664 const IntSize& aRequestedSize,
665 uint32_t aFlags) {
666 // We check the minimum size because while we support downscaling, we do not
667 // support upscaling. If aRequestedSize > mSize, we will never give a larger
668 // surface than mSize. If mSize > aRequestedSize, and mSize > maxTextureSize,
669 // we still want to use image containers if aRequestedSize <= maxTextureSize.
670 int32_t maxTextureSize = aManager->GetMaxTextureSize();
671 if (!LoadHasSize() || aRequestedSize.IsEmpty() ||
672 min(mSize.width, aRequestedSize.width) > maxTextureSize ||
673 min(mSize.height, aRequestedSize.height) > maxTextureSize) {
674 return false;
677 return true;
680 NS_IMETHODIMP_(ImgDrawResult)
681 RasterImage::GetImageContainerAtSize(layers::LayerManager* aManager,
682 const gfx::IntSize& aSize,
683 const Maybe<SVGImageContext>& aSVGContext,
684 uint32_t aFlags,
685 layers::ImageContainer** aOutContainer) {
686 // We do not pass in the given SVG context because in theory it could differ
687 // between calls, but actually have no impact on the actual contents of the
688 // image container.
689 return GetImageContainerImpl(aManager, aSize, Nothing(), aFlags,
690 aOutContainer);
693 size_t RasterImage::SizeOfSourceWithComputedFallback(
694 SizeOfState& aState) const {
695 return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(
696 aState.mMallocSizeOf);
699 void RasterImage::CollectSizeOfSurfaces(
700 nsTArray<SurfaceMemoryCounter>& aCounters,
701 MallocSizeOf aMallocSizeOf) const {
702 SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
705 bool RasterImage::SetMetadata(const ImageMetadata& aMetadata,
706 bool aFromMetadataDecode) {
707 MOZ_ASSERT(NS_IsMainThread());
709 if (mError) {
710 return true;
713 if (aMetadata.HasSize()) {
714 auto metadataSize = UnorientedIntSize::FromUnknownSize(aMetadata.GetSize());
715 if (metadataSize.width < 0 || metadataSize.height < 0) {
716 NS_WARNING("Image has negative intrinsic size");
717 DoError();
718 return true;
721 MOZ_ASSERT(aMetadata.HasOrientation());
722 Orientation orientation = aMetadata.GetOrientation();
724 // If we already have a size, check the new size against the old one.
725 if (LoadHasSize() &&
726 (metadataSize != ToUnoriented(mSize) || orientation != mOrientation)) {
727 NS_WARNING(
728 "Image changed size or orientation on redecode! "
729 "This should not happen!");
730 DoError();
731 return true;
734 // Set the size and flag that we have it.
735 mOrientation = orientation;
736 mSize = ToOriented(metadataSize);
737 mNativeSizes.Clear();
738 for (const auto& nativeSize : aMetadata.GetNativeSizes()) {
739 mNativeSizes.AppendElement(
740 ToOriented(UnorientedIntSize::FromUnknownSize(nativeSize)));
742 StoreHasSize(true);
745 if (LoadHasSize() && aMetadata.HasAnimation() && !mAnimationState) {
746 // We're becoming animated, so initialize animation stuff.
747 mAnimationState.emplace(mAnimationMode);
748 mFrameAnimator =
749 MakeUnique<FrameAnimator>(this, ToUnoriented(mSize).ToUnknownSize());
751 if (!StaticPrefs::image_mem_animated_discardable_AtStartup()) {
752 // We don't support discarding animated images (See bug 414259).
753 // Lock the image and throw away the key.
754 LockImage();
757 if (!aFromMetadataDecode) {
758 // The metadata decode reported that this image isn't animated, but we
759 // discovered that it actually was during the full decode. This is a
760 // rare failure that only occurs for corrupt images. To recover, we need
761 // to discard all existing surfaces and redecode.
762 return false;
766 if (mAnimationState) {
767 mAnimationState->SetLoopCount(aMetadata.GetLoopCount());
768 mAnimationState->SetFirstFrameTimeout(aMetadata.GetFirstFrameTimeout());
770 if (aMetadata.HasLoopLength()) {
771 mAnimationState->SetLoopLength(aMetadata.GetLoopLength());
773 if (aMetadata.HasFirstFrameRefreshArea()) {
774 mAnimationState->SetFirstFrameRefreshArea(
775 aMetadata.GetFirstFrameRefreshArea());
779 if (aMetadata.HasHotspot()) {
780 // NOTE(heycam): We shouldn't have any image formats that support both
781 // orientation and hotspots, so we assert that rather than add code
782 // to orient the hotspot point correctly.
783 MOZ_ASSERT(mOrientation.IsIdentity(), "Would need to orient hotspot point");
785 auto hotspot = aMetadata.GetHotspot();
786 mHotspot.x = std::max(std::min(hotspot.x, mSize.width - 1), 0);
787 mHotspot.y = std::max(std::min(hotspot.y, mSize.height - 1), 0);
790 return true;
793 NS_IMETHODIMP
794 RasterImage::SetAnimationMode(uint16_t aAnimationMode) {
795 if (mAnimationState) {
796 mAnimationState->SetAnimationMode(aAnimationMode);
798 return SetAnimationModeInternal(aAnimationMode);
801 //******************************************************************************
803 nsresult RasterImage::StartAnimation() {
804 if (mError) {
805 return NS_ERROR_FAILURE;
808 MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
810 // If we're not ready to animate, then set mPendingAnimation, which will cause
811 // us to start animating if and when we do become ready.
812 StorePendingAnimation(!mAnimationState ||
813 mAnimationState->KnownFrameCount() < 1);
814 if (LoadPendingAnimation()) {
815 return NS_OK;
818 // Don't bother to animate if we're displaying the first frame forever.
819 if (mAnimationState->GetCurrentAnimationFrameIndex() == 0 &&
820 mAnimationState->FirstFrameTimeout() == FrameTimeout::Forever()) {
821 StoreAnimationFinished(true);
822 return NS_ERROR_ABORT;
825 // We need to set the time that this initial frame was first displayed, as
826 // this is used in AdvanceFrame().
827 mAnimationState->InitAnimationFrameTimeIfNecessary();
829 return NS_OK;
832 //******************************************************************************
833 nsresult RasterImage::StopAnimation() {
834 MOZ_ASSERT(mAnimating, "Should be animating!");
836 nsresult rv = NS_OK;
837 if (mError) {
838 rv = NS_ERROR_FAILURE;
839 } else {
840 mAnimationState->SetAnimationFrameTime(TimeStamp());
843 mAnimating = false;
844 return rv;
847 //******************************************************************************
848 NS_IMETHODIMP
849 RasterImage::ResetAnimation() {
850 if (mError) {
851 return NS_ERROR_FAILURE;
854 StorePendingAnimation(false);
856 if (mAnimationMode == kDontAnimMode || !mAnimationState ||
857 mAnimationState->GetCurrentAnimationFrameIndex() == 0) {
858 return NS_OK;
861 StoreAnimationFinished(false);
863 if (mAnimating) {
864 StopAnimation();
867 MOZ_ASSERT(mAnimationState, "Should have AnimationState");
868 MOZ_ASSERT(mFrameAnimator, "Should have FrameAnimator");
869 mFrameAnimator->ResetAnimation(*mAnimationState);
871 IntRect area = mAnimationState->FirstFrameRefreshArea();
872 NotifyProgress(NoProgress, UnorientedIntRect::FromUnknownRect(area));
874 // Start the animation again. It may not have been running before, if
875 // mAnimationFinished was true before entering this function.
876 EvaluateAnimation();
878 return NS_OK;
881 //******************************************************************************
882 NS_IMETHODIMP_(void)
883 RasterImage::SetAnimationStartTime(const TimeStamp& aTime) {
884 if (mError || mAnimationMode == kDontAnimMode || mAnimating ||
885 !mAnimationState) {
886 return;
889 mAnimationState->SetAnimationFrameTime(aTime);
892 NS_IMETHODIMP_(float)
893 RasterImage::GetFrameIndex(uint32_t aWhichFrame) {
894 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
895 return (aWhichFrame == FRAME_FIRST || !mAnimationState)
896 ? 0.0f
897 : mAnimationState->GetCurrentAnimationFrameIndex();
900 NS_IMETHODIMP_(IntRect)
901 RasterImage::GetImageSpaceInvalidationRect(const IntRect& aRect) {
902 // Note that we do not transform aRect into an UnorientedIntRect, since
903 // RasterImage::NotifyProgress notifies all consumers of the image using
904 // OrientedIntRect values. (This is unlike OrientedImage, which notifies
905 // using inner image coordinates.)
906 return aRect;
909 nsresult RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*,
910 nsresult aStatus, bool aLastPart) {
911 MOZ_ASSERT(NS_IsMainThread());
913 // Record that we have all the data we're going to get now.
914 StoreAllSourceData(true);
916 // Let decoders know that there won't be any more data coming.
917 mSourceBuffer->Complete(aStatus);
919 // Allow a synchronous metadata decode if mSyncLoad was set, or if we're
920 // running on a single thread (in which case waiting for the async metadata
921 // decoder could delay this image's load event quite a bit), or if this image
922 // is transient.
923 bool canSyncDecodeMetadata =
924 LoadSyncLoad() || LoadTransient() || DecodePool::NumberOfCores() < 2;
926 if (canSyncDecodeMetadata && !LoadHasSize()) {
927 // We're loading this image synchronously, so it needs to be usable after
928 // this call returns. Since we haven't gotten our size yet, we need to do a
929 // synchronous metadata decode here.
930 DecodeMetadata(FLAG_SYNC_DECODE);
933 // Determine our final status, giving precedence to Necko failure codes. We
934 // check after running the metadata decode in case it triggered an error.
935 nsresult finalStatus = mError ? NS_ERROR_FAILURE : NS_OK;
936 if (NS_FAILED(aStatus)) {
937 finalStatus = aStatus;
940 // If loading failed, report an error.
941 if (NS_FAILED(finalStatus)) {
942 DoError();
945 Progress loadProgress = LoadCompleteProgress(aLastPart, mError, finalStatus);
947 if (!LoadHasSize() && !mError) {
948 // We don't have our size yet, so we'll fire the load event in SetSize().
949 MOZ_ASSERT(!canSyncDecodeMetadata,
950 "Firing load async after metadata sync decode?");
951 mLoadProgress = Some(loadProgress);
952 return finalStatus;
955 NotifyForLoadEvent(loadProgress);
957 return finalStatus;
960 void RasterImage::NotifyForLoadEvent(Progress aProgress) {
961 MOZ_ASSERT(LoadHasSize() || mError,
962 "Need to know size before firing load event");
963 MOZ_ASSERT(
964 !LoadHasSize() || (mProgressTracker->GetProgress() & FLAG_SIZE_AVAILABLE),
965 "Should have notified that the size is available if we have it");
967 // If we encountered an error, make sure we notify for that as well.
968 if (mError) {
969 aProgress |= FLAG_HAS_ERROR;
972 // Notify our listeners, which will fire this image's load event.
973 NotifyProgress(aProgress);
976 nsresult RasterImage::OnImageDataAvailable(nsIRequest*, nsISupports*,
977 nsIInputStream* aInputStream,
978 uint64_t, uint32_t aCount) {
979 nsresult rv = mSourceBuffer->AppendFromInputStream(aInputStream, aCount);
980 if (NS_SUCCEEDED(rv) && !LoadSomeSourceData()) {
981 StoreSomeSourceData(true);
982 if (!LoadSyncLoad()) {
983 // Create an async metadata decoder and verify we succeed in doing so.
984 rv = DecodeMetadata(DECODE_FLAGS_DEFAULT);
988 if (NS_FAILED(rv)) {
989 DoError();
991 return rv;
994 nsresult RasterImage::SetSourceSizeHint(uint32_t aSizeHint) {
995 if (aSizeHint == 0) {
996 return NS_OK;
999 nsresult rv = mSourceBuffer->ExpectLength(aSizeHint);
1000 if (rv == NS_ERROR_OUT_OF_MEMORY) {
1001 // Flush memory, try to get some back, and try again.
1002 rv = nsMemory::HeapMinimize(true);
1003 if (NS_SUCCEEDED(rv)) {
1004 rv = mSourceBuffer->ExpectLength(aSizeHint);
1008 return rv;
1011 nsresult RasterImage::GetHotspotX(int32_t* aX) {
1012 *aX = mHotspot.x;
1013 return NS_OK;
1016 nsresult RasterImage::GetHotspotY(int32_t* aY) {
1017 *aY = mHotspot.y;
1018 return NS_OK;
1021 void RasterImage::Discard() {
1022 MOZ_ASSERT(NS_IsMainThread());
1023 MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
1024 MOZ_ASSERT(!mAnimationState ||
1025 StaticPrefs::image_mem_animated_discardable_AtStartup(),
1026 "Asked to discard for animated image");
1028 // Delete all the decoded frames.
1029 SurfaceCache::RemoveImage(ImageKey(this));
1031 if (mAnimationState) {
1032 ReleaseImageContainer();
1034 auto size = ToUnoriented(mSize);
1035 IntRect rect = mAnimationState->UpdateState(this, size.ToUnknownSize());
1037 auto dirtyRect = UnorientedIntRect::FromUnknownRect(rect);
1038 NotifyProgress(NoProgress, dirtyRect);
1041 // Notify that we discarded.
1042 if (mProgressTracker) {
1043 mProgressTracker->OnDiscard();
1047 bool RasterImage::CanDiscard() {
1048 return LoadAllSourceData() &&
1049 // Can discard animated images if the pref is set
1050 (!mAnimationState ||
1051 StaticPrefs::image_mem_animated_discardable_AtStartup());
1054 NS_IMETHODIMP
1055 RasterImage::StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) {
1056 if (mError) {
1057 return NS_ERROR_FAILURE;
1060 if (!LoadHasSize()) {
1061 StoreWantFullDecode(true);
1062 return NS_OK;
1065 uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
1066 FLAG_HIGH_QUALITY_SCALING;
1067 return RequestDecodeForSize(mSize.ToUnknownSize(), flags, aWhichFrame);
1070 bool RasterImage::StartDecodingWithResult(uint32_t aFlags,
1071 uint32_t aWhichFrame) {
1072 if (mError) {
1073 return false;
1076 if (!LoadHasSize()) {
1077 StoreWantFullDecode(true);
1078 return false;
1081 uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
1082 FLAG_HIGH_QUALITY_SCALING;
1083 LookupResult result =
1084 RequestDecodeForSizeInternal(ToUnoriented(mSize), flags, aWhichFrame);
1085 DrawableSurface surface = std::move(result.Surface());
1086 return surface && surface->IsFinished();
1089 imgIContainer::DecodeResult RasterImage::RequestDecodeWithResult(
1090 uint32_t aFlags, uint32_t aWhichFrame) {
1091 MOZ_ASSERT(NS_IsMainThread());
1093 if (mError) {
1094 return imgIContainer::DECODE_REQUEST_FAILED;
1097 uint32_t flags = aFlags | FLAG_ASYNC_NOTIFY;
1098 LookupResult result =
1099 RequestDecodeForSizeInternal(ToUnoriented(mSize), flags, aWhichFrame);
1100 DrawableSurface surface = std::move(result.Surface());
1101 if (surface && surface->IsFinished()) {
1102 return imgIContainer::DECODE_SURFACE_AVAILABLE;
1104 if (result.GetFailedToRequestDecode()) {
1105 return imgIContainer::DECODE_REQUEST_FAILED;
1107 return imgIContainer::DECODE_REQUESTED;
1110 NS_IMETHODIMP
1111 RasterImage::RequestDecodeForSize(const IntSize& aSize, uint32_t aFlags,
1112 uint32_t aWhichFrame) {
1113 MOZ_ASSERT(NS_IsMainThread());
1115 if (mError) {
1116 return NS_ERROR_FAILURE;
1119 RequestDecodeForSizeInternal(
1120 ToUnoriented(OrientedIntSize::FromUnknownSize(aSize)), aFlags,
1121 aWhichFrame);
1123 return NS_OK;
1126 LookupResult RasterImage::RequestDecodeForSizeInternal(
1127 const UnorientedIntSize& aSize, uint32_t aFlags, uint32_t aWhichFrame) {
1128 MOZ_ASSERT(NS_IsMainThread());
1130 if (aWhichFrame > FRAME_MAX_VALUE) {
1131 return LookupResult(MatchType::NOT_FOUND);
1134 if (mError) {
1135 LookupResult result = LookupResult(MatchType::NOT_FOUND);
1136 result.SetFailedToRequestDecode();
1137 return result;
1140 if (!LoadHasSize()) {
1141 StoreWantFullDecode(true);
1142 return LookupResult(MatchType::NOT_FOUND);
1145 // Decide whether to sync decode images we can decode quickly. Here we are
1146 // explicitly trading off flashing for responsiveness in the case that we're
1147 // redecoding an image (see bug 845147).
1148 bool shouldSyncDecodeIfFast =
1149 !LoadHasBeenDecoded() && (aFlags & FLAG_SYNC_DECODE_IF_FAST);
1151 uint32_t flags =
1152 shouldSyncDecodeIfFast ? aFlags : aFlags & ~FLAG_SYNC_DECODE_IF_FAST;
1154 // Perform a frame lookup, which will implicitly start decoding if needed.
1155 return LookupFrame(aSize, flags, ToPlaybackType(aWhichFrame),
1156 /* aMarkUsed = */ false);
1159 static bool LaunchDecodingTask(IDecodingTask* aTask, RasterImage* aImage,
1160 uint32_t aFlags, bool aHaveSourceData) {
1161 if (aHaveSourceData) {
1162 nsCString uri(aImage->GetURIString());
1164 // If we have all the data, we can sync decode if requested.
1165 if (aFlags & imgIContainer::FLAG_SYNC_DECODE) {
1166 DecodePool::Singleton()->SyncRunIfPossible(aTask, uri);
1167 return true;
1170 if (aFlags & imgIContainer::FLAG_SYNC_DECODE_IF_FAST) {
1171 return DecodePool::Singleton()->SyncRunIfPreferred(aTask, uri);
1175 // Perform an async decode. We also take this path if we don't have all the
1176 // source data yet, since sync decoding is impossible in that situation.
1177 DecodePool::Singleton()->AsyncRun(aTask);
1178 return false;
1181 void RasterImage::Decode(const UnorientedIntSize& aSize, uint32_t aFlags,
1182 PlaybackType aPlaybackType, bool& aOutRanSync,
1183 bool& aOutFailed) {
1184 MOZ_ASSERT(NS_IsMainThread());
1186 if (mError) {
1187 aOutFailed = true;
1188 return;
1191 // If we don't have a size yet, we can't do any other decoding.
1192 if (!LoadHasSize()) {
1193 StoreWantFullDecode(true);
1194 return;
1197 // We're about to decode again, which may mean that some of the previous sizes
1198 // we've decoded at aren't useful anymore. We can allow them to expire from
1199 // the cache by unlocking them here. When the decode finishes, it will send an
1200 // invalidation that will cause all instances of this image to redraw. If this
1201 // image is locked, any surfaces that are still useful will become locked
1202 // again when LookupFrame touches them, and the remainder will eventually
1203 // expire.
1204 SurfaceCache::UnlockEntries(ImageKey(this));
1206 // Determine which flags we need to decode this image with.
1207 DecoderFlags decoderFlags = DefaultDecoderFlags();
1208 if (aFlags & FLAG_ASYNC_NOTIFY) {
1209 decoderFlags |= DecoderFlags::ASYNC_NOTIFY;
1211 if (LoadTransient()) {
1212 decoderFlags |= DecoderFlags::IMAGE_IS_TRANSIENT;
1214 if (LoadHasBeenDecoded()) {
1215 decoderFlags |= DecoderFlags::IS_REDECODE;
1217 if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
1218 // Used SurfaceCache::Lookup instead of SurfaceCache::LookupBestMatch. That
1219 // means the caller can handle a differently sized surface to be returned
1220 // at any point.
1221 decoderFlags |= DecoderFlags::CANNOT_SUBSTITUTE;
1224 SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
1225 if (IsOpaque()) {
1226 // If there's no transparency, it doesn't matter whether we premultiply
1227 // alpha or not.
1228 surfaceFlags &= ~SurfaceFlags::NO_PREMULTIPLY_ALPHA;
1231 // Create a decoder.
1232 RefPtr<IDecodingTask> task;
1233 nsresult rv;
1234 bool animated = mAnimationState && aPlaybackType == PlaybackType::eAnimated;
1235 if (animated) {
1236 size_t currentFrame = mAnimationState->GetCurrentAnimationFrameIndex();
1237 rv = DecoderFactory::CreateAnimationDecoder(
1238 mDecoderType, WrapNotNull(this), mSourceBuffer,
1239 ToUnoriented(mSize).ToUnknownSize(), decoderFlags, surfaceFlags,
1240 currentFrame, getter_AddRefs(task));
1241 } else {
1242 rv = DecoderFactory::CreateDecoder(
1243 mDecoderType, WrapNotNull(this), mSourceBuffer,
1244 ToUnoriented(mSize).ToUnknownSize(), aSize.ToUnknownSize(),
1245 decoderFlags, surfaceFlags, getter_AddRefs(task));
1248 if (rv == NS_ERROR_ALREADY_INITIALIZED) {
1249 // We raced with an already pending decoder, and it finished before we
1250 // managed to insert the new decoder. Pretend we did a sync call to make
1251 // the caller lookup in the surface cache again.
1252 MOZ_ASSERT(!task);
1253 aOutRanSync = true;
1254 return;
1257 if (animated) {
1258 // We pass false for aAllowInvalidation because we may be asked to use
1259 // async notifications. Any potential invalidation here will be sent when
1260 // RequestRefresh is called, or NotifyDecodeComplete.
1261 #ifdef DEBUG
1262 IntRect rect =
1263 #endif
1264 mAnimationState->UpdateState(this, ToUnoriented(mSize).ToUnknownSize(),
1265 false);
1266 MOZ_ASSERT(rect.IsEmpty());
1269 // Make sure DecoderFactory was able to create a decoder successfully.
1270 if (NS_FAILED(rv)) {
1271 MOZ_ASSERT(!task);
1272 aOutFailed = true;
1273 return;
1276 MOZ_ASSERT(task);
1277 mDecodeCount++;
1279 // We're ready to decode; start the decoder.
1280 aOutRanSync = LaunchDecodingTask(task, this, aFlags, LoadAllSourceData());
1283 NS_IMETHODIMP
1284 RasterImage::DecodeMetadata(uint32_t aFlags) {
1285 if (mError) {
1286 return NS_ERROR_FAILURE;
1289 MOZ_ASSERT(!LoadHasSize(), "Should not do unnecessary metadata decodes");
1291 // Create a decoder.
1292 RefPtr<IDecodingTask> task = DecoderFactory::CreateMetadataDecoder(
1293 mDecoderType, WrapNotNull(this), mSourceBuffer);
1295 // Make sure DecoderFactory was able to create a decoder successfully.
1296 if (!task) {
1297 return NS_ERROR_FAILURE;
1300 // We're ready to decode; start the decoder.
1301 LaunchDecodingTask(task, this, aFlags, LoadAllSourceData());
1302 return NS_OK;
1305 void RasterImage::RecoverFromInvalidFrames(const UnorientedIntSize& aSize,
1306 uint32_t aFlags) {
1307 if (!LoadHasSize()) {
1308 return;
1311 NS_WARNING("A RasterImage's frames became invalid. Attempting to recover...");
1313 // Discard all existing frames, since they're probably all now invalid.
1314 SurfaceCache::RemoveImage(ImageKey(this));
1316 // Relock the image if it's supposed to be locked.
1317 if (mLockCount > 0) {
1318 SurfaceCache::LockImage(ImageKey(this));
1321 bool unused1, unused2;
1323 // Animated images require some special handling, because we normally require
1324 // that they never be discarded.
1325 if (mAnimationState) {
1326 Decode(ToUnoriented(mSize), aFlags | FLAG_SYNC_DECODE,
1327 PlaybackType::eAnimated, unused1, unused2);
1328 ResetAnimation();
1329 return;
1332 // For non-animated images, it's fine to recover using an async decode.
1333 Decode(aSize, aFlags, PlaybackType::eStatic, unused1, unused2);
1336 static bool HaveSkia() {
1337 #ifdef MOZ_ENABLE_SKIA
1338 return true;
1339 #else
1340 return false;
1341 #endif
1344 bool RasterImage::CanDownscaleDuringDecode(const UnorientedIntSize& aSize,
1345 uint32_t aFlags) {
1346 // Check basic requirements: downscale-during-decode is enabled, Skia is
1347 // available, this image isn't transient, we have all the source data and know
1348 // our size, and the flags allow us to do it.
1349 if (!LoadHasSize() || LoadTransient() || !HaveSkia() ||
1350 !StaticPrefs::image_downscale_during_decode_enabled() ||
1351 !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
1352 return false;
1355 // We don't downscale animated images during decode.
1356 if (mAnimationState) {
1357 return false;
1360 // Never upscale.
1361 UnorientedIntSize ourSize = ToUnoriented(mSize);
1362 if (aSize.width >= ourSize.width || aSize.height >= ourSize.height) {
1363 return false;
1366 // Zero or negative width or height is unacceptable.
1367 if (aSize.width < 1 || aSize.height < 1) {
1368 return false;
1371 // There's no point in scaling if we can't store the result.
1372 if (!SurfaceCache::CanHold(aSize.ToUnknownSize())) {
1373 return false;
1376 return true;
1379 ImgDrawResult RasterImage::DrawInternal(DrawableSurface&& aSurface,
1380 gfxContext* aContext,
1381 const UnorientedIntSize& aSize,
1382 const ImageRegion& aRegion,
1383 SamplingFilter aSamplingFilter,
1384 uint32_t aFlags, float aOpacity) {
1385 gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
1386 ImageRegion region(aRegion);
1387 bool frameIsFinished = aSurface->IsFinished();
1389 AutoProfilerImagePaintMarker PROFILER_RAII(this);
1390 #ifdef DEBUG
1391 NotifyDrawingObservers();
1392 #endif
1394 // By now we may have a frame with the requested size. If not, we need to
1395 // adjust the drawing parameters accordingly.
1396 IntSize finalSize = aSurface->GetSize();
1397 bool couldRedecodeForBetterFrame = false;
1398 if (finalSize != aSize.ToUnknownSize()) {
1399 gfx::Size scale(double(aSize.width) / finalSize.width,
1400 double(aSize.height) / finalSize.height);
1401 aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
1402 region.Scale(1.0 / scale.width, 1.0 / scale.height);
1404 couldRedecodeForBetterFrame = CanDownscaleDuringDecode(aSize, aFlags);
1407 if (!aSurface->Draw(aContext, region, aSamplingFilter, aFlags, aOpacity)) {
1408 RecoverFromInvalidFrames(aSize, aFlags);
1409 return ImgDrawResult::TEMPORARY_ERROR;
1411 if (!frameIsFinished) {
1412 return ImgDrawResult::INCOMPLETE;
1414 if (couldRedecodeForBetterFrame) {
1415 return ImgDrawResult::WRONG_SIZE;
1417 return ImgDrawResult::SUCCESS;
1420 //******************************************************************************
1421 NS_IMETHODIMP_(ImgDrawResult)
1422 RasterImage::Draw(gfxContext* aContext, const IntSize& aSize,
1423 const ImageRegion& aRegion, uint32_t aWhichFrame,
1424 SamplingFilter aSamplingFilter,
1425 const Maybe<SVGImageContext>& /*aSVGContext - ignored*/,
1426 uint32_t aFlags, float aOpacity) {
1427 if (aWhichFrame > FRAME_MAX_VALUE) {
1428 return ImgDrawResult::BAD_ARGS;
1431 if (mError) {
1432 return ImgDrawResult::BAD_IMAGE;
1435 // Illegal -- you can't draw with non-default decode flags.
1436 // (Disabling colorspace conversion might make sense to allow, but
1437 // we don't currently.)
1438 if (ToSurfaceFlags(aFlags) != DefaultSurfaceFlags()) {
1439 return ImgDrawResult::BAD_ARGS;
1442 if (!aContext) {
1443 return ImgDrawResult::BAD_ARGS;
1446 if (mAnimationConsumers == 0) {
1447 SendOnUnlockedDraw(aFlags);
1450 // If we're not using SamplingFilter::GOOD, we shouldn't high-quality scale or
1451 // downscale during decode.
1452 uint32_t flags = aSamplingFilter == SamplingFilter::GOOD
1453 ? aFlags
1454 : aFlags & ~FLAG_HIGH_QUALITY_SCALING;
1456 auto size = ToUnoriented(OrientedIntSize::FromUnknownSize(aSize));
1457 LookupResult result = LookupFrame(size, flags, ToPlaybackType(aWhichFrame),
1458 /* aMarkUsed = */ true);
1459 if (!result) {
1460 // Getting the frame (above) touches the image and kicks off decoding.
1461 if (mDrawStartTime.IsNull()) {
1462 mDrawStartTime = TimeStamp::Now();
1464 return ImgDrawResult::NOT_READY;
1467 bool shouldRecordTelemetry =
1468 !mDrawStartTime.IsNull() && result.Surface()->IsFinished();
1470 ImgDrawResult drawResult;
1472 gfxContextMatrixAutoSaveRestore asr;
1473 ImageRegion region(aRegion);
1475 if (!mOrientation.IsIdentity()) {
1476 // Apply a transform so that the unoriented image is drawn in the
1477 // orientation expected by the caller.
1478 gfxMatrix matrix = OrientationMatrix(size);
1479 asr.SetContext(aContext);
1480 aContext->Multiply(matrix);
1482 // Convert the region to unoriented coordinates.
1483 gfxMatrix inverseMatrix = OrientationMatrix(size, /* aInvert = */ true);
1484 region.TransformBoundsBy(inverseMatrix);
1487 drawResult = DrawInternal(std::move(result.Surface()), aContext, size,
1488 region, aSamplingFilter, flags, aOpacity);
1491 if (shouldRecordTelemetry) {
1492 TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
1493 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY,
1494 int32_t(drawLatency.ToMicroseconds()));
1495 mDrawStartTime = TimeStamp();
1498 return drawResult;
1501 //******************************************************************************
1503 NS_IMETHODIMP
1504 RasterImage::LockImage() {
1505 MOZ_ASSERT(NS_IsMainThread(),
1506 "Main thread to encourage serialization with UnlockImage");
1507 if (mError) {
1508 return NS_ERROR_FAILURE;
1511 // Increment the lock count
1512 mLockCount++;
1514 // Lock this image's surfaces in the SurfaceCache.
1515 if (mLockCount == 1) {
1516 SurfaceCache::LockImage(ImageKey(this));
1519 return NS_OK;
1522 //******************************************************************************
1524 NS_IMETHODIMP
1525 RasterImage::UnlockImage() {
1526 MOZ_ASSERT(NS_IsMainThread(),
1527 "Main thread to encourage serialization with LockImage");
1528 if (mError) {
1529 return NS_ERROR_FAILURE;
1532 // It's an error to call this function if the lock count is 0
1533 MOZ_ASSERT(mLockCount > 0, "Calling UnlockImage with mLockCount == 0!");
1534 if (mLockCount == 0) {
1535 return NS_ERROR_ABORT;
1538 // Decrement our lock count
1539 mLockCount--;
1541 // Unlock this image's surfaces in the SurfaceCache.
1542 if (mLockCount == 0) {
1543 SurfaceCache::UnlockImage(ImageKey(this));
1546 return NS_OK;
1549 //******************************************************************************
1551 NS_IMETHODIMP
1552 RasterImage::RequestDiscard() {
1553 if (LoadDiscardable() && // Enabled at creation time...
1554 mLockCount == 0 && // ...not temporarily disabled...
1555 CanDiscard()) {
1556 Discard();
1559 return NS_OK;
1562 // Idempotent error flagging routine. If a decoder is open, shuts it down.
1563 void RasterImage::DoError() {
1564 // If we've flagged an error before, we have nothing to do
1565 if (mError) {
1566 return;
1569 // We can't safely handle errors off-main-thread, so dispatch a worker to
1570 // do it.
1571 if (!NS_IsMainThread()) {
1572 HandleErrorWorker::DispatchIfNeeded(this);
1573 return;
1576 // Put the container in an error state.
1577 mError = true;
1579 // Stop animation and release our FrameAnimator.
1580 if (mAnimating) {
1581 StopAnimation();
1583 mAnimationState = Nothing();
1584 mFrameAnimator = nullptr;
1586 // Release all locks.
1587 mLockCount = 0;
1588 SurfaceCache::UnlockImage(ImageKey(this));
1590 // Release all frames from the surface cache.
1591 SurfaceCache::RemoveImage(ImageKey(this));
1593 // Invalidate to get rid of any partially-drawn image content.
1594 auto dirtyRect = UnorientedIntRect({0, 0}, ToUnoriented(mSize));
1595 NotifyProgress(NoProgress, dirtyRect);
1597 MOZ_LOG(gImgLog, LogLevel::Error,
1598 ("RasterImage: [this=%p] Error detected for image\n", this));
1601 /* static */
1602 void RasterImage::HandleErrorWorker::DispatchIfNeeded(RasterImage* aImage) {
1603 RefPtr<HandleErrorWorker> worker = new HandleErrorWorker(aImage);
1604 NS_DispatchToMainThread(worker);
1607 RasterImage::HandleErrorWorker::HandleErrorWorker(RasterImage* aImage)
1608 : Runnable("image::RasterImage::HandleErrorWorker"), mImage(aImage) {
1609 MOZ_ASSERT(mImage, "Should have image");
1612 NS_IMETHODIMP
1613 RasterImage::HandleErrorWorker::Run() {
1614 mImage->DoError();
1616 return NS_OK;
1619 bool RasterImage::ShouldAnimate() {
1620 return ImageResource::ShouldAnimate() && mAnimationState &&
1621 mAnimationState->KnownFrameCount() >= 1 && !LoadAnimationFinished();
1624 #ifdef DEBUG
1625 NS_IMETHODIMP
1626 RasterImage::GetFramesNotified(uint32_t* aFramesNotified) {
1627 NS_ENSURE_ARG_POINTER(aFramesNotified);
1629 *aFramesNotified = mFramesNotified;
1631 return NS_OK;
1633 #endif
1635 void RasterImage::NotifyProgress(
1636 Progress aProgress,
1637 const UnorientedIntRect& aInvalidRect /* = UnorientedIntRect() */,
1638 const Maybe<uint32_t>& aFrameCount /* = Nothing() */,
1639 DecoderFlags aDecoderFlags /* = DefaultDecoderFlags() */,
1640 SurfaceFlags aSurfaceFlags /* = DefaultSurfaceFlags() */) {
1641 MOZ_ASSERT(NS_IsMainThread());
1643 // Ensure that we stay alive long enough to finish notifying.
1644 RefPtr<RasterImage> image = this;
1646 UnorientedIntRect invalidRect = aInvalidRect;
1648 if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY)) {
1649 // We may have decoded new animation frames; update our animation state.
1650 MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError);
1651 if (mAnimationState && aFrameCount) {
1652 mAnimationState->UpdateKnownFrameCount(*aFrameCount);
1655 // If we should start animating right now, do so.
1656 if (mAnimationState && aFrameCount == Some(1u) && LoadPendingAnimation() &&
1657 ShouldAnimate()) {
1658 StartAnimation();
1661 if (mAnimationState) {
1662 auto size = ToUnoriented(mSize);
1663 IntRect rect = mAnimationState->UpdateState(this, size.ToUnknownSize());
1665 invalidRect.UnionRect(invalidRect,
1666 UnorientedIntRect::FromUnknownRect(rect));
1670 const bool wasDefaultFlags = aSurfaceFlags == DefaultSurfaceFlags();
1672 auto orientedInvalidRect = ToOriented(invalidRect);
1674 if (!orientedInvalidRect.IsEmpty() && wasDefaultFlags) {
1675 // Update our image container since we're invalidating.
1676 UpdateImageContainer(Some(orientedInvalidRect.ToUnknownRect()));
1679 // Tell the observers what happened.
1680 image->mProgressTracker->SyncNotifyProgress(
1681 aProgress, orientedInvalidRect.ToUnknownRect());
1684 void RasterImage::NotifyDecodeComplete(
1685 const DecoderFinalStatus& aStatus, const ImageMetadata& aMetadata,
1686 const DecoderTelemetry& aTelemetry, Progress aProgress,
1687 const UnorientedIntRect& aInvalidRect, const Maybe<uint32_t>& aFrameCount,
1688 DecoderFlags aDecoderFlags, SurfaceFlags aSurfaceFlags) {
1689 MOZ_ASSERT(NS_IsMainThread());
1691 // If the decoder detected an error, log it to the error console.
1692 if (aStatus.mShouldReportError) {
1693 ReportDecoderError();
1696 // Record all the metadata the decoder gathered about this image.
1697 bool metadataOK = SetMetadata(aMetadata, aStatus.mWasMetadataDecode);
1698 if (!metadataOK) {
1699 // This indicates a serious error that requires us to discard all existing
1700 // surfaces and redecode to recover. We'll drop the results from this
1701 // decoder on the floor, since they aren't valid.
1702 RecoverFromInvalidFrames(ToUnoriented(mSize),
1703 FromSurfaceFlags(aSurfaceFlags));
1704 return;
1707 MOZ_ASSERT(mError || LoadHasSize() || !aMetadata.HasSize(),
1708 "SetMetadata should've gotten a size");
1710 if (!aStatus.mWasMetadataDecode && aStatus.mFinished) {
1711 // Flag that we've been decoded before.
1712 StoreHasBeenDecoded(true);
1715 // Send out any final notifications.
1716 NotifyProgress(aProgress, aInvalidRect, aFrameCount, aDecoderFlags,
1717 aSurfaceFlags);
1719 if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY)) {
1720 // We may have decoded new animation frames; update our animation state.
1721 MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError);
1722 if (mAnimationState && aFrameCount) {
1723 mAnimationState->UpdateKnownFrameCount(*aFrameCount);
1726 // If we should start animating right now, do so.
1727 if (mAnimationState && aFrameCount == Some(1u) && LoadPendingAnimation() &&
1728 ShouldAnimate()) {
1729 StartAnimation();
1732 if (mAnimationState && LoadHasBeenDecoded()) {
1733 // We've finished a full decode of all animation frames and our
1734 // AnimationState has been notified about them all, so let it know not to
1735 // expect anymore.
1736 mAnimationState->NotifyDecodeComplete();
1738 auto size = ToUnoriented(mSize);
1739 IntRect rect = mAnimationState->UpdateState(this, size.ToUnknownSize());
1741 if (!rect.IsEmpty()) {
1742 auto dirtyRect = UnorientedIntRect::FromUnknownRect(rect);
1743 NotifyProgress(NoProgress, dirtyRect);
1748 // Do some telemetry if this isn't a metadata decode.
1749 if (!aStatus.mWasMetadataDecode) {
1750 if (aTelemetry.mChunkCount) {
1751 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS,
1752 aTelemetry.mChunkCount);
1755 if (aStatus.mFinished) {
1756 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
1757 int32_t(aTelemetry.mDecodeTime.ToMicroseconds()));
1759 if (aTelemetry.mSpeedHistogram && aTelemetry.mBytesDecoded) {
1760 Telemetry::Accumulate(*aTelemetry.mSpeedHistogram, aTelemetry.Speed());
1765 // Only act on errors if we have no usable frames from the decoder.
1766 if (aStatus.mHadError &&
1767 (!mAnimationState || mAnimationState->KnownFrameCount() == 0)) {
1768 DoError();
1769 } else if (aStatus.mWasMetadataDecode && !LoadHasSize()) {
1770 DoError();
1773 // XXX(aosmond): Can we get this far without mFinished == true?
1774 if (aStatus.mFinished && aStatus.mWasMetadataDecode) {
1775 // If we were waiting to fire the load event, go ahead and fire it now.
1776 if (mLoadProgress) {
1777 NotifyForLoadEvent(*mLoadProgress);
1778 mLoadProgress = Nothing();
1781 // If we were a metadata decode and a full decode was requested, do it.
1782 if (LoadWantFullDecode()) {
1783 StoreWantFullDecode(false);
1784 RequestDecodeForSize(mSize.ToUnknownSize(),
1785 DECODE_FLAGS_DEFAULT | FLAG_HIGH_QUALITY_SCALING,
1786 FRAME_CURRENT);
1791 void RasterImage::ReportDecoderError() {
1792 nsCOMPtr<nsIConsoleService> consoleService =
1793 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1794 nsCOMPtr<nsIScriptError> errorObject =
1795 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
1797 if (consoleService && errorObject) {
1798 nsAutoString msg(u"Image corrupt or truncated."_ns);
1799 nsAutoString src;
1800 if (GetURI()) {
1801 nsAutoCString uri;
1802 if (!GetSpecTruncatedTo1k(uri)) {
1803 msg += u" URI in this note truncated due to length."_ns;
1805 CopyUTF8toUTF16(uri, src);
1807 if (NS_SUCCEEDED(errorObject->InitWithWindowID(msg, src, u""_ns, 0, 0,
1808 nsIScriptError::errorFlag,
1809 "Image", InnerWindowID()))) {
1810 consoleService->LogMessage(errorObject);
1815 already_AddRefed<imgIContainer> RasterImage::Unwrap() {
1816 nsCOMPtr<imgIContainer> self(this);
1817 return self.forget();
1820 void RasterImage::PropagateUseCounters(dom::Document*) {
1821 // No use counters.
1824 IntSize RasterImage::OptimalImageSizeForDest(const gfxSize& aDest,
1825 uint32_t aWhichFrame,
1826 SamplingFilter aSamplingFilter,
1827 uint32_t aFlags) {
1828 MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
1829 aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
1830 "Unexpected destination size");
1832 if (mSize.IsEmpty() || aDest.IsEmpty()) {
1833 return IntSize(0, 0);
1836 auto dest = OrientedIntSize::FromUnknownSize(
1837 IntSize::Ceil(aDest.width, aDest.height));
1839 if (aSamplingFilter == SamplingFilter::GOOD &&
1840 CanDownscaleDuringDecode(ToUnoriented(dest), aFlags)) {
1841 return dest.ToUnknownSize();
1844 // We can't scale to this size. Use our intrinsic size for now.
1845 return mSize.ToUnknownSize();
1848 gfxMatrix RasterImage::OrientationMatrix(const UnorientedIntSize& aSize,
1849 bool aInvert) const {
1850 return OrientedImage::OrientationMatrix(mOrientation, aSize.ToUnknownSize(),
1851 aInvert);
1855 * Rotate aRect by the given angle within the space specified by aSize.
1857 * For example, with aRect = [20, 10, 5, 5] and aSize = [100, 100], rotating
1858 * with Angle::D90 will result in aRect = [85, 20, 5, 5].
1860 static void Rotate(IntRect& aRect, const IntSize& aSize, Angle aAngle) {
1861 switch (aAngle) {
1862 case Angle::D0:
1863 break;
1864 case Angle::D90:
1865 aRect = {aSize.height - aRect.YMost(), aRect.x, aRect.height,
1866 aRect.width};
1867 break;
1868 case Angle::D180:
1869 aRect.MoveTo(aSize.width - aRect.XMost(), aSize.height - aRect.YMost());
1870 break;
1871 case Angle::D270:
1872 aRect = {aRect.y, aSize.width - aRect.XMost(), aRect.height, aRect.width};
1873 break;
1878 * Flip aRect along the central axis within aSize.
1880 * For example, with aRect = [20, 10, 5, 5] and aSize = [100, 100], flipping
1881 * with Flip::Horizontal will result in aRect = [75, 10, 5, 5].
1883 static void Flip(IntRect& aRect, const IntSize& aSize, Flip aFlip) {
1884 switch (aFlip) {
1885 case Flip::Unflipped:
1886 break;
1887 case Flip::Horizontal:
1888 aRect.x = aSize.width - aRect.XMost();
1889 break;
1893 OrientedIntRect RasterImage::ToOriented(UnorientedIntRect aRect) const {
1894 IntRect rect = aRect.ToUnknownRect();
1895 auto size = ToUnoriented(mSize);
1897 MOZ_ASSERT(!mOrientation.flipFirst,
1898 "flipFirst should only be used by OrientedImage");
1900 // mOrientation specifies the transformation from a correctly oriented image
1901 // to the pixels stored in the file, so we need to rotate by the negation of
1902 // the given angle.
1903 Angle angle = Orientation::InvertAngle(mOrientation.rotation);
1904 Rotate(rect, size.ToUnknownSize(), angle);
1906 // Use mSize instead of size, since after the Rotate call, the size of the
1907 // space that rect is in has had its width and height swapped.
1908 Flip(rect, mSize.ToUnknownSize(), mOrientation.flip);
1910 return OrientedIntRect::FromUnknownRect(rect);
1913 UnorientedIntRect RasterImage::ToUnoriented(OrientedIntRect aRect) const {
1914 IntRect rect = aRect.ToUnknownRect();
1916 Flip(rect, mSize.ToUnknownSize(), mOrientation.flip);
1917 Rotate(rect, mSize.ToUnknownSize(), mOrientation.rotation);
1919 MOZ_ASSERT(!mOrientation.flipFirst,
1920 "flipFirst should only be used by OrientedImage");
1922 return UnorientedIntRect::FromUnknownRect(rect);
1925 } // namespace image
1926 } // namespace mozilla