Bug 1610357 [wpt PR 21278] - Update wpt metadata, a=testonly
[gecko.git] / image / RasterImage.cpp
blob2756b72ee081e26ca74c002c3af84c6270acba71
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 "SourceBuffer.h"
25 #include "SurfaceCache.h"
26 #include "gfx2DGlue.h"
27 #include "gfxContext.h"
28 #include "gfxPlatform.h"
29 #include "mozilla/ClearOnShutdown.h"
30 #include "mozilla/DebugOnly.h"
31 #include "mozilla/Likely.h"
32 #include "mozilla/MemoryReporting.h"
33 #include "mozilla/RefPtr.h"
34 #include "mozilla/SizeOfState.h"
35 #include "mozilla/StaticPrefs_image.h"
36 #include "mozilla/Telemetry.h"
37 #include "mozilla/TimeStamp.h"
38 #include "mozilla/Tuple.h"
39 #include "mozilla/gfx/2D.h"
40 #include "mozilla/gfx/Scale.h"
41 #include "nsComponentManagerUtils.h"
42 #include "nsError.h"
43 #include "nsIConsoleService.h"
44 #include "nsIInputStream.h"
45 #include "nsIScriptError.h"
46 #include "nsISupportsPrimitives.h"
47 #include "nsMemory.h"
48 #include "nsPresContext.h"
49 #include "nsProperties.h"
50 #include "prenv.h"
51 #include "prsystem.h"
53 namespace mozilla {
55 using namespace gfx;
56 using namespace layers;
58 namespace image {
60 using std::ceil;
61 using std::min;
63 #ifndef DEBUG
64 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties)
65 #else
66 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties, imgIContainerDebug)
67 #endif
69 //******************************************************************************
70 RasterImage::RasterImage(nsIURI* aURI /* = nullptr */)
71 : ImageResource(aURI), // invoke superclass's constructor
72 mSize(0, 0),
73 mLockCount(0),
74 mDecoderType(DecoderType::UNKNOWN),
75 mDecodeCount(0),
76 #ifdef DEBUG
77 mFramesNotified(0),
78 #endif
79 mSourceBuffer(MakeNotNull<SourceBuffer*>()),
80 mHasSize(false),
81 mTransient(false),
82 mSyncLoad(false),
83 mDiscardable(false),
84 mSomeSourceData(false),
85 mAllSourceData(false),
86 mHasBeenDecoded(false),
87 mPendingAnimation(false),
88 mAnimationFinished(false),
89 mWantFullDecode(false) {
92 //******************************************************************************
93 RasterImage::~RasterImage() {
94 // Make sure our SourceBuffer is marked as complete. This will ensure that any
95 // outstanding decoders terminate.
96 if (!mSourceBuffer->IsComplete()) {
97 mSourceBuffer->Complete(NS_ERROR_ABORT);
100 // Release all frames from the surface cache.
101 SurfaceCache::RemoveImage(ImageKey(this));
103 // Record Telemetry.
104 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_COUNT, mDecodeCount);
107 nsresult RasterImage::Init(const char* aMimeType, uint32_t aFlags) {
108 // We don't support re-initialization
109 if (mInitialized) {
110 return NS_ERROR_ILLEGAL_VALUE;
113 // Not sure an error can happen before init, but be safe
114 if (mError) {
115 return NS_ERROR_FAILURE;
118 // We want to avoid redecodes for transient images.
119 MOZ_ASSERT_IF(aFlags & INIT_FLAG_TRANSIENT,
120 !(aFlags & INIT_FLAG_DISCARDABLE));
122 // Store initialization data
123 mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
124 mWantFullDecode = !!(aFlags & INIT_FLAG_DECODE_IMMEDIATELY);
125 mTransient = !!(aFlags & INIT_FLAG_TRANSIENT);
126 mSyncLoad = !!(aFlags & INIT_FLAG_SYNC_LOAD);
128 // Use the MIME type to select a decoder type, and make sure there *is* a
129 // decoder for this MIME type.
130 NS_ENSURE_ARG_POINTER(aMimeType);
131 mDecoderType = DecoderFactory::GetDecoderType(aMimeType);
132 if (mDecoderType == DecoderType::UNKNOWN) {
133 return NS_ERROR_FAILURE;
136 // Lock this image's surfaces in the SurfaceCache if we're not discardable.
137 if (!mDiscardable) {
138 mLockCount++;
139 SurfaceCache::LockImage(ImageKey(this));
142 // Mark us as initialized
143 mInitialized = true;
145 return NS_OK;
148 //******************************************************************************
149 NS_IMETHODIMP_(void)
150 RasterImage::RequestRefresh(const TimeStamp& aTime) {
151 if (HadRecentRefresh(aTime)) {
152 return;
155 EvaluateAnimation();
157 if (!mAnimating) {
158 return;
161 RefreshResult res;
162 if (mAnimationState) {
163 MOZ_ASSERT(mFrameAnimator);
164 res = mFrameAnimator->RequestRefresh(*mAnimationState, aTime,
165 mAnimationFinished);
168 if (res.mFrameAdvanced) {
169 // Notify listeners that our frame has actually changed, but do this only
170 // once for all frames that we've now passed (if AdvanceFrame() was called
171 // more than once).
172 #ifdef DEBUG
173 mFramesNotified++;
174 #endif
176 NotifyProgress(NoProgress, res.mDirtyRect);
179 if (res.mAnimationFinished) {
180 mAnimationFinished = true;
181 EvaluateAnimation();
185 //******************************************************************************
186 NS_IMETHODIMP
187 RasterImage::GetWidth(int32_t* aWidth) {
188 NS_ENSURE_ARG_POINTER(aWidth);
190 if (mError) {
191 *aWidth = 0;
192 return NS_ERROR_FAILURE;
195 *aWidth = mSize.width;
196 return NS_OK;
199 //******************************************************************************
200 NS_IMETHODIMP
201 RasterImage::GetHeight(int32_t* aHeight) {
202 NS_ENSURE_ARG_POINTER(aHeight);
204 if (mError) {
205 *aHeight = 0;
206 return NS_ERROR_FAILURE;
209 *aHeight = mSize.height;
210 return NS_OK;
213 //******************************************************************************
214 nsresult RasterImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const {
215 if (mError) {
216 return NS_ERROR_FAILURE;
219 if (mNativeSizes.IsEmpty()) {
220 aNativeSizes.Clear();
221 aNativeSizes.AppendElement(mSize);
222 } else {
223 aNativeSizes = mNativeSizes;
226 return NS_OK;
229 //******************************************************************************
230 size_t RasterImage::GetNativeSizesLength() const {
231 if (mError || !mHasSize) {
232 return 0;
235 if (mNativeSizes.IsEmpty()) {
236 return 1;
239 return mNativeSizes.Length();
242 //******************************************************************************
243 NS_IMETHODIMP
244 RasterImage::GetIntrinsicSize(nsSize* aSize) {
245 if (mError) {
246 return NS_ERROR_FAILURE;
249 *aSize = nsSize(nsPresContext::CSSPixelsToAppUnits(mSize.width),
250 nsPresContext::CSSPixelsToAppUnits(mSize.height));
251 return NS_OK;
254 //******************************************************************************
255 Maybe<AspectRatio> RasterImage::GetIntrinsicRatio() {
256 if (mError) {
257 return Nothing();
260 return Some(AspectRatio::FromSize(mSize.width, mSize.height));
263 NS_IMETHODIMP_(Orientation)
264 RasterImage::GetOrientation() { return mOrientation; }
266 //******************************************************************************
267 NS_IMETHODIMP
268 RasterImage::GetType(uint16_t* aType) {
269 NS_ENSURE_ARG_POINTER(aType);
271 *aType = imgIContainer::TYPE_RASTER;
272 return NS_OK;
275 NS_IMETHODIMP
276 RasterImage::GetProducerId(uint32_t* aId) {
277 NS_ENSURE_ARG_POINTER(aId);
279 *aId = ImageResource::GetImageProducerId();
280 return NS_OK;
283 LookupResult RasterImage::LookupFrameInternal(const IntSize& aSize,
284 uint32_t aFlags,
285 PlaybackType aPlaybackType,
286 bool aMarkUsed) {
287 if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
288 MOZ_ASSERT(mFrameAnimator);
289 MOZ_ASSERT(ToSurfaceFlags(aFlags) == DefaultSurfaceFlags(),
290 "Can't composite frames with non-default surface flags");
291 return mFrameAnimator->GetCompositedFrame(*mAnimationState, aMarkUsed);
294 SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
296 // We don't want any substitution for sync decodes, and substitution would be
297 // illegal when high quality downscaling is disabled, so we use
298 // SurfaceCache::Lookup in this case.
299 if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
300 return SurfaceCache::Lookup(
301 ImageKey(this),
302 RasterSurfaceKey(aSize, surfaceFlags, PlaybackType::eStatic),
303 aMarkUsed);
306 // We'll return the best match we can find to the requested frame.
307 return SurfaceCache::LookupBestMatch(
308 ImageKey(this),
309 RasterSurfaceKey(aSize, surfaceFlags, PlaybackType::eStatic), aMarkUsed);
312 LookupResult RasterImage::LookupFrame(const IntSize& aSize, 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 IntSize requestedSize =
324 CanDownscaleDuringDecode(aSize, aFlags) ? aSize : 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 && !mHasSize) {
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 = result.SuggestedSize();
359 bool ranSync = Decode(requestedSize, aFlags, aPlaybackType);
361 // If we can or did sync decode, we should already have the frame.
362 if (ranSync || syncDecode) {
363 result =
364 LookupFrameInternal(requestedSize, aFlags, aPlaybackType, aMarkUsed);
368 if (!result) {
369 // We still weren't able to get a frame. Give up.
370 return result;
373 // Sync decoding guarantees that we got the frame, but if it's owned by an
374 // async decoder that's currently running, the contents of the frame may not
375 // be available yet. Make sure we get everything.
376 if (mAllSourceData && syncDecode) {
377 result.Surface()->WaitUntilFinished();
380 // If we could have done some decoding in this function we need to check if
381 // that decoding encountered an error and hence aborted the surface. We want
382 // to avoid calling IsAborted if we weren't passed any sync decode flag
383 // because IsAborted acquires the monitor for the imgFrame.
384 if (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST) &&
385 result.Surface()->IsAborted()) {
386 DrawableSurface tmp = std::move(result.Surface());
387 return result;
390 return result;
393 bool RasterImage::IsOpaque() {
394 if (mError) {
395 return false;
398 Progress progress = mProgressTracker->GetProgress();
400 // If we haven't yet finished decoding, the safe answer is "not opaque".
401 if (!(progress & FLAG_DECODE_COMPLETE)) {
402 return false;
405 // Other, we're opaque if FLAG_HAS_TRANSPARENCY is not set.
406 return !(progress & FLAG_HAS_TRANSPARENCY);
409 NS_IMETHODIMP_(bool)
410 RasterImage::WillDrawOpaqueNow() {
411 if (!IsOpaque()) {
412 return false;
415 if (mAnimationState) {
416 if (!StaticPrefs::image_mem_animated_discardable_AtStartup()) {
417 // We never discard frames of animated images.
418 return true;
419 } else {
420 if (mAnimationState->GetCompositedFrameInvalid()) {
421 // We're not going to draw anything at all.
422 return false;
427 // If we are not locked our decoded data could get discard at any time (ie
428 // between the call to this function and when we are asked to draw), so we
429 // have to return false if we are unlocked.
430 if (mLockCount == 0) {
431 return false;
434 LookupResult result = SurfaceCache::LookupBestMatch(
435 ImageKey(this),
436 RasterSurfaceKey(mSize, DefaultSurfaceFlags(), PlaybackType::eStatic),
437 /* aMarkUsed = */ false);
438 MatchType matchType = result.Type();
439 if (matchType == MatchType::NOT_FOUND || matchType == MatchType::PENDING ||
440 !result.Surface()->IsFinished()) {
441 return false;
444 return true;
447 void RasterImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) {
448 MOZ_ASSERT(mProgressTracker);
450 bool animatedFramesDiscarded =
451 mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated;
453 nsCOMPtr<nsIEventTarget> eventTarget;
454 if (mProgressTracker) {
455 eventTarget = mProgressTracker->GetEventTarget();
456 } else {
457 eventTarget = do_GetMainThread();
460 RefPtr<RasterImage> image = this;
461 nsCOMPtr<nsIRunnable> ev =
462 NS_NewRunnableFunction("RasterImage::OnSurfaceDiscarded", [=]() -> void {
463 image->OnSurfaceDiscardedInternal(animatedFramesDiscarded);
465 eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
468 void RasterImage::OnSurfaceDiscardedInternal(bool aAnimatedFramesDiscarded) {
469 MOZ_ASSERT(NS_IsMainThread());
471 if (aAnimatedFramesDiscarded && mAnimationState) {
472 MOZ_ASSERT(StaticPrefs::image_mem_animated_discardable_AtStartup());
473 ReleaseImageContainer();
474 gfx::IntRect rect =
475 mAnimationState->UpdateState(mAnimationFinished, this, mSize);
476 NotifyProgress(NoProgress, rect);
479 if (mProgressTracker) {
480 mProgressTracker->OnDiscard();
484 //******************************************************************************
485 NS_IMETHODIMP
486 RasterImage::GetAnimated(bool* aAnimated) {
487 if (mError) {
488 return NS_ERROR_FAILURE;
491 NS_ENSURE_ARG_POINTER(aAnimated);
493 // If we have an AnimationState, we can know for sure.
494 if (mAnimationState) {
495 *aAnimated = true;
496 return NS_OK;
499 // Otherwise, we need to have been decoded to know for sure, since if we were
500 // decoded at least once mAnimationState would have been created for animated
501 // images. This is true even though we check for animation during the
502 // metadata decode, because we may still discover animation only during the
503 // full decode for corrupt images.
504 if (!mHasBeenDecoded) {
505 return NS_ERROR_NOT_AVAILABLE;
508 // We know for sure
509 *aAnimated = false;
511 return NS_OK;
514 //******************************************************************************
515 NS_IMETHODIMP_(int32_t)
516 RasterImage::GetFirstFrameDelay() {
517 if (mError) {
518 return -1;
521 bool animated = false;
522 if (NS_FAILED(GetAnimated(&animated)) || !animated) {
523 return -1;
526 MOZ_ASSERT(mAnimationState, "Animated images should have an AnimationState");
527 return mAnimationState->FirstFrameTimeout().AsEncodedValueDeprecated();
530 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
531 RasterImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) {
532 return GetFrameAtSize(mSize, aWhichFrame, aFlags);
535 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
536 RasterImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame,
537 uint32_t aFlags) {
538 #ifdef DEBUG
539 NotifyDrawingObservers();
540 #endif
542 auto result = GetFrameInternal(aSize, Nothing(), aWhichFrame, aFlags);
543 return mozilla::Get<2>(result).forget();
546 Tuple<ImgDrawResult, IntSize, RefPtr<SourceSurface>>
547 RasterImage::GetFrameInternal(const IntSize& aSize,
548 const Maybe<SVGImageContext>& aSVGContext,
549 uint32_t aWhichFrame, uint32_t aFlags) {
550 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
552 if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE) {
553 return MakeTuple(ImgDrawResult::BAD_ARGS, aSize, RefPtr<SourceSurface>());
556 if (mError) {
557 return MakeTuple(ImgDrawResult::BAD_IMAGE, aSize, RefPtr<SourceSurface>());
560 // Get the frame. If it's not there, it's probably the caller's fault for
561 // not waiting for the data to be loaded from the network or not passing
562 // FLAG_SYNC_DECODE.
563 LookupResult result = LookupFrame(aSize, aFlags, ToPlaybackType(aWhichFrame),
564 /* aMarkUsed = */ true);
566 // The surface cache may have suggested we use a different size than the
567 // given size in the future. This may or may not be accompanied by an
568 // actual surface, depending on what it has in its cache.
569 IntSize suggestedSize =
570 result.SuggestedSize().IsEmpty() ? aSize : result.SuggestedSize();
571 MOZ_ASSERT_IF(result.Type() == MatchType::SUBSTITUTE_BECAUSE_BEST,
572 suggestedSize != aSize);
574 if (!result) {
575 // The OS threw this frame away and we couldn't redecode it.
576 return MakeTuple(ImgDrawResult::TEMPORARY_ERROR, suggestedSize,
577 RefPtr<SourceSurface>());
580 RefPtr<SourceSurface> surface = result.Surface()->GetSourceSurface();
581 if (!result.Surface()->IsFinished()) {
582 return MakeTuple(ImgDrawResult::INCOMPLETE, suggestedSize,
583 std::move(surface));
586 return MakeTuple(ImgDrawResult::SUCCESS, suggestedSize, std::move(surface));
589 Tuple<ImgDrawResult, IntSize> RasterImage::GetImageContainerSize(
590 LayerManager* aManager, const IntSize& aSize, uint32_t aFlags) {
591 if (!mHasSize) {
592 return MakeTuple(ImgDrawResult::NOT_READY, IntSize(0, 0));
595 if (aSize.IsEmpty()) {
596 return MakeTuple(ImgDrawResult::BAD_ARGS, IntSize(0, 0));
599 // We check the minimum size because while we support downscaling, we do not
600 // support upscaling. If aSize > mSize, we will never give a larger surface
601 // than mSize. If mSize > aSize, and mSize > maxTextureSize, we still want to
602 // use image containers if aSize <= maxTextureSize.
603 int32_t maxTextureSize = aManager->GetMaxTextureSize();
604 if (min(mSize.width, aSize.width) > maxTextureSize ||
605 min(mSize.height, aSize.height) > maxTextureSize) {
606 return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0));
609 if (!CanDownscaleDuringDecode(aSize, aFlags)) {
610 return MakeTuple(ImgDrawResult::SUCCESS, mSize);
613 return MakeTuple(ImgDrawResult::SUCCESS, aSize);
616 NS_IMETHODIMP_(bool)
617 RasterImage::IsImageContainerAvailable(LayerManager* aManager,
618 uint32_t aFlags) {
619 return IsImageContainerAvailableAtSize(aManager, mSize, aFlags);
622 NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
623 RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags) {
624 RefPtr<ImageContainer> container;
625 ImgDrawResult drawResult = GetImageContainerImpl(
626 aManager, mSize, Nothing(), aFlags, getter_AddRefs(container));
628 // We silence the unused warning here because anything that needs the draw
629 // result should be using GetImageContainerAtSize, not GetImageContainer.
630 (void)drawResult;
631 return container.forget();
634 NS_IMETHODIMP_(bool)
635 RasterImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
636 const IntSize& aSize,
637 uint32_t aFlags) {
638 // We check the minimum size because while we support downscaling, we do not
639 // support upscaling. If aSize > mSize, we will never give a larger surface
640 // than mSize. If mSize > aSize, and mSize > maxTextureSize, we still want to
641 // use image containers if aSize <= maxTextureSize.
642 int32_t maxTextureSize = aManager->GetMaxTextureSize();
643 if (!mHasSize || aSize.IsEmpty() ||
644 min(mSize.width, aSize.width) > maxTextureSize ||
645 min(mSize.height, aSize.height) > maxTextureSize) {
646 return false;
649 return true;
652 NS_IMETHODIMP_(ImgDrawResult)
653 RasterImage::GetImageContainerAtSize(layers::LayerManager* aManager,
654 const gfx::IntSize& aSize,
655 const Maybe<SVGImageContext>& aSVGContext,
656 uint32_t aFlags,
657 layers::ImageContainer** aOutContainer) {
658 // We do not pass in the given SVG context because in theory it could differ
659 // between calls, but actually have no impact on the actual contents of the
660 // image container.
661 return GetImageContainerImpl(aManager, aSize, Nothing(), aFlags,
662 aOutContainer);
665 size_t RasterImage::SizeOfSourceWithComputedFallback(
666 SizeOfState& aState) const {
667 return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(
668 aState.mMallocSizeOf);
671 void RasterImage::CollectSizeOfSurfaces(
672 nsTArray<SurfaceMemoryCounter>& aCounters,
673 MallocSizeOf aMallocSizeOf) const {
674 SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
677 bool RasterImage::SetMetadata(const ImageMetadata& aMetadata,
678 bool aFromMetadataDecode) {
679 MOZ_ASSERT(NS_IsMainThread());
681 if (mError) {
682 return true;
685 if (aMetadata.HasSize()) {
686 IntSize size = aMetadata.GetSize();
687 if (size.width < 0 || size.height < 0) {
688 NS_WARNING("Image has negative intrinsic size");
689 DoError();
690 return true;
693 MOZ_ASSERT(aMetadata.HasOrientation());
694 Orientation orientation = aMetadata.GetOrientation();
696 // If we already have a size, check the new size against the old one.
697 if (mHasSize && (size != mSize || orientation != mOrientation)) {
698 NS_WARNING(
699 "Image changed size or orientation on redecode! "
700 "This should not happen!");
701 DoError();
702 return true;
705 // Set the size and flag that we have it.
706 mSize = size;
707 mOrientation = orientation;
708 mNativeSizes = aMetadata.GetNativeSizes();
709 mHasSize = true;
712 if (mHasSize && aMetadata.HasAnimation() && !mAnimationState) {
713 // We're becoming animated, so initialize animation stuff.
714 mAnimationState.emplace(mAnimationMode);
715 mFrameAnimator = MakeUnique<FrameAnimator>(this, mSize);
717 if (!StaticPrefs::image_mem_animated_discardable_AtStartup()) {
718 // We don't support discarding animated images (See bug 414259).
719 // Lock the image and throw away the key.
720 LockImage();
723 if (!aFromMetadataDecode) {
724 // The metadata decode reported that this image isn't animated, but we
725 // discovered that it actually was during the full decode. This is a
726 // rare failure that only occurs for corrupt images. To recover, we need
727 // to discard all existing surfaces and redecode.
728 return false;
732 if (mAnimationState) {
733 mAnimationState->SetLoopCount(aMetadata.GetLoopCount());
734 mAnimationState->SetFirstFrameTimeout(aMetadata.GetFirstFrameTimeout());
736 if (aMetadata.HasLoopLength()) {
737 mAnimationState->SetLoopLength(aMetadata.GetLoopLength());
739 if (aMetadata.HasFirstFrameRefreshArea()) {
740 mAnimationState->SetFirstFrameRefreshArea(
741 aMetadata.GetFirstFrameRefreshArea());
745 if (aMetadata.HasHotspot()) {
746 IntPoint hotspot = aMetadata.GetHotspot();
748 nsCOMPtr<nsISupportsPRUint32> intwrapx =
749 do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
750 nsCOMPtr<nsISupportsPRUint32> intwrapy =
751 do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
752 intwrapx->SetData(hotspot.x);
753 intwrapy->SetData(hotspot.y);
755 Set("hotspotX", intwrapx);
756 Set("hotspotY", intwrapy);
759 return true;
762 NS_IMETHODIMP
763 RasterImage::SetAnimationMode(uint16_t aAnimationMode) {
764 if (mAnimationState) {
765 mAnimationState->SetAnimationMode(aAnimationMode);
767 return SetAnimationModeInternal(aAnimationMode);
770 //******************************************************************************
772 nsresult RasterImage::StartAnimation() {
773 if (mError) {
774 return NS_ERROR_FAILURE;
777 MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
779 // If we're not ready to animate, then set mPendingAnimation, which will cause
780 // us to start animating if and when we do become ready.
781 mPendingAnimation =
782 !mAnimationState || mAnimationState->KnownFrameCount() < 1;
783 if (mPendingAnimation) {
784 return NS_OK;
787 // Don't bother to animate if we're displaying the first frame forever.
788 if (mAnimationState->GetCurrentAnimationFrameIndex() == 0 &&
789 mAnimationState->FirstFrameTimeout() == FrameTimeout::Forever()) {
790 mAnimationFinished = true;
791 return NS_ERROR_ABORT;
794 // We need to set the time that this initial frame was first displayed, as
795 // this is used in AdvanceFrame().
796 mAnimationState->InitAnimationFrameTimeIfNecessary();
798 return NS_OK;
801 //******************************************************************************
802 nsresult RasterImage::StopAnimation() {
803 MOZ_ASSERT(mAnimating, "Should be animating!");
805 nsresult rv = NS_OK;
806 if (mError) {
807 rv = NS_ERROR_FAILURE;
808 } else {
809 mAnimationState->SetAnimationFrameTime(TimeStamp());
812 mAnimating = false;
813 return rv;
816 //******************************************************************************
817 NS_IMETHODIMP
818 RasterImage::ResetAnimation() {
819 if (mError) {
820 return NS_ERROR_FAILURE;
823 mPendingAnimation = false;
825 if (mAnimationMode == kDontAnimMode || !mAnimationState ||
826 mAnimationState->GetCurrentAnimationFrameIndex() == 0) {
827 return NS_OK;
830 mAnimationFinished = false;
832 if (mAnimating) {
833 StopAnimation();
836 MOZ_ASSERT(mAnimationState, "Should have AnimationState");
837 MOZ_ASSERT(mFrameAnimator, "Should have FrameAnimator");
838 mFrameAnimator->ResetAnimation(*mAnimationState);
840 NotifyProgress(NoProgress, mAnimationState->FirstFrameRefreshArea());
842 // Start the animation again. It may not have been running before, if
843 // mAnimationFinished was true before entering this function.
844 EvaluateAnimation();
846 return NS_OK;
849 //******************************************************************************
850 NS_IMETHODIMP_(void)
851 RasterImage::SetAnimationStartTime(const TimeStamp& aTime) {
852 if (mError || mAnimationMode == kDontAnimMode || mAnimating ||
853 !mAnimationState) {
854 return;
857 mAnimationState->SetAnimationFrameTime(aTime);
860 NS_IMETHODIMP_(float)
861 RasterImage::GetFrameIndex(uint32_t aWhichFrame) {
862 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
863 return (aWhichFrame == FRAME_FIRST || !mAnimationState)
864 ? 0.0f
865 : mAnimationState->GetCurrentAnimationFrameIndex();
868 NS_IMETHODIMP_(IntRect)
869 RasterImage::GetImageSpaceInvalidationRect(const IntRect& aRect) {
870 return aRect;
873 nsresult RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*,
874 nsresult aStatus, bool aLastPart) {
875 MOZ_ASSERT(NS_IsMainThread());
877 // Record that we have all the data we're going to get now.
878 mAllSourceData = true;
880 // Let decoders know that there won't be any more data coming.
881 mSourceBuffer->Complete(aStatus);
883 // Allow a synchronous metadata decode if mSyncLoad was set, or if we're
884 // running on a single thread (in which case waiting for the async metadata
885 // decoder could delay this image's load event quite a bit), or if this image
886 // is transient.
887 bool canSyncDecodeMetadata =
888 mSyncLoad || mTransient || DecodePool::NumberOfCores() < 2;
890 if (canSyncDecodeMetadata && !mHasSize) {
891 // We're loading this image synchronously, so it needs to be usable after
892 // this call returns. Since we haven't gotten our size yet, we need to do a
893 // synchronous metadata decode here.
894 DecodeMetadata(FLAG_SYNC_DECODE);
897 // Determine our final status, giving precedence to Necko failure codes. We
898 // check after running the metadata decode in case it triggered an error.
899 nsresult finalStatus = mError ? NS_ERROR_FAILURE : NS_OK;
900 if (NS_FAILED(aStatus)) {
901 finalStatus = aStatus;
904 // If loading failed, report an error.
905 if (NS_FAILED(finalStatus)) {
906 DoError();
909 Progress loadProgress = LoadCompleteProgress(aLastPart, mError, finalStatus);
911 if (!mHasSize && !mError) {
912 // We don't have our size yet, so we'll fire the load event in SetSize().
913 MOZ_ASSERT(!canSyncDecodeMetadata,
914 "Firing load async after metadata sync decode?");
915 mLoadProgress = Some(loadProgress);
916 return finalStatus;
919 NotifyForLoadEvent(loadProgress);
921 return finalStatus;
924 void RasterImage::NotifyForLoadEvent(Progress aProgress) {
925 MOZ_ASSERT(mHasSize || mError, "Need to know size before firing load event");
926 MOZ_ASSERT(
927 !mHasSize || (mProgressTracker->GetProgress() & FLAG_SIZE_AVAILABLE),
928 "Should have notified that the size is available if we have it");
930 // If we encountered an error, make sure we notify for that as well.
931 if (mError) {
932 aProgress |= FLAG_HAS_ERROR;
935 // Notify our listeners, which will fire this image's load event.
936 NotifyProgress(aProgress);
939 nsresult RasterImage::OnImageDataAvailable(nsIRequest*, nsISupports*,
940 nsIInputStream* aInputStream,
941 uint64_t, uint32_t aCount) {
942 nsresult rv = mSourceBuffer->AppendFromInputStream(aInputStream, aCount);
943 if (NS_SUCCEEDED(rv) && !mSomeSourceData) {
944 mSomeSourceData = true;
945 if (!mSyncLoad) {
946 // Create an async metadata decoder and verify we succeed in doing so.
947 rv = DecodeMetadata(DECODE_FLAGS_DEFAULT);
951 if (NS_FAILED(rv)) {
952 DoError();
954 return rv;
957 nsresult RasterImage::SetSourceSizeHint(uint32_t aSizeHint) {
958 if (aSizeHint == 0) {
959 return NS_OK;
962 nsresult rv = mSourceBuffer->ExpectLength(aSizeHint);
963 if (rv == NS_ERROR_OUT_OF_MEMORY) {
964 // Flush memory, try to get some back, and try again.
965 rv = nsMemory::HeapMinimize(true);
966 if (NS_SUCCEEDED(rv)) {
967 rv = mSourceBuffer->ExpectLength(aSizeHint);
971 return rv;
974 /********* Methods to implement lazy allocation of nsIProperties object *******/
975 NS_IMETHODIMP
976 RasterImage::Get(const char* prop, const nsIID& iid, void** result) {
977 if (!mProperties) {
978 return NS_ERROR_FAILURE;
980 return mProperties->Get(prop, iid, result);
983 NS_IMETHODIMP
984 RasterImage::Set(const char* prop, nsISupports* value) {
985 if (!mProperties) {
986 mProperties = new nsProperties();
988 return mProperties->Set(prop, value);
991 NS_IMETHODIMP
992 RasterImage::Has(const char* prop, bool* _retval) {
993 NS_ENSURE_ARG_POINTER(_retval);
994 if (!mProperties) {
995 *_retval = false;
996 return NS_OK;
998 return mProperties->Has(prop, _retval);
1001 NS_IMETHODIMP
1002 RasterImage::Undefine(const char* prop) {
1003 if (!mProperties) {
1004 return NS_ERROR_FAILURE;
1006 return mProperties->Undefine(prop);
1009 NS_IMETHODIMP
1010 RasterImage::GetKeys(nsTArray<nsCString>& keys) {
1011 if (!mProperties) {
1012 keys.Clear();
1013 return NS_OK;
1015 return mProperties->GetKeys(keys);
1018 void RasterImage::Discard() {
1019 MOZ_ASSERT(NS_IsMainThread());
1020 MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
1021 MOZ_ASSERT(!mAnimationState ||
1022 StaticPrefs::image_mem_animated_discardable_AtStartup(),
1023 "Asked to discard for animated image");
1025 // Delete all the decoded frames.
1026 SurfaceCache::RemoveImage(ImageKey(this));
1028 if (mAnimationState) {
1029 ReleaseImageContainer();
1030 gfx::IntRect rect =
1031 mAnimationState->UpdateState(mAnimationFinished, this, mSize);
1032 NotifyProgress(NoProgress, rect);
1035 // Notify that we discarded.
1036 if (mProgressTracker) {
1037 mProgressTracker->OnDiscard();
1041 bool RasterImage::CanDiscard() {
1042 return mAllSourceData &&
1043 // Can discard animated images if the pref is set
1044 (!mAnimationState ||
1045 StaticPrefs::image_mem_animated_discardable_AtStartup());
1048 NS_IMETHODIMP
1049 RasterImage::StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) {
1050 if (mError) {
1051 return NS_ERROR_FAILURE;
1054 if (!mHasSize) {
1055 mWantFullDecode = true;
1056 return NS_OK;
1059 uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
1060 FLAG_HIGH_QUALITY_SCALING;
1061 return RequestDecodeForSize(mSize, flags, aWhichFrame);
1064 bool RasterImage::StartDecodingWithResult(uint32_t aFlags,
1065 uint32_t aWhichFrame) {
1066 if (mError) {
1067 return false;
1070 if (!mHasSize) {
1071 mWantFullDecode = true;
1072 return false;
1075 uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
1076 FLAG_HIGH_QUALITY_SCALING;
1077 DrawableSurface surface =
1078 RequestDecodeForSizeInternal(mSize, flags, aWhichFrame);
1079 return surface && surface->IsFinished();
1082 bool RasterImage::RequestDecodeWithResult(uint32_t aFlags,
1083 uint32_t aWhichFrame) {
1084 MOZ_ASSERT(NS_IsMainThread());
1086 if (mError) {
1087 return false;
1090 uint32_t flags = aFlags | FLAG_ASYNC_NOTIFY;
1091 DrawableSurface surface =
1092 RequestDecodeForSizeInternal(mSize, flags, aWhichFrame);
1093 return surface && surface->IsFinished();
1096 NS_IMETHODIMP
1097 RasterImage::RequestDecodeForSize(const IntSize& aSize, uint32_t aFlags,
1098 uint32_t aWhichFrame) {
1099 MOZ_ASSERT(NS_IsMainThread());
1101 if (mError) {
1102 return NS_ERROR_FAILURE;
1105 RequestDecodeForSizeInternal(aSize, aFlags, aWhichFrame);
1107 return NS_OK;
1110 DrawableSurface RasterImage::RequestDecodeForSizeInternal(
1111 const IntSize& aSize, uint32_t aFlags, uint32_t aWhichFrame) {
1112 MOZ_ASSERT(NS_IsMainThread());
1114 if (aWhichFrame > FRAME_MAX_VALUE) {
1115 return DrawableSurface();
1118 if (mError) {
1119 return DrawableSurface();
1122 if (!mHasSize) {
1123 mWantFullDecode = true;
1124 return DrawableSurface();
1127 // Decide whether to sync decode images we can decode quickly. Here we are
1128 // explicitly trading off flashing for responsiveness in the case that we're
1129 // redecoding an image (see bug 845147).
1130 bool shouldSyncDecodeIfFast =
1131 !mHasBeenDecoded && (aFlags & FLAG_SYNC_DECODE_IF_FAST);
1133 uint32_t flags =
1134 shouldSyncDecodeIfFast ? aFlags : aFlags & ~FLAG_SYNC_DECODE_IF_FAST;
1136 // Perform a frame lookup, which will implicitly start decoding if needed.
1137 LookupResult result = LookupFrame(aSize, flags, ToPlaybackType(aWhichFrame),
1138 /* aMarkUsed = */ false);
1139 return std::move(result.Surface());
1142 static bool LaunchDecodingTask(IDecodingTask* aTask, RasterImage* aImage,
1143 uint32_t aFlags, bool aHaveSourceData) {
1144 if (aHaveSourceData) {
1145 nsCString uri(aImage->GetURIString());
1147 // If we have all the data, we can sync decode if requested.
1148 if (aFlags & imgIContainer::FLAG_SYNC_DECODE) {
1149 DecodePool::Singleton()->SyncRunIfPossible(aTask, uri);
1150 return true;
1153 if (aFlags & imgIContainer::FLAG_SYNC_DECODE_IF_FAST) {
1154 return DecodePool::Singleton()->SyncRunIfPreferred(aTask, uri);
1158 // Perform an async decode. We also take this path if we don't have all the
1159 // source data yet, since sync decoding is impossible in that situation.
1160 DecodePool::Singleton()->AsyncRun(aTask);
1161 return false;
1164 bool RasterImage::Decode(const IntSize& aSize, uint32_t aFlags,
1165 PlaybackType aPlaybackType) {
1166 MOZ_ASSERT(NS_IsMainThread());
1168 if (mError) {
1169 return false;
1172 // If we don't have a size yet, we can't do any other decoding.
1173 if (!mHasSize) {
1174 mWantFullDecode = true;
1175 return false;
1178 // We're about to decode again, which may mean that some of the previous sizes
1179 // we've decoded at aren't useful anymore. We can allow them to expire from
1180 // the cache by unlocking them here. When the decode finishes, it will send an
1181 // invalidation that will cause all instances of this image to redraw. If this
1182 // image is locked, any surfaces that are still useful will become locked
1183 // again when LookupFrame touches them, and the remainder will eventually
1184 // expire.
1185 SurfaceCache::UnlockEntries(ImageKey(this));
1187 // Determine which flags we need to decode this image with.
1188 DecoderFlags decoderFlags = DefaultDecoderFlags();
1189 if (aFlags & FLAG_ASYNC_NOTIFY) {
1190 decoderFlags |= DecoderFlags::ASYNC_NOTIFY;
1192 if (mTransient) {
1193 decoderFlags |= DecoderFlags::IMAGE_IS_TRANSIENT;
1195 if (mHasBeenDecoded) {
1196 decoderFlags |= DecoderFlags::IS_REDECODE;
1198 if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
1199 // Used SurfaceCache::Lookup instead of SurfaceCache::LookupBestMatch. That
1200 // means the caller can handle a differently sized surface to be returned
1201 // at any point.
1202 decoderFlags |= DecoderFlags::CANNOT_SUBSTITUTE;
1205 SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
1206 if (IsOpaque()) {
1207 // If there's no transparency, it doesn't matter whether we premultiply
1208 // alpha or not.
1209 surfaceFlags &= ~SurfaceFlags::NO_PREMULTIPLY_ALPHA;
1212 // Create a decoder.
1213 RefPtr<IDecodingTask> task;
1214 nsresult rv;
1215 bool animated = mAnimationState && aPlaybackType == PlaybackType::eAnimated;
1216 if (animated) {
1217 size_t currentFrame = mAnimationState->GetCurrentAnimationFrameIndex();
1218 rv = DecoderFactory::CreateAnimationDecoder(
1219 mDecoderType, WrapNotNull(this), mSourceBuffer, mSize, decoderFlags,
1220 surfaceFlags, currentFrame, getter_AddRefs(task));
1221 } else {
1222 rv = DecoderFactory::CreateDecoder(
1223 mDecoderType, WrapNotNull(this), mSourceBuffer, mSize, aSize,
1224 decoderFlags, surfaceFlags, getter_AddRefs(task));
1227 if (rv == NS_ERROR_ALREADY_INITIALIZED) {
1228 // We raced with an already pending decoder, and it finished before we
1229 // managed to insert the new decoder. Pretend we did a sync call to make
1230 // the caller lookup in the surface cache again.
1231 MOZ_ASSERT(!task);
1232 return true;
1235 if (animated) {
1236 // We pass false for aAllowInvalidation because we may be asked to use
1237 // async notifications. Any potential invalidation here will be sent when
1238 // RequestRefresh is called, or NotifyDecodeComplete.
1239 #ifdef DEBUG
1240 gfx::IntRect rect =
1241 #endif
1242 mAnimationState->UpdateState(mAnimationFinished, this, mSize, false);
1243 MOZ_ASSERT(rect.IsEmpty());
1246 // Make sure DecoderFactory was able to create a decoder successfully.
1247 if (NS_FAILED(rv)) {
1248 MOZ_ASSERT(!task);
1249 return false;
1252 MOZ_ASSERT(task);
1253 mDecodeCount++;
1255 // We're ready to decode; start the decoder.
1256 return LaunchDecodingTask(task, this, aFlags, mAllSourceData);
1259 NS_IMETHODIMP
1260 RasterImage::DecodeMetadata(uint32_t aFlags) {
1261 if (mError) {
1262 return NS_ERROR_FAILURE;
1265 MOZ_ASSERT(!mHasSize, "Should not do unnecessary metadata decodes");
1267 // Create a decoder.
1268 RefPtr<IDecodingTask> task = DecoderFactory::CreateMetadataDecoder(
1269 mDecoderType, WrapNotNull(this), mSourceBuffer);
1271 // Make sure DecoderFactory was able to create a decoder successfully.
1272 if (!task) {
1273 return NS_ERROR_FAILURE;
1276 // We're ready to decode; start the decoder.
1277 LaunchDecodingTask(task, this, aFlags, mAllSourceData);
1278 return NS_OK;
1281 void RasterImage::RecoverFromInvalidFrames(const IntSize& aSize,
1282 uint32_t aFlags) {
1283 if (!mHasSize) {
1284 return;
1287 NS_WARNING("A RasterImage's frames became invalid. Attempting to recover...");
1289 // Discard all existing frames, since they're probably all now invalid.
1290 SurfaceCache::RemoveImage(ImageKey(this));
1292 // Relock the image if it's supposed to be locked.
1293 if (mLockCount > 0) {
1294 SurfaceCache::LockImage(ImageKey(this));
1297 // Animated images require some special handling, because we normally require
1298 // that they never be discarded.
1299 if (mAnimationState) {
1300 Decode(mSize, aFlags | FLAG_SYNC_DECODE, PlaybackType::eAnimated);
1301 ResetAnimation();
1302 return;
1305 // For non-animated images, it's fine to recover using an async decode.
1306 Decode(aSize, aFlags, PlaybackType::eStatic);
1309 static bool HaveSkia() {
1310 #ifdef MOZ_ENABLE_SKIA
1311 return true;
1312 #else
1313 return false;
1314 #endif
1317 bool RasterImage::CanDownscaleDuringDecode(const IntSize& aSize,
1318 uint32_t aFlags) {
1319 // Check basic requirements: downscale-during-decode is enabled, Skia is
1320 // available, this image isn't transient, we have all the source data and know
1321 // our size, and the flags allow us to do it.
1322 if (!mHasSize || mTransient || !HaveSkia() ||
1323 !StaticPrefs::image_downscale_during_decode_enabled() ||
1324 !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
1325 return false;
1328 // We don't downscale animated images during decode.
1329 if (mAnimationState) {
1330 return false;
1333 // Never upscale.
1334 if (aSize.width >= mSize.width || aSize.height >= mSize.height) {
1335 return false;
1338 // Zero or negative width or height is unacceptable.
1339 if (aSize.width < 1 || aSize.height < 1) {
1340 return false;
1343 // There's no point in scaling if we can't store the result.
1344 if (!SurfaceCache::CanHold(aSize)) {
1345 return false;
1348 return true;
1351 ImgDrawResult RasterImage::DrawInternal(DrawableSurface&& aSurface,
1352 gfxContext* aContext,
1353 const IntSize& aSize,
1354 const ImageRegion& aRegion,
1355 SamplingFilter aSamplingFilter,
1356 uint32_t aFlags, float aOpacity) {
1357 gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
1358 ImageRegion region(aRegion);
1359 bool frameIsFinished = aSurface->IsFinished();
1361 #ifdef DEBUG
1362 NotifyDrawingObservers();
1363 #endif
1365 // By now we may have a frame with the requested size. If not, we need to
1366 // adjust the drawing parameters accordingly.
1367 IntSize finalSize = aSurface->GetSize();
1368 bool couldRedecodeForBetterFrame = false;
1369 if (finalSize != aSize) {
1370 gfx::Size scale(double(aSize.width) / finalSize.width,
1371 double(aSize.height) / finalSize.height);
1372 aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
1373 region.Scale(1.0 / scale.width, 1.0 / scale.height);
1375 couldRedecodeForBetterFrame = CanDownscaleDuringDecode(aSize, aFlags);
1378 if (!aSurface->Draw(aContext, region, aSamplingFilter, aFlags, aOpacity)) {
1379 RecoverFromInvalidFrames(aSize, aFlags);
1380 return ImgDrawResult::TEMPORARY_ERROR;
1382 if (!frameIsFinished) {
1383 return ImgDrawResult::INCOMPLETE;
1385 if (couldRedecodeForBetterFrame) {
1386 return ImgDrawResult::WRONG_SIZE;
1388 return ImgDrawResult::SUCCESS;
1391 //******************************************************************************
1392 NS_IMETHODIMP_(ImgDrawResult)
1393 RasterImage::Draw(gfxContext* aContext, const IntSize& aSize,
1394 const ImageRegion& aRegion, uint32_t aWhichFrame,
1395 SamplingFilter aSamplingFilter,
1396 const Maybe<SVGImageContext>& /*aSVGContext - ignored*/,
1397 uint32_t aFlags, float aOpacity) {
1398 if (aWhichFrame > FRAME_MAX_VALUE) {
1399 return ImgDrawResult::BAD_ARGS;
1402 if (mError) {
1403 return ImgDrawResult::BAD_IMAGE;
1406 // Illegal -- you can't draw with non-default decode flags.
1407 // (Disabling colorspace conversion might make sense to allow, but
1408 // we don't currently.)
1409 if (ToSurfaceFlags(aFlags) != DefaultSurfaceFlags()) {
1410 return ImgDrawResult::BAD_ARGS;
1413 if (!aContext) {
1414 return ImgDrawResult::BAD_ARGS;
1417 if (mAnimationConsumers == 0) {
1418 SendOnUnlockedDraw(aFlags);
1421 // If we're not using SamplingFilter::GOOD, we shouldn't high-quality scale or
1422 // downscale during decode.
1423 uint32_t flags = aSamplingFilter == SamplingFilter::GOOD
1424 ? aFlags
1425 : aFlags & ~FLAG_HIGH_QUALITY_SCALING;
1427 LookupResult result = LookupFrame(aSize, flags, ToPlaybackType(aWhichFrame),
1428 /* aMarkUsed = */ true);
1429 if (!result) {
1430 // Getting the frame (above) touches the image and kicks off decoding.
1431 if (mDrawStartTime.IsNull()) {
1432 mDrawStartTime = TimeStamp::Now();
1434 return ImgDrawResult::NOT_READY;
1437 bool shouldRecordTelemetry =
1438 !mDrawStartTime.IsNull() && result.Surface()->IsFinished();
1440 auto drawResult = DrawInternal(std::move(result.Surface()), aContext, aSize,
1441 aRegion, aSamplingFilter, flags, aOpacity);
1443 if (shouldRecordTelemetry) {
1444 TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
1445 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY,
1446 int32_t(drawLatency.ToMicroseconds()));
1447 mDrawStartTime = TimeStamp();
1450 return drawResult;
1453 //******************************************************************************
1455 NS_IMETHODIMP
1456 RasterImage::LockImage() {
1457 MOZ_ASSERT(NS_IsMainThread(),
1458 "Main thread to encourage serialization with UnlockImage");
1459 if (mError) {
1460 return NS_ERROR_FAILURE;
1463 // Increment the lock count
1464 mLockCount++;
1466 // Lock this image's surfaces in the SurfaceCache.
1467 if (mLockCount == 1) {
1468 SurfaceCache::LockImage(ImageKey(this));
1471 return NS_OK;
1474 //******************************************************************************
1476 NS_IMETHODIMP
1477 RasterImage::UnlockImage() {
1478 MOZ_ASSERT(NS_IsMainThread(),
1479 "Main thread to encourage serialization with LockImage");
1480 if (mError) {
1481 return NS_ERROR_FAILURE;
1484 // It's an error to call this function if the lock count is 0
1485 MOZ_ASSERT(mLockCount > 0, "Calling UnlockImage with mLockCount == 0!");
1486 if (mLockCount == 0) {
1487 return NS_ERROR_ABORT;
1490 // Decrement our lock count
1491 mLockCount--;
1493 // Unlock this image's surfaces in the SurfaceCache.
1494 if (mLockCount == 0) {
1495 SurfaceCache::UnlockImage(ImageKey(this));
1498 return NS_OK;
1501 //******************************************************************************
1503 NS_IMETHODIMP
1504 RasterImage::RequestDiscard() {
1505 if (mDiscardable && // Enabled at creation time...
1506 mLockCount == 0 && // ...not temporarily disabled...
1507 CanDiscard()) {
1508 Discard();
1511 return NS_OK;
1514 // Idempotent error flagging routine. If a decoder is open, shuts it down.
1515 void RasterImage::DoError() {
1516 // If we've flagged an error before, we have nothing to do
1517 if (mError) {
1518 return;
1521 // We can't safely handle errors off-main-thread, so dispatch a worker to
1522 // do it.
1523 if (!NS_IsMainThread()) {
1524 HandleErrorWorker::DispatchIfNeeded(this);
1525 return;
1528 // Put the container in an error state.
1529 mError = true;
1531 // Stop animation and release our FrameAnimator.
1532 if (mAnimating) {
1533 StopAnimation();
1535 mAnimationState = Nothing();
1536 mFrameAnimator = nullptr;
1538 // Release all locks.
1539 mLockCount = 0;
1540 SurfaceCache::UnlockImage(ImageKey(this));
1542 // Release all frames from the surface cache.
1543 SurfaceCache::RemoveImage(ImageKey(this));
1545 // Invalidate to get rid of any partially-drawn image content.
1546 NotifyProgress(NoProgress, IntRect(0, 0, mSize.width, mSize.height));
1548 MOZ_LOG(gImgLog, LogLevel::Error,
1549 ("RasterImage: [this=%p] Error detected for image\n", this));
1552 /* static */
1553 void RasterImage::HandleErrorWorker::DispatchIfNeeded(RasterImage* aImage) {
1554 RefPtr<HandleErrorWorker> worker = new HandleErrorWorker(aImage);
1555 NS_DispatchToMainThread(worker);
1558 RasterImage::HandleErrorWorker::HandleErrorWorker(RasterImage* aImage)
1559 : Runnable("image::RasterImage::HandleErrorWorker"), mImage(aImage) {
1560 MOZ_ASSERT(mImage, "Should have image");
1563 NS_IMETHODIMP
1564 RasterImage::HandleErrorWorker::Run() {
1565 mImage->DoError();
1567 return NS_OK;
1570 bool RasterImage::ShouldAnimate() {
1571 return ImageResource::ShouldAnimate() && mAnimationState &&
1572 mAnimationState->KnownFrameCount() >= 1 && !mAnimationFinished;
1575 #ifdef DEBUG
1576 NS_IMETHODIMP
1577 RasterImage::GetFramesNotified(uint32_t* aFramesNotified) {
1578 NS_ENSURE_ARG_POINTER(aFramesNotified);
1580 *aFramesNotified = mFramesNotified;
1582 return NS_OK;
1584 #endif
1586 void RasterImage::NotifyProgress(
1587 Progress aProgress, const IntRect& aInvalidRect /* = IntRect() */,
1588 const Maybe<uint32_t>& aFrameCount /* = Nothing() */,
1589 DecoderFlags aDecoderFlags
1590 /* = DefaultDecoderFlags() */,
1591 SurfaceFlags aSurfaceFlags
1592 /* = DefaultSurfaceFlags() */) {
1593 MOZ_ASSERT(NS_IsMainThread());
1595 // Ensure that we stay alive long enough to finish notifying.
1596 RefPtr<RasterImage> image = this;
1598 const bool wasDefaultFlags = aSurfaceFlags == DefaultSurfaceFlags();
1600 if (!aInvalidRect.IsEmpty() && wasDefaultFlags) {
1601 // Update our image container since we're invalidating.
1602 UpdateImageContainer(Some(aInvalidRect));
1605 if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY)) {
1606 // We may have decoded new animation frames; update our animation state.
1607 MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError);
1608 if (mAnimationState && aFrameCount) {
1609 mAnimationState->UpdateKnownFrameCount(*aFrameCount);
1612 // If we should start animating right now, do so.
1613 if (mAnimationState && aFrameCount == Some(1u) && mPendingAnimation &&
1614 ShouldAnimate()) {
1615 StartAnimation();
1619 // Tell the observers what happened.
1620 image->mProgressTracker->SyncNotifyProgress(aProgress, aInvalidRect);
1623 void RasterImage::NotifyDecodeComplete(
1624 const DecoderFinalStatus& aStatus, const ImageMetadata& aMetadata,
1625 const DecoderTelemetry& aTelemetry, Progress aProgress,
1626 const IntRect& aInvalidRect, const Maybe<uint32_t>& aFrameCount,
1627 DecoderFlags aDecoderFlags, SurfaceFlags aSurfaceFlags) {
1628 MOZ_ASSERT(NS_IsMainThread());
1630 // If the decoder detected an error, log it to the error console.
1631 if (aStatus.mShouldReportError) {
1632 ReportDecoderError();
1635 // Record all the metadata the decoder gathered about this image.
1636 bool metadataOK = SetMetadata(aMetadata, aStatus.mWasMetadataDecode);
1637 if (!metadataOK) {
1638 // This indicates a serious error that requires us to discard all existing
1639 // surfaces and redecode to recover. We'll drop the results from this
1640 // decoder on the floor, since they aren't valid.
1641 RecoverFromInvalidFrames(mSize, FromSurfaceFlags(aSurfaceFlags));
1642 return;
1645 MOZ_ASSERT(mError || mHasSize || !aMetadata.HasSize(),
1646 "SetMetadata should've gotten a size");
1648 if (!aStatus.mWasMetadataDecode && aStatus.mFinished) {
1649 // Flag that we've been decoded before.
1650 mHasBeenDecoded = true;
1653 // Send out any final notifications.
1654 NotifyProgress(aProgress, aInvalidRect, aFrameCount, aDecoderFlags,
1655 aSurfaceFlags);
1657 if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY) && mHasBeenDecoded &&
1658 mAnimationState) {
1659 // We've finished a full decode of all animation frames and our
1660 // AnimationState has been notified about them all, so let it know not to
1661 // expect anymore.
1662 mAnimationState->NotifyDecodeComplete();
1663 gfx::IntRect rect =
1664 mAnimationState->UpdateState(mAnimationFinished, this, mSize);
1665 if (!rect.IsEmpty()) {
1666 NotifyProgress(NoProgress, rect);
1670 // Do some telemetry if this isn't a metadata decode.
1671 if (!aStatus.mWasMetadataDecode) {
1672 if (aTelemetry.mChunkCount) {
1673 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS,
1674 aTelemetry.mChunkCount);
1677 if (aStatus.mFinished) {
1678 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
1679 int32_t(aTelemetry.mDecodeTime.ToMicroseconds()));
1681 if (aTelemetry.mSpeedHistogram && aTelemetry.mBytesDecoded) {
1682 Telemetry::Accumulate(*aTelemetry.mSpeedHistogram, aTelemetry.Speed());
1687 // Only act on errors if we have no usable frames from the decoder.
1688 if (aStatus.mHadError &&
1689 (!mAnimationState || mAnimationState->KnownFrameCount() == 0)) {
1690 DoError();
1691 } else if (aStatus.mWasMetadataDecode && !mHasSize) {
1692 DoError();
1695 // XXX(aosmond): Can we get this far without mFinished == true?
1696 if (aStatus.mFinished && aStatus.mWasMetadataDecode) {
1697 // If we were waiting to fire the load event, go ahead and fire it now.
1698 if (mLoadProgress) {
1699 NotifyForLoadEvent(*mLoadProgress);
1700 mLoadProgress = Nothing();
1703 // If we were a metadata decode and a full decode was requested, do it.
1704 if (mWantFullDecode) {
1705 mWantFullDecode = false;
1706 RequestDecodeForSize(mSize,
1707 DECODE_FLAGS_DEFAULT | FLAG_HIGH_QUALITY_SCALING,
1708 FRAME_CURRENT);
1713 void RasterImage::ReportDecoderError() {
1714 nsCOMPtr<nsIConsoleService> consoleService =
1715 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1716 nsCOMPtr<nsIScriptError> errorObject =
1717 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
1719 if (consoleService && errorObject) {
1720 nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated."));
1721 nsAutoString src;
1722 if (GetURI()) {
1723 nsAutoCString uri;
1724 if (!GetSpecTruncatedTo1k(uri)) {
1725 msg += NS_LITERAL_STRING(" URI in this note truncated due to length.");
1727 src = NS_ConvertUTF8toUTF16(uri);
1729 if (NS_SUCCEEDED(errorObject->InitWithWindowID(msg, src, EmptyString(), 0,
1730 0, nsIScriptError::errorFlag,
1731 "Image", InnerWindowID()))) {
1732 consoleService->LogMessage(errorObject);
1737 already_AddRefed<imgIContainer> RasterImage::Unwrap() {
1738 nsCOMPtr<imgIContainer> self(this);
1739 return self.forget();
1742 void RasterImage::PropagateUseCounters(dom::Document*) {
1743 // No use counters.
1746 IntSize RasterImage::OptimalImageSizeForDest(const gfxSize& aDest,
1747 uint32_t aWhichFrame,
1748 SamplingFilter aSamplingFilter,
1749 uint32_t aFlags) {
1750 MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
1751 aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
1752 "Unexpected destination size");
1754 if (mSize.IsEmpty() || aDest.IsEmpty()) {
1755 return IntSize(0, 0);
1758 IntSize destSize = IntSize::Ceil(aDest.width, aDest.height);
1760 if (aSamplingFilter == SamplingFilter::GOOD &&
1761 CanDownscaleDuringDecode(destSize, aFlags)) {
1762 return destSize;
1765 // We can't scale to this size. Use our intrinsic size for now.
1766 return mSize;
1769 } // namespace image
1770 } // namespace mozilla