Bug 1492664 - update funsize scripts to use TASKCLUSTER_ROOT_URL; r=sfraser
[gecko.git] / image / RasterImage.cpp
blob006b911796df13224d1ed42fa6cfe119f0187621
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // Must #include ImageLogging.h before any IPDL-generated files or other files
7 // that #include prlog.h
8 #include "ImageLogging.h"
10 #include "RasterImage.h"
12 #include "gfxPlatform.h"
13 #include "nsComponentManagerUtils.h"
14 #include "nsError.h"
15 #include "DecodePool.h"
16 #include "Decoder.h"
17 #include "prenv.h"
18 #include "prsystem.h"
19 #include "IDecodingTask.h"
20 #include "ImageRegion.h"
21 #include "Layers.h"
22 #include "LookupResult.h"
23 #include "nsIConsoleService.h"
24 #include "nsIInputStream.h"
25 #include "nsIScriptError.h"
26 #include "nsISupportsPrimitives.h"
27 #include "nsMemory.h"
28 #include "nsPresContext.h"
29 #include "SourceBuffer.h"
30 #include "SurfaceCache.h"
31 #include "FrameAnimator.h"
33 #include "gfxContext.h"
35 #include "mozilla/gfx/2D.h"
36 #include "mozilla/DebugOnly.h"
37 #include "mozilla/Likely.h"
38 #include "mozilla/RefPtr.h"
39 #include "mozilla/Move.h"
40 #include "mozilla/MemoryReporting.h"
41 #include "mozilla/SizeOfState.h"
42 #include <stdint.h>
43 #include "mozilla/Telemetry.h"
44 #include "mozilla/TimeStamp.h"
45 #include "mozilla/Tuple.h"
46 #include "mozilla/ClearOnShutdown.h"
47 #include "mozilla/gfx/Scale.h"
49 #include "GeckoProfiler.h"
50 #include "gfx2DGlue.h"
51 #include "gfxPrefs.h"
52 #include "nsProperties.h"
53 #include <algorithm>
55 namespace mozilla {
57 using namespace gfx;
58 using namespace layers;
60 namespace image {
62 using std::ceil;
63 using std::min;
65 #ifndef DEBUG
66 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties)
67 #else
68 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties, imgIContainerDebug)
69 #endif
71 //******************************************************************************
72 RasterImage::RasterImage(nsIURI* aURI /* = nullptr */)
73 : ImageResource(aURI), // invoke superclass's constructor
74 mSize(0, 0),
75 mLockCount(0),
76 mDecoderType(DecoderType::UNKNOWN),
77 mDecodeCount(0),
78 #ifdef DEBUG
79 mFramesNotified(0),
80 #endif
81 mSourceBuffer(MakeNotNull<SourceBuffer*>()),
82 mHasSize(false),
83 mTransient(false),
84 mSyncLoad(false),
85 mDiscardable(false),
86 mSomeSourceData(false),
87 mAllSourceData(false),
88 mHasBeenDecoded(false),
89 mPendingAnimation(false),
90 mAnimationFinished(false),
91 mWantFullDecode(false) {
94 //******************************************************************************
95 RasterImage::~RasterImage() {
96 // Make sure our SourceBuffer is marked as complete. This will ensure that any
97 // outstanding decoders terminate.
98 if (!mSourceBuffer->IsComplete()) {
99 mSourceBuffer->Complete(NS_ERROR_ABORT);
102 // Release all frames from the surface cache.
103 SurfaceCache::RemoveImage(ImageKey(this));
105 // Record Telemetry.
106 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_COUNT, mDecodeCount);
109 nsresult RasterImage::Init(const char* aMimeType, uint32_t aFlags) {
110 // We don't support re-initialization
111 if (mInitialized) {
112 return NS_ERROR_ILLEGAL_VALUE;
115 // Not sure an error can happen before init, but be safe
116 if (mError) {
117 return NS_ERROR_FAILURE;
120 // We want to avoid redecodes for transient images.
121 MOZ_ASSERT_IF(aFlags & INIT_FLAG_TRANSIENT,
122 !(aFlags & INIT_FLAG_DISCARDABLE));
124 // Store initialization data
125 mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
126 mWantFullDecode = !!(aFlags & INIT_FLAG_DECODE_IMMEDIATELY);
127 mTransient = !!(aFlags & INIT_FLAG_TRANSIENT);
128 mSyncLoad = !!(aFlags & INIT_FLAG_SYNC_LOAD);
130 // Use the MIME type to select a decoder type, and make sure there *is* a
131 // decoder for this MIME type.
132 NS_ENSURE_ARG_POINTER(aMimeType);
133 mDecoderType = DecoderFactory::GetDecoderType(aMimeType);
134 if (mDecoderType == DecoderType::UNKNOWN) {
135 return NS_ERROR_FAILURE;
138 // Lock this image's surfaces in the SurfaceCache if we're not discardable.
139 if (!mDiscardable) {
140 mLockCount++;
141 SurfaceCache::LockImage(ImageKey(this));
144 // Mark us as initialized
145 mInitialized = true;
147 return NS_OK;
150 //******************************************************************************
151 NS_IMETHODIMP_(void)
152 RasterImage::RequestRefresh(const TimeStamp& aTime) {
153 if (HadRecentRefresh(aTime)) {
154 return;
157 EvaluateAnimation();
159 if (!mAnimating) {
160 return;
163 RefreshResult res;
164 if (mAnimationState) {
165 MOZ_ASSERT(mFrameAnimator);
166 res = mFrameAnimator->RequestRefresh(*mAnimationState, aTime,
167 mAnimationFinished);
170 if (res.mFrameAdvanced) {
171 // Notify listeners that our frame has actually changed, but do this only
172 // once for all frames that we've now passed (if AdvanceFrame() was called
173 // more than once).
174 #ifdef DEBUG
175 mFramesNotified++;
176 #endif
178 NotifyProgress(NoProgress, res.mDirtyRect);
181 if (res.mAnimationFinished) {
182 mAnimationFinished = true;
183 EvaluateAnimation();
187 //******************************************************************************
188 NS_IMETHODIMP
189 RasterImage::GetWidth(int32_t* aWidth) {
190 NS_ENSURE_ARG_POINTER(aWidth);
192 if (mError) {
193 *aWidth = 0;
194 return NS_ERROR_FAILURE;
197 *aWidth = mSize.width;
198 return NS_OK;
201 //******************************************************************************
202 NS_IMETHODIMP
203 RasterImage::GetHeight(int32_t* aHeight) {
204 NS_ENSURE_ARG_POINTER(aHeight);
206 if (mError) {
207 *aHeight = 0;
208 return NS_ERROR_FAILURE;
211 *aHeight = mSize.height;
212 return NS_OK;
215 //******************************************************************************
216 nsresult RasterImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const {
217 if (mError) {
218 return NS_ERROR_FAILURE;
221 if (mNativeSizes.IsEmpty()) {
222 aNativeSizes.Clear();
223 aNativeSizes.AppendElement(mSize);
224 } else {
225 aNativeSizes = mNativeSizes;
228 return NS_OK;
231 //******************************************************************************
232 size_t RasterImage::GetNativeSizesLength() const {
233 if (mError || !mHasSize) {
234 return 0;
237 if (mNativeSizes.IsEmpty()) {
238 return 1;
241 return mNativeSizes.Length();
244 //******************************************************************************
245 NS_IMETHODIMP
246 RasterImage::GetIntrinsicSize(nsSize* aSize) {
247 if (mError) {
248 return NS_ERROR_FAILURE;
251 *aSize = nsSize(nsPresContext::CSSPixelsToAppUnits(mSize.width),
252 nsPresContext::CSSPixelsToAppUnits(mSize.height));
253 return NS_OK;
256 //******************************************************************************
257 NS_IMETHODIMP
258 RasterImage::GetIntrinsicRatio(nsSize* aRatio) {
259 if (mError) {
260 return NS_ERROR_FAILURE;
263 *aRatio = nsSize(mSize.width, mSize.height);
264 return NS_OK;
267 NS_IMETHODIMP_(Orientation)
268 RasterImage::GetOrientation() { return mOrientation; }
270 //******************************************************************************
271 NS_IMETHODIMP
272 RasterImage::GetType(uint16_t* aType) {
273 NS_ENSURE_ARG_POINTER(aType);
275 *aType = imgIContainer::TYPE_RASTER;
276 return NS_OK;
279 LookupResult RasterImage::LookupFrameInternal(const IntSize& 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, surfaceFlags, PlaybackType::eStatic),
299 aMarkUsed);
302 // We'll return the best match we can find to the requested frame.
303 return SurfaceCache::LookupBestMatch(
304 ImageKey(this),
305 RasterSurfaceKey(aSize, surfaceFlags, PlaybackType::eStatic), aMarkUsed);
308 LookupResult RasterImage::LookupFrame(const IntSize& aSize, uint32_t aFlags,
309 PlaybackType aPlaybackType,
310 bool aMarkUsed) {
311 MOZ_ASSERT(NS_IsMainThread());
313 // If we're opaque, we don't need to care about premultiplied alpha, because
314 // that can only matter for frames with transparency.
315 if (IsOpaque()) {
316 aFlags &= ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
319 IntSize requestedSize =
320 CanDownscaleDuringDecode(aSize, aFlags) ? aSize : mSize;
321 if (requestedSize.IsEmpty()) {
322 // Can't decode to a surface of zero size.
323 return LookupResult(MatchType::NOT_FOUND);
326 LookupResult result =
327 LookupFrameInternal(requestedSize, aFlags, aPlaybackType, aMarkUsed);
329 if (!result && !mHasSize) {
330 // We can't request a decode without knowing our intrinsic size. Give up.
331 return LookupResult(MatchType::NOT_FOUND);
334 if (result.Type() == MatchType::NOT_FOUND ||
335 result.Type() == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND ||
336 ((aFlags & FLAG_SYNC_DECODE) && !result)) {
337 // We don't have a copy of this frame, and there's no decoder working on
338 // one. (Or we're sync decoding and the existing decoder hasn't even started
339 // yet.) Trigger decoding so it'll be available next time.
340 MOZ_ASSERT(aPlaybackType != PlaybackType::eAnimated ||
341 gfxPrefs::ImageMemAnimatedDiscardable() ||
342 !mAnimationState || mAnimationState->KnownFrameCount() < 1,
343 "Animated frames should be locked");
345 // The surface cache may suggest the preferred size we are supposed to
346 // decode at. This should only happen if we accept substitutions.
347 if (!result.SuggestedSize().IsEmpty()) {
348 MOZ_ASSERT(!(aFlags & FLAG_SYNC_DECODE) &&
349 (aFlags & FLAG_HIGH_QUALITY_SCALING));
350 requestedSize = result.SuggestedSize();
353 bool ranSync = Decode(requestedSize, aFlags, aPlaybackType);
355 // If we can or did sync decode, we should already have the frame.
356 if (ranSync || (aFlags & FLAG_SYNC_DECODE)) {
357 result =
358 LookupFrameInternal(requestedSize, aFlags, aPlaybackType, aMarkUsed);
362 if (!result) {
363 // We still weren't able to get a frame. Give up.
364 return result;
367 if (result.Surface()->GetCompositingFailed()) {
368 DrawableSurface tmp = std::move(result.Surface());
369 return result;
372 MOZ_ASSERT(!result.Surface()->GetIsPaletted(),
373 "Should not have a paletted frame");
375 // Sync decoding guarantees that we got the frame, but if it's owned by an
376 // async decoder that's currently running, the contents of the frame may not
377 // be available yet. Make sure we get everything.
378 if (mAllSourceData && (aFlags & FLAG_SYNC_DECODE)) {
379 result.Surface()->WaitUntilFinished();
382 // If we could have done some decoding in this function we need to check if
383 // that decoding encountered an error and hence aborted the surface. We want
384 // to avoid calling IsAborted if we weren't passed any sync decode flag
385 // because IsAborted acquires the monitor for the imgFrame.
386 if (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST) &&
387 result.Surface()->IsAborted()) {
388 DrawableSurface tmp = std::move(result.Surface());
389 return result;
392 return result;
395 bool RasterImage::IsOpaque() {
396 if (mError) {
397 return false;
400 Progress progress = mProgressTracker->GetProgress();
402 // If we haven't yet finished decoding, the safe answer is "not opaque".
403 if (!(progress & FLAG_DECODE_COMPLETE)) {
404 return false;
407 // Other, we're opaque if FLAG_HAS_TRANSPARENCY is not set.
408 return !(progress & FLAG_HAS_TRANSPARENCY);
411 NS_IMETHODIMP_(bool)
412 RasterImage::WillDrawOpaqueNow() {
413 if (!IsOpaque()) {
414 return false;
417 if (mAnimationState) {
418 if (!gfxPrefs::ImageMemAnimatedDiscardable()) {
419 // We never discard frames of animated images.
420 return true;
421 } else {
422 if (mAnimationState->GetCompositedFrameInvalid()) {
423 // We're not going to draw anything at all.
424 return false;
429 // If we are not locked our decoded data could get discard at any time (ie
430 // between the call to this function and when we are asked to draw), so we
431 // have to return false if we are unlocked.
432 if (mLockCount == 0) {
433 return false;
436 LookupResult result = SurfaceCache::LookupBestMatch(
437 ImageKey(this),
438 RasterSurfaceKey(mSize, DefaultSurfaceFlags(), PlaybackType::eStatic),
439 /* aMarkUsed = */ false);
440 MatchType matchType = result.Type();
441 if (matchType == MatchType::NOT_FOUND || matchType == MatchType::PENDING ||
442 !result.Surface()->IsFinished()) {
443 return false;
446 return true;
449 void RasterImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) {
450 MOZ_ASSERT(mProgressTracker);
452 bool animatedFramesDiscarded =
453 mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated;
455 nsCOMPtr<nsIEventTarget> eventTarget;
456 if (mProgressTracker) {
457 eventTarget = mProgressTracker->GetEventTarget();
458 } else {
459 eventTarget = do_GetMainThread();
462 RefPtr<RasterImage> image = this;
463 nsCOMPtr<nsIRunnable> ev =
464 NS_NewRunnableFunction("RasterImage::OnSurfaceDiscarded", [=]() -> void {
465 image->OnSurfaceDiscardedInternal(animatedFramesDiscarded);
467 eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
470 void RasterImage::OnSurfaceDiscardedInternal(bool aAnimatedFramesDiscarded) {
471 MOZ_ASSERT(NS_IsMainThread());
473 if (aAnimatedFramesDiscarded && mAnimationState) {
474 MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
475 ReleaseImageContainer();
476 gfx::IntRect rect =
477 mAnimationState->UpdateState(mAnimationFinished, this, mSize);
478 NotifyProgress(NoProgress, rect);
481 if (mProgressTracker) {
482 mProgressTracker->OnDiscard();
486 //******************************************************************************
487 NS_IMETHODIMP
488 RasterImage::GetAnimated(bool* aAnimated) {
489 if (mError) {
490 return NS_ERROR_FAILURE;
493 NS_ENSURE_ARG_POINTER(aAnimated);
495 // If we have an AnimationState, we can know for sure.
496 if (mAnimationState) {
497 *aAnimated = true;
498 return NS_OK;
501 // Otherwise, we need to have been decoded to know for sure, since if we were
502 // decoded at least once mAnimationState would have been created for animated
503 // images. This is true even though we check for animation during the
504 // metadata decode, because we may still discover animation only during the
505 // full decode for corrupt images.
506 if (!mHasBeenDecoded) {
507 return NS_ERROR_NOT_AVAILABLE;
510 // We know for sure
511 *aAnimated = false;
513 return NS_OK;
516 //******************************************************************************
517 NS_IMETHODIMP_(int32_t)
518 RasterImage::GetFirstFrameDelay() {
519 if (mError) {
520 return -1;
523 bool animated = false;
524 if (NS_FAILED(GetAnimated(&animated)) || !animated) {
525 return -1;
528 MOZ_ASSERT(mAnimationState, "Animated images should have an AnimationState");
529 return mAnimationState->FirstFrameTimeout().AsEncodedValueDeprecated();
532 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
533 RasterImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) {
534 return GetFrameAtSize(mSize, aWhichFrame, aFlags);
537 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
538 RasterImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame,
539 uint32_t aFlags) {
540 #ifdef DEBUG
541 NotifyDrawingObservers();
542 #endif
544 auto result = GetFrameInternal(aSize, Nothing(), aWhichFrame, aFlags);
545 return mozilla::Get<2>(result).forget();
548 Tuple<ImgDrawResult, IntSize, RefPtr<SourceSurface>>
549 RasterImage::GetFrameInternal(const IntSize& aSize,
550 const Maybe<SVGImageContext>& aSVGContext,
551 uint32_t aWhichFrame, uint32_t aFlags) {
552 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
554 if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE) {
555 return MakeTuple(ImgDrawResult::BAD_ARGS, aSize, RefPtr<SourceSurface>());
558 if (mError) {
559 return MakeTuple(ImgDrawResult::BAD_IMAGE, aSize, RefPtr<SourceSurface>());
562 // Get the frame. If it's not there, it's probably the caller's fault for
563 // not waiting for the data to be loaded from the network or not passing
564 // FLAG_SYNC_DECODE.
565 LookupResult result = LookupFrame(aSize, aFlags, ToPlaybackType(aWhichFrame),
566 /* aMarkUsed = */ true);
568 // The surface cache may have suggested we use a different size than the
569 // given size in the future. This may or may not be accompanied by an
570 // actual surface, depending on what it has in its cache.
571 IntSize suggestedSize =
572 result.SuggestedSize().IsEmpty() ? aSize : result.SuggestedSize();
573 MOZ_ASSERT_IF(result.Type() == MatchType::SUBSTITUTE_BECAUSE_BEST,
574 suggestedSize != aSize);
576 if (!result) {
577 // The OS threw this frame away and we couldn't redecode it.
578 return MakeTuple(ImgDrawResult::TEMPORARY_ERROR, suggestedSize,
579 RefPtr<SourceSurface>());
582 RefPtr<SourceSurface> surface = result.Surface()->GetSourceSurface();
583 if (!result.Surface()->IsFinished()) {
584 return MakeTuple(ImgDrawResult::INCOMPLETE, suggestedSize,
585 std::move(surface));
588 return MakeTuple(ImgDrawResult::SUCCESS, suggestedSize, std::move(surface));
591 Tuple<ImgDrawResult, IntSize> RasterImage::GetImageContainerSize(
592 LayerManager* aManager, const IntSize& aSize, uint32_t aFlags) {
593 if (!mHasSize) {
594 return MakeTuple(ImgDrawResult::NOT_READY, IntSize(0, 0));
597 if (aSize.IsEmpty()) {
598 return MakeTuple(ImgDrawResult::BAD_ARGS, IntSize(0, 0));
601 // We check the minimum size because while we support downscaling, we do not
602 // support upscaling. If aSize > mSize, we will never give a larger surface
603 // than mSize. If mSize > aSize, and mSize > maxTextureSize, we still want to
604 // use image containers if aSize <= maxTextureSize.
605 int32_t maxTextureSize = aManager->GetMaxTextureSize();
606 if (min(mSize.width, aSize.width) > maxTextureSize ||
607 min(mSize.height, aSize.height) > maxTextureSize) {
608 return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0));
611 if (!CanDownscaleDuringDecode(aSize, aFlags)) {
612 return MakeTuple(ImgDrawResult::SUCCESS, mSize);
615 return MakeTuple(ImgDrawResult::SUCCESS, aSize);
618 NS_IMETHODIMP_(bool)
619 RasterImage::IsImageContainerAvailable(LayerManager* aManager,
620 uint32_t aFlags) {
621 return IsImageContainerAvailableAtSize(aManager, mSize, aFlags);
624 NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
625 RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags) {
626 RefPtr<ImageContainer> container;
627 ImgDrawResult drawResult = GetImageContainerImpl(
628 aManager, mSize, Nothing(), aFlags, getter_AddRefs(container));
630 // We silence the unused warning here because anything that needs the draw
631 // result should be using GetImageContainerAtSize, not GetImageContainer.
632 (void)drawResult;
633 return container.forget();
636 NS_IMETHODIMP_(bool)
637 RasterImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
638 const IntSize& aSize,
639 uint32_t aFlags) {
640 // We check the minimum size because while we support downscaling, we do not
641 // support upscaling. If aSize > mSize, we will never give a larger surface
642 // than mSize. If mSize > aSize, and mSize > maxTextureSize, we still want to
643 // use image containers if aSize <= maxTextureSize.
644 int32_t maxTextureSize = aManager->GetMaxTextureSize();
645 if (!mHasSize || aSize.IsEmpty() ||
646 min(mSize.width, aSize.width) > maxTextureSize ||
647 min(mSize.height, aSize.height) > maxTextureSize) {
648 return false;
651 return true;
654 NS_IMETHODIMP_(ImgDrawResult)
655 RasterImage::GetImageContainerAtSize(layers::LayerManager* aManager,
656 const gfx::IntSize& aSize,
657 const Maybe<SVGImageContext>& aSVGContext,
658 uint32_t aFlags,
659 layers::ImageContainer** aOutContainer) {
660 // We do not pass in the given SVG context because in theory it could differ
661 // between calls, but actually have no impact on the actual contents of the
662 // image container.
663 return GetImageContainerImpl(aManager, aSize, Nothing(), aFlags,
664 aOutContainer);
667 size_t RasterImage::SizeOfSourceWithComputedFallback(
668 SizeOfState& aState) const {
669 return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(
670 aState.mMallocSizeOf);
673 void RasterImage::CollectSizeOfSurfaces(
674 nsTArray<SurfaceMemoryCounter>& aCounters,
675 MallocSizeOf aMallocSizeOf) const {
676 SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
677 if (mFrameAnimator) {
678 mFrameAnimator->CollectSizeOfCompositingSurfaces(aCounters, aMallocSizeOf);
682 bool RasterImage::SetMetadata(const ImageMetadata& aMetadata,
683 bool aFromMetadataDecode) {
684 MOZ_ASSERT(NS_IsMainThread());
686 if (mError) {
687 return true;
690 if (aMetadata.HasSize()) {
691 IntSize size = aMetadata.GetSize();
692 if (size.width < 0 || size.height < 0) {
693 NS_WARNING("Image has negative intrinsic size");
694 DoError();
695 return true;
698 MOZ_ASSERT(aMetadata.HasOrientation());
699 Orientation orientation = aMetadata.GetOrientation();
701 // If we already have a size, check the new size against the old one.
702 if (mHasSize && (size != mSize || orientation != mOrientation)) {
703 NS_WARNING(
704 "Image changed size or orientation on redecode! "
705 "This should not happen!");
706 DoError();
707 return true;
710 // Set the size and flag that we have it.
711 mSize = size;
712 mOrientation = orientation;
713 mNativeSizes = aMetadata.GetNativeSizes();
714 mHasSize = true;
717 if (mHasSize && aMetadata.HasAnimation() && !mAnimationState) {
718 // We're becoming animated, so initialize animation stuff.
719 mAnimationState.emplace(mAnimationMode);
720 mFrameAnimator = MakeUnique<FrameAnimator>(this, mSize);
722 if (!gfxPrefs::ImageMemAnimatedDiscardable()) {
723 // We don't support discarding animated images (See bug 414259).
724 // Lock the image and throw away the key.
725 LockImage();
728 if (!aFromMetadataDecode) {
729 // The metadata decode reported that this image isn't animated, but we
730 // discovered that it actually was during the full decode. This is a
731 // rare failure that only occurs for corrupt images. To recover, we need
732 // to discard all existing surfaces and redecode.
733 return false;
737 if (mAnimationState) {
738 mAnimationState->SetLoopCount(aMetadata.GetLoopCount());
739 mAnimationState->SetFirstFrameTimeout(aMetadata.GetFirstFrameTimeout());
741 if (aMetadata.HasLoopLength()) {
742 mAnimationState->SetLoopLength(aMetadata.GetLoopLength());
744 if (aMetadata.HasFirstFrameRefreshArea()) {
745 mAnimationState->SetFirstFrameRefreshArea(
746 aMetadata.GetFirstFrameRefreshArea());
750 if (aMetadata.HasHotspot()) {
751 IntPoint hotspot = aMetadata.GetHotspot();
753 nsCOMPtr<nsISupportsPRUint32> intwrapx =
754 do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
755 nsCOMPtr<nsISupportsPRUint32> intwrapy =
756 do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
757 intwrapx->SetData(hotspot.x);
758 intwrapy->SetData(hotspot.y);
760 Set("hotspotX", intwrapx);
761 Set("hotspotY", intwrapy);
764 return true;
767 NS_IMETHODIMP
768 RasterImage::SetAnimationMode(uint16_t aAnimationMode) {
769 if (mAnimationState) {
770 mAnimationState->SetAnimationMode(aAnimationMode);
772 return SetAnimationModeInternal(aAnimationMode);
775 //******************************************************************************
777 nsresult RasterImage::StartAnimation() {
778 if (mError) {
779 return NS_ERROR_FAILURE;
782 MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
784 // If we're not ready to animate, then set mPendingAnimation, which will cause
785 // us to start animating if and when we do become ready.
786 mPendingAnimation =
787 !mAnimationState || mAnimationState->KnownFrameCount() < 1;
788 if (mPendingAnimation) {
789 return NS_OK;
792 // Don't bother to animate if we're displaying the first frame forever.
793 if (mAnimationState->GetCurrentAnimationFrameIndex() == 0 &&
794 mAnimationState->FirstFrameTimeout() == FrameTimeout::Forever()) {
795 mAnimationFinished = true;
796 return NS_ERROR_ABORT;
799 // We need to set the time that this initial frame was first displayed, as
800 // this is used in AdvanceFrame().
801 mAnimationState->InitAnimationFrameTimeIfNecessary();
803 return NS_OK;
806 //******************************************************************************
807 nsresult RasterImage::StopAnimation() {
808 MOZ_ASSERT(mAnimating, "Should be animating!");
810 nsresult rv = NS_OK;
811 if (mError) {
812 rv = NS_ERROR_FAILURE;
813 } else {
814 mAnimationState->SetAnimationFrameTime(TimeStamp());
817 mAnimating = false;
818 return rv;
821 //******************************************************************************
822 NS_IMETHODIMP
823 RasterImage::ResetAnimation() {
824 if (mError) {
825 return NS_ERROR_FAILURE;
828 mPendingAnimation = false;
830 if (mAnimationMode == kDontAnimMode || !mAnimationState ||
831 mAnimationState->GetCurrentAnimationFrameIndex() == 0) {
832 return NS_OK;
835 mAnimationFinished = false;
837 if (mAnimating) {
838 StopAnimation();
841 MOZ_ASSERT(mAnimationState, "Should have AnimationState");
842 MOZ_ASSERT(mFrameAnimator, "Should have FrameAnimator");
843 mFrameAnimator->ResetAnimation(*mAnimationState);
845 NotifyProgress(NoProgress, mAnimationState->FirstFrameRefreshArea());
847 // Start the animation again. It may not have been running before, if
848 // mAnimationFinished was true before entering this function.
849 EvaluateAnimation();
851 return NS_OK;
854 //******************************************************************************
855 NS_IMETHODIMP_(void)
856 RasterImage::SetAnimationStartTime(const TimeStamp& aTime) {
857 if (mError || mAnimationMode == kDontAnimMode || mAnimating ||
858 !mAnimationState) {
859 return;
862 mAnimationState->SetAnimationFrameTime(aTime);
865 NS_IMETHODIMP_(float)
866 RasterImage::GetFrameIndex(uint32_t aWhichFrame) {
867 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
868 return (aWhichFrame == FRAME_FIRST || !mAnimationState)
869 ? 0.0f
870 : mAnimationState->GetCurrentAnimationFrameIndex();
873 NS_IMETHODIMP_(IntRect)
874 RasterImage::GetImageSpaceInvalidationRect(const IntRect& aRect) {
875 return aRect;
878 nsresult RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*,
879 nsresult aStatus, bool aLastPart) {
880 MOZ_ASSERT(NS_IsMainThread());
882 // Record that we have all the data we're going to get now.
883 mAllSourceData = true;
885 // Let decoders know that there won't be any more data coming.
886 mSourceBuffer->Complete(aStatus);
888 // Allow a synchronous metadata decode if mSyncLoad was set, or if we're
889 // running on a single thread (in which case waiting for the async metadata
890 // decoder could delay this image's load event quite a bit), or if this image
891 // is transient.
892 bool canSyncDecodeMetadata =
893 mSyncLoad || mTransient || DecodePool::NumberOfCores() < 2;
895 if (canSyncDecodeMetadata && !mHasSize) {
896 // We're loading this image synchronously, so it needs to be usable after
897 // this call returns. Since we haven't gotten our size yet, we need to do a
898 // synchronous metadata decode here.
899 DecodeMetadata(FLAG_SYNC_DECODE);
902 // Determine our final status, giving precedence to Necko failure codes. We
903 // check after running the metadata decode in case it triggered an error.
904 nsresult finalStatus = mError ? NS_ERROR_FAILURE : NS_OK;
905 if (NS_FAILED(aStatus)) {
906 finalStatus = aStatus;
909 // If loading failed, report an error.
910 if (NS_FAILED(finalStatus)) {
911 DoError();
914 Progress loadProgress = LoadCompleteProgress(aLastPart, mError, finalStatus);
916 if (!mHasSize && !mError) {
917 // We don't have our size yet, so we'll fire the load event in SetSize().
918 MOZ_ASSERT(!canSyncDecodeMetadata,
919 "Firing load async after metadata sync decode?");
920 mLoadProgress = Some(loadProgress);
921 return finalStatus;
924 NotifyForLoadEvent(loadProgress);
926 return finalStatus;
929 void RasterImage::NotifyForLoadEvent(Progress aProgress) {
930 MOZ_ASSERT(mHasSize || mError, "Need to know size before firing load event");
931 MOZ_ASSERT(
932 !mHasSize || (mProgressTracker->GetProgress() & FLAG_SIZE_AVAILABLE),
933 "Should have notified that the size is available if we have it");
935 // If we encountered an error, make sure we notify for that as well.
936 if (mError) {
937 aProgress |= FLAG_HAS_ERROR;
940 // Notify our listeners, which will fire this image's load event.
941 NotifyProgress(aProgress);
944 nsresult RasterImage::OnImageDataAvailable(nsIRequest*, nsISupports*,
945 nsIInputStream* aInputStream,
946 uint64_t, uint32_t aCount) {
947 nsresult rv = mSourceBuffer->AppendFromInputStream(aInputStream, aCount);
948 if (NS_SUCCEEDED(rv) && !mSomeSourceData) {
949 mSomeSourceData = true;
950 if (!mSyncLoad) {
951 // Create an async metadata decoder and verify we succeed in doing so.
952 rv = DecodeMetadata(DECODE_FLAGS_DEFAULT);
956 if (NS_FAILED(rv)) {
957 DoError();
959 return rv;
962 nsresult RasterImage::SetSourceSizeHint(uint32_t aSizeHint) {
963 if (aSizeHint == 0) {
964 return NS_OK;
967 nsresult rv = mSourceBuffer->ExpectLength(aSizeHint);
968 if (rv == NS_ERROR_OUT_OF_MEMORY) {
969 // Flush memory, try to get some back, and try again.
970 rv = nsMemory::HeapMinimize(true);
971 if (NS_SUCCEEDED(rv)) {
972 rv = mSourceBuffer->ExpectLength(aSizeHint);
976 return rv;
979 /********* Methods to implement lazy allocation of nsIProperties object *******/
980 NS_IMETHODIMP
981 RasterImage::Get(const char* prop, const nsIID& iid, void** result) {
982 if (!mProperties) {
983 return NS_ERROR_FAILURE;
985 return mProperties->Get(prop, iid, result);
988 NS_IMETHODIMP
989 RasterImage::Set(const char* prop, nsISupports* value) {
990 if (!mProperties) {
991 mProperties = new nsProperties();
993 return mProperties->Set(prop, value);
996 NS_IMETHODIMP
997 RasterImage::Has(const char* prop, bool* _retval) {
998 NS_ENSURE_ARG_POINTER(_retval);
999 if (!mProperties) {
1000 *_retval = false;
1001 return NS_OK;
1003 return mProperties->Has(prop, _retval);
1006 NS_IMETHODIMP
1007 RasterImage::Undefine(const char* prop) {
1008 if (!mProperties) {
1009 return NS_ERROR_FAILURE;
1011 return mProperties->Undefine(prop);
1014 NS_IMETHODIMP
1015 RasterImage::GetKeys(uint32_t* count, char*** keys) {
1016 if (!mProperties) {
1017 *count = 0;
1018 *keys = nullptr;
1019 return NS_OK;
1021 return mProperties->GetKeys(count, keys);
1024 void RasterImage::Discard() {
1025 MOZ_ASSERT(NS_IsMainThread());
1026 MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
1027 MOZ_ASSERT(!mAnimationState || gfxPrefs::ImageMemAnimatedDiscardable(),
1028 "Asked to discard for animated image");
1030 // Delete all the decoded frames.
1031 SurfaceCache::RemoveImage(ImageKey(this));
1033 if (mAnimationState) {
1034 ReleaseImageContainer();
1035 gfx::IntRect rect =
1036 mAnimationState->UpdateState(mAnimationFinished, this, mSize);
1037 NotifyProgress(NoProgress, rect);
1040 // Notify that we discarded.
1041 if (mProgressTracker) {
1042 mProgressTracker->OnDiscard();
1046 bool RasterImage::CanDiscard() {
1047 return mAllSourceData &&
1048 // Can discard animated images if the pref is set
1049 (!mAnimationState || gfxPrefs::ImageMemAnimatedDiscardable());
1052 NS_IMETHODIMP
1053 RasterImage::StartDecoding(uint32_t aFlags) {
1054 if (mError) {
1055 return NS_ERROR_FAILURE;
1058 if (!mHasSize) {
1059 mWantFullDecode = true;
1060 return NS_OK;
1063 uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
1064 FLAG_HIGH_QUALITY_SCALING;
1065 return RequestDecodeForSize(mSize, flags);
1068 bool RasterImage::StartDecodingWithResult(uint32_t aFlags) {
1069 if (mError) {
1070 return false;
1073 if (!mHasSize) {
1074 mWantFullDecode = true;
1075 return false;
1078 uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST |
1079 FLAG_HIGH_QUALITY_SCALING;
1080 DrawableSurface surface = RequestDecodeForSizeInternal(mSize, flags);
1081 return surface && surface->IsFinished();
1084 NS_IMETHODIMP
1085 RasterImage::RequestDecodeForSize(const IntSize& aSize, uint32_t aFlags) {
1086 MOZ_ASSERT(NS_IsMainThread());
1088 if (mError) {
1089 return NS_ERROR_FAILURE;
1092 RequestDecodeForSizeInternal(aSize, aFlags);
1094 return NS_OK;
1097 DrawableSurface RasterImage::RequestDecodeForSizeInternal(const IntSize& aSize,
1098 uint32_t aFlags) {
1099 MOZ_ASSERT(NS_IsMainThread());
1101 if (mError) {
1102 return DrawableSurface();
1105 if (!mHasSize) {
1106 mWantFullDecode = true;
1107 return DrawableSurface();
1110 // Decide whether to sync decode images we can decode quickly. Here we are
1111 // explicitly trading off flashing for responsiveness in the case that we're
1112 // redecoding an image (see bug 845147).
1113 bool shouldSyncDecodeIfFast =
1114 !mHasBeenDecoded && (aFlags & FLAG_SYNC_DECODE_IF_FAST);
1116 uint32_t flags =
1117 shouldSyncDecodeIfFast ? aFlags : aFlags & ~FLAG_SYNC_DECODE_IF_FAST;
1119 // Perform a frame lookup, which will implicitly start decoding if needed.
1120 PlaybackType playbackType =
1121 mAnimationState ? PlaybackType::eAnimated : PlaybackType::eStatic;
1122 LookupResult result =
1123 LookupFrame(aSize, flags, playbackType, /* aMarkUsed = */ false);
1124 return std::move(result.Surface());
1127 static bool LaunchDecodingTask(IDecodingTask* aTask, RasterImage* aImage,
1128 uint32_t aFlags, bool aHaveSourceData) {
1129 if (aHaveSourceData) {
1130 nsCString uri(aImage->GetURIString());
1132 // If we have all the data, we can sync decode if requested.
1133 if (aFlags & imgIContainer::FLAG_SYNC_DECODE) {
1134 DecodePool::Singleton()->SyncRunIfPossible(aTask, uri);
1135 return true;
1138 if (aFlags & imgIContainer::FLAG_SYNC_DECODE_IF_FAST) {
1139 return DecodePool::Singleton()->SyncRunIfPreferred(aTask, uri);
1143 // Perform an async decode. We also take this path if we don't have all the
1144 // source data yet, since sync decoding is impossible in that situation.
1145 DecodePool::Singleton()->AsyncRun(aTask);
1146 return false;
1149 bool RasterImage::Decode(const IntSize& aSize, uint32_t aFlags,
1150 PlaybackType aPlaybackType) {
1151 MOZ_ASSERT(NS_IsMainThread());
1153 if (mError) {
1154 return false;
1157 // If we don't have a size yet, we can't do any other decoding.
1158 if (!mHasSize) {
1159 mWantFullDecode = true;
1160 return false;
1163 // We're about to decode again, which may mean that some of the previous sizes
1164 // we've decoded at aren't useful anymore. We can allow them to expire from
1165 // the cache by unlocking them here. When the decode finishes, it will send an
1166 // invalidation that will cause all instances of this image to redraw. If this
1167 // image is locked, any surfaces that are still useful will become locked
1168 // again when LookupFrame touches them, and the remainder will eventually
1169 // expire.
1170 SurfaceCache::UnlockEntries(ImageKey(this));
1172 // Determine which flags we need to decode this image with.
1173 DecoderFlags decoderFlags = DefaultDecoderFlags();
1174 if (aFlags & FLAG_ASYNC_NOTIFY) {
1175 decoderFlags |= DecoderFlags::ASYNC_NOTIFY;
1177 if (mTransient) {
1178 decoderFlags |= DecoderFlags::IMAGE_IS_TRANSIENT;
1180 if (mHasBeenDecoded) {
1181 decoderFlags |= DecoderFlags::IS_REDECODE;
1183 if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
1184 // Used SurfaceCache::Lookup instead of SurfaceCache::LookupBestMatch. That
1185 // means the caller can handle a differently sized surface to be returned
1186 // at any point.
1187 decoderFlags |= DecoderFlags::CANNOT_SUBSTITUTE;
1190 SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
1191 if (IsOpaque()) {
1192 // If there's no transparency, it doesn't matter whether we premultiply
1193 // alpha or not.
1194 surfaceFlags &= ~SurfaceFlags::NO_PREMULTIPLY_ALPHA;
1197 // Create a decoder.
1198 RefPtr<IDecodingTask> task;
1199 nsresult rv;
1200 bool animated = mAnimationState && aPlaybackType == PlaybackType::eAnimated;
1201 if (animated) {
1202 if (gfxPrefs::ImageAnimatedGenerateFullFrames()) {
1203 decoderFlags |= DecoderFlags::BLEND_ANIMATION;
1206 size_t currentFrame = mAnimationState->GetCurrentAnimationFrameIndex();
1207 rv = DecoderFactory::CreateAnimationDecoder(
1208 mDecoderType, WrapNotNull(this), mSourceBuffer, mSize, decoderFlags,
1209 surfaceFlags, currentFrame, getter_AddRefs(task));
1210 } else {
1211 rv = DecoderFactory::CreateDecoder(
1212 mDecoderType, WrapNotNull(this), mSourceBuffer, mSize, aSize,
1213 decoderFlags, surfaceFlags, getter_AddRefs(task));
1216 if (rv == NS_ERROR_ALREADY_INITIALIZED) {
1217 // We raced with an already pending decoder, and it finished before we
1218 // managed to insert the new decoder. Pretend we did a sync call to make
1219 // the caller lookup in the surface cache again.
1220 MOZ_ASSERT(!task);
1221 return true;
1224 if (animated) {
1225 // We pass false for aAllowInvalidation because we may be asked to use
1226 // async notifications. Any potential invalidation here will be sent when
1227 // RequestRefresh is called, or NotifyDecodeComplete.
1228 #ifdef DEBUG
1229 gfx::IntRect rect =
1230 #endif
1231 mAnimationState->UpdateState(mAnimationFinished, this, mSize, false);
1232 MOZ_ASSERT(rect.IsEmpty());
1235 // Make sure DecoderFactory was able to create a decoder successfully.
1236 if (NS_FAILED(rv)) {
1237 MOZ_ASSERT(!task);
1238 return false;
1241 MOZ_ASSERT(task);
1242 mDecodeCount++;
1244 // We're ready to decode; start the decoder.
1245 return LaunchDecodingTask(task, this, aFlags, mAllSourceData);
1248 NS_IMETHODIMP
1249 RasterImage::DecodeMetadata(uint32_t aFlags) {
1250 if (mError) {
1251 return NS_ERROR_FAILURE;
1254 MOZ_ASSERT(!mHasSize, "Should not do unnecessary metadata decodes");
1256 // Create a decoder.
1257 RefPtr<IDecodingTask> task = DecoderFactory::CreateMetadataDecoder(
1258 mDecoderType, WrapNotNull(this), mSourceBuffer);
1260 // Make sure DecoderFactory was able to create a decoder successfully.
1261 if (!task) {
1262 return NS_ERROR_FAILURE;
1265 // We're ready to decode; start the decoder.
1266 LaunchDecodingTask(task, this, aFlags, mAllSourceData);
1267 return NS_OK;
1270 void RasterImage::RecoverFromInvalidFrames(const IntSize& aSize,
1271 uint32_t aFlags) {
1272 if (!mHasSize) {
1273 return;
1276 NS_WARNING("A RasterImage's frames became invalid. Attempting to recover...");
1278 // Discard all existing frames, since they're probably all now invalid.
1279 SurfaceCache::RemoveImage(ImageKey(this));
1281 // Relock the image if it's supposed to be locked.
1282 if (mLockCount > 0) {
1283 SurfaceCache::LockImage(ImageKey(this));
1286 // Animated images require some special handling, because we normally require
1287 // that they never be discarded.
1288 if (mAnimationState) {
1289 Decode(mSize, aFlags | FLAG_SYNC_DECODE, PlaybackType::eAnimated);
1290 ResetAnimation();
1291 return;
1294 // For non-animated images, it's fine to recover using an async decode.
1295 Decode(aSize, aFlags, PlaybackType::eStatic);
1298 static bool HaveSkia() {
1299 #ifdef MOZ_ENABLE_SKIA
1300 return true;
1301 #else
1302 return false;
1303 #endif
1306 bool RasterImage::CanDownscaleDuringDecode(const IntSize& aSize,
1307 uint32_t aFlags) {
1308 // Check basic requirements: downscale-during-decode is enabled, Skia is
1309 // available, this image isn't transient, we have all the source data and know
1310 // our size, and the flags allow us to do it.
1311 if (!mHasSize || mTransient || !HaveSkia() ||
1312 !gfxPrefs::ImageDownscaleDuringDecodeEnabled() ||
1313 !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
1314 return false;
1317 // We don't downscale animated images during decode.
1318 if (mAnimationState) {
1319 return false;
1322 // Never upscale.
1323 if (aSize.width >= mSize.width || aSize.height >= mSize.height) {
1324 return false;
1327 // Zero or negative width or height is unacceptable.
1328 if (aSize.width < 1 || aSize.height < 1) {
1329 return false;
1332 // There's no point in scaling if we can't store the result.
1333 if (!SurfaceCache::CanHold(aSize)) {
1334 return false;
1337 return true;
1340 ImgDrawResult RasterImage::DrawInternal(DrawableSurface&& aSurface,
1341 gfxContext* aContext,
1342 const IntSize& aSize,
1343 const ImageRegion& aRegion,
1344 SamplingFilter aSamplingFilter,
1345 uint32_t aFlags, float aOpacity) {
1346 gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
1347 ImageRegion region(aRegion);
1348 bool frameIsFinished = aSurface->IsFinished();
1350 #ifdef DEBUG
1351 NotifyDrawingObservers();
1352 #endif
1354 // By now we may have a frame with the requested size. If not, we need to
1355 // adjust the drawing parameters accordingly.
1356 IntSize finalSize = aSurface->GetImageSize();
1357 bool couldRedecodeForBetterFrame = false;
1358 if (finalSize != aSize) {
1359 gfx::Size scale(double(aSize.width) / finalSize.width,
1360 double(aSize.height) / finalSize.height);
1361 aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
1362 region.Scale(1.0 / scale.width, 1.0 / scale.height);
1364 couldRedecodeForBetterFrame = CanDownscaleDuringDecode(aSize, aFlags);
1367 if (!aSurface->Draw(aContext, region, aSamplingFilter, aFlags, aOpacity)) {
1368 RecoverFromInvalidFrames(aSize, aFlags);
1369 return ImgDrawResult::TEMPORARY_ERROR;
1371 if (!frameIsFinished) {
1372 return ImgDrawResult::INCOMPLETE;
1374 if (couldRedecodeForBetterFrame) {
1375 return ImgDrawResult::WRONG_SIZE;
1377 return ImgDrawResult::SUCCESS;
1380 //******************************************************************************
1381 NS_IMETHODIMP_(ImgDrawResult)
1382 RasterImage::Draw(gfxContext* aContext, const IntSize& aSize,
1383 const ImageRegion& aRegion, uint32_t aWhichFrame,
1384 SamplingFilter aSamplingFilter,
1385 const Maybe<SVGImageContext>& /*aSVGContext - ignored*/,
1386 uint32_t aFlags, float aOpacity) {
1387 if (aWhichFrame > FRAME_MAX_VALUE) {
1388 return ImgDrawResult::BAD_ARGS;
1391 if (mError) {
1392 return ImgDrawResult::BAD_IMAGE;
1395 // Illegal -- you can't draw with non-default decode flags.
1396 // (Disabling colorspace conversion might make sense to allow, but
1397 // we don't currently.)
1398 if (ToSurfaceFlags(aFlags) != DefaultSurfaceFlags()) {
1399 return ImgDrawResult::BAD_ARGS;
1402 if (!aContext) {
1403 return ImgDrawResult::BAD_ARGS;
1406 if (mAnimationConsumers == 0) {
1407 SendOnUnlockedDraw(aFlags);
1410 // If we're not using SamplingFilter::GOOD, we shouldn't high-quality scale or
1411 // downscale during decode.
1412 uint32_t flags = aSamplingFilter == SamplingFilter::GOOD
1413 ? aFlags
1414 : aFlags & ~FLAG_HIGH_QUALITY_SCALING;
1416 LookupResult result = LookupFrame(aSize, flags, ToPlaybackType(aWhichFrame),
1417 /* aMarkUsed = */ true);
1418 if (!result) {
1419 // Getting the frame (above) touches the image and kicks off decoding.
1420 if (mDrawStartTime.IsNull()) {
1421 mDrawStartTime = TimeStamp::Now();
1423 return ImgDrawResult::NOT_READY;
1426 bool shouldRecordTelemetry =
1427 !mDrawStartTime.IsNull() && result.Surface()->IsFinished();
1429 auto drawResult = DrawInternal(std::move(result.Surface()), aContext, aSize,
1430 aRegion, aSamplingFilter, flags, aOpacity);
1432 if (shouldRecordTelemetry) {
1433 TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
1434 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY,
1435 int32_t(drawLatency.ToMicroseconds()));
1436 mDrawStartTime = TimeStamp();
1439 return drawResult;
1442 //******************************************************************************
1444 NS_IMETHODIMP
1445 RasterImage::LockImage() {
1446 MOZ_ASSERT(NS_IsMainThread(),
1447 "Main thread to encourage serialization with UnlockImage");
1448 if (mError) {
1449 return NS_ERROR_FAILURE;
1452 // Increment the lock count
1453 mLockCount++;
1455 // Lock this image's surfaces in the SurfaceCache.
1456 if (mLockCount == 1) {
1457 SurfaceCache::LockImage(ImageKey(this));
1460 return NS_OK;
1463 //******************************************************************************
1465 NS_IMETHODIMP
1466 RasterImage::UnlockImage() {
1467 MOZ_ASSERT(NS_IsMainThread(),
1468 "Main thread to encourage serialization with LockImage");
1469 if (mError) {
1470 return NS_ERROR_FAILURE;
1473 // It's an error to call this function if the lock count is 0
1474 MOZ_ASSERT(mLockCount > 0, "Calling UnlockImage with mLockCount == 0!");
1475 if (mLockCount == 0) {
1476 return NS_ERROR_ABORT;
1479 // Decrement our lock count
1480 mLockCount--;
1482 // Unlock this image's surfaces in the SurfaceCache.
1483 if (mLockCount == 0) {
1484 SurfaceCache::UnlockImage(ImageKey(this));
1487 return NS_OK;
1490 //******************************************************************************
1492 NS_IMETHODIMP
1493 RasterImage::RequestDiscard() {
1494 if (mDiscardable && // Enabled at creation time...
1495 mLockCount == 0 && // ...not temporarily disabled...
1496 CanDiscard()) {
1497 Discard();
1500 return NS_OK;
1503 // Indempotent error flagging routine. If a decoder is open, shuts it down.
1504 void RasterImage::DoError() {
1505 // If we've flagged an error before, we have nothing to do
1506 if (mError) {
1507 return;
1510 // We can't safely handle errors off-main-thread, so dispatch a worker to
1511 // do it.
1512 if (!NS_IsMainThread()) {
1513 HandleErrorWorker::DispatchIfNeeded(this);
1514 return;
1517 // Put the container in an error state.
1518 mError = true;
1520 // Stop animation and release our FrameAnimator.
1521 if (mAnimating) {
1522 StopAnimation();
1524 mAnimationState = Nothing();
1525 mFrameAnimator = nullptr;
1527 // Release all locks.
1528 mLockCount = 0;
1529 SurfaceCache::UnlockImage(ImageKey(this));
1531 // Release all frames from the surface cache.
1532 SurfaceCache::RemoveImage(ImageKey(this));
1534 // Invalidate to get rid of any partially-drawn image content.
1535 NotifyProgress(NoProgress, IntRect(0, 0, mSize.width, mSize.height));
1537 MOZ_LOG(gImgLog, LogLevel::Error,
1538 ("RasterImage: [this=%p] Error detected for image\n", this));
1541 /* static */ void RasterImage::HandleErrorWorker::DispatchIfNeeded(
1542 RasterImage* aImage) {
1543 RefPtr<HandleErrorWorker> worker = new HandleErrorWorker(aImage);
1544 NS_DispatchToMainThread(worker);
1547 RasterImage::HandleErrorWorker::HandleErrorWorker(RasterImage* aImage)
1548 : Runnable("image::RasterImage::HandleErrorWorker"), mImage(aImage) {
1549 MOZ_ASSERT(mImage, "Should have image");
1552 NS_IMETHODIMP
1553 RasterImage::HandleErrorWorker::Run() {
1554 mImage->DoError();
1556 return NS_OK;
1559 bool RasterImage::ShouldAnimate() {
1560 return ImageResource::ShouldAnimate() && mAnimationState &&
1561 mAnimationState->KnownFrameCount() >= 1 && !mAnimationFinished;
1564 #ifdef DEBUG
1565 NS_IMETHODIMP
1566 RasterImage::GetFramesNotified(uint32_t* aFramesNotified) {
1567 NS_ENSURE_ARG_POINTER(aFramesNotified);
1569 *aFramesNotified = mFramesNotified;
1571 return NS_OK;
1573 #endif
1575 void RasterImage::NotifyProgress(
1576 Progress aProgress, const IntRect& aInvalidRect /* = IntRect() */,
1577 const Maybe<uint32_t>& aFrameCount /* = Nothing() */,
1578 DecoderFlags aDecoderFlags
1579 /* = DefaultDecoderFlags() */,
1580 SurfaceFlags aSurfaceFlags
1581 /* = DefaultSurfaceFlags() */) {
1582 MOZ_ASSERT(NS_IsMainThread());
1584 // Ensure that we stay alive long enough to finish notifying.
1585 RefPtr<RasterImage> image = this;
1587 const bool wasDefaultFlags = aSurfaceFlags == DefaultSurfaceFlags();
1589 if (!aInvalidRect.IsEmpty() && wasDefaultFlags) {
1590 // Update our image container since we're invalidating.
1591 UpdateImageContainer(Some(aInvalidRect));
1594 if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY)) {
1595 // We may have decoded new animation frames; update our animation state.
1596 MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError);
1597 if (mAnimationState && aFrameCount) {
1598 mAnimationState->UpdateKnownFrameCount(*aFrameCount);
1601 // If we should start animating right now, do so.
1602 if (mAnimationState && aFrameCount == Some(1u) && mPendingAnimation &&
1603 ShouldAnimate()) {
1604 StartAnimation();
1608 // Tell the observers what happened.
1609 image->mProgressTracker->SyncNotifyProgress(aProgress, aInvalidRect);
1612 void RasterImage::NotifyDecodeComplete(
1613 const DecoderFinalStatus& aStatus, const ImageMetadata& aMetadata,
1614 const DecoderTelemetry& aTelemetry, Progress aProgress,
1615 const IntRect& aInvalidRect, const Maybe<uint32_t>& aFrameCount,
1616 DecoderFlags aDecoderFlags, SurfaceFlags aSurfaceFlags) {
1617 MOZ_ASSERT(NS_IsMainThread());
1619 // If the decoder detected an error, log it to the error console.
1620 if (aStatus.mShouldReportError) {
1621 ReportDecoderError();
1624 // Record all the metadata the decoder gathered about this image.
1625 bool metadataOK = SetMetadata(aMetadata, aStatus.mWasMetadataDecode);
1626 if (!metadataOK) {
1627 // This indicates a serious error that requires us to discard all existing
1628 // surfaces and redecode to recover. We'll drop the results from this
1629 // decoder on the floor, since they aren't valid.
1630 RecoverFromInvalidFrames(mSize, FromSurfaceFlags(aSurfaceFlags));
1631 return;
1634 MOZ_ASSERT(mError || mHasSize || !aMetadata.HasSize(),
1635 "SetMetadata should've gotten a size");
1637 if (!aStatus.mWasMetadataDecode && aStatus.mFinished) {
1638 // Flag that we've been decoded before.
1639 mHasBeenDecoded = true;
1642 // Send out any final notifications.
1643 NotifyProgress(aProgress, aInvalidRect, aFrameCount, aDecoderFlags,
1644 aSurfaceFlags);
1646 if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY) && mHasBeenDecoded &&
1647 mAnimationState) {
1648 // We've finished a full decode of all animation frames and our
1649 // AnimationState has been notified about them all, so let it know not to
1650 // expect anymore.
1651 mAnimationState->NotifyDecodeComplete();
1652 gfx::IntRect rect =
1653 mAnimationState->UpdateState(mAnimationFinished, this, mSize);
1654 if (!rect.IsEmpty()) {
1655 NotifyProgress(NoProgress, rect);
1659 // Do some telemetry if this isn't a metadata decode.
1660 if (!aStatus.mWasMetadataDecode) {
1661 if (aTelemetry.mChunkCount) {
1662 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS,
1663 aTelemetry.mChunkCount);
1666 if (aStatus.mFinished) {
1667 Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
1668 int32_t(aTelemetry.mDecodeTime.ToMicroseconds()));
1670 if (aTelemetry.mSpeedHistogram && aTelemetry.mBytesDecoded) {
1671 Telemetry::Accumulate(*aTelemetry.mSpeedHistogram, aTelemetry.Speed());
1676 // Only act on errors if we have no usable frames from the decoder.
1677 if (aStatus.mHadError &&
1678 (!mAnimationState || mAnimationState->KnownFrameCount() == 0)) {
1679 DoError();
1680 } else if (aStatus.mWasMetadataDecode && !mHasSize) {
1681 DoError();
1684 // XXX(aosmond): Can we get this far without mFinished == true?
1685 if (aStatus.mFinished && aStatus.mWasMetadataDecode) {
1686 // If we were waiting to fire the load event, go ahead and fire it now.
1687 if (mLoadProgress) {
1688 NotifyForLoadEvent(*mLoadProgress);
1689 mLoadProgress = Nothing();
1692 // If we were a metadata decode and a full decode was requested, do it.
1693 if (mWantFullDecode) {
1694 mWantFullDecode = false;
1695 RequestDecodeForSize(mSize,
1696 DECODE_FLAGS_DEFAULT | FLAG_HIGH_QUALITY_SCALING);
1701 void RasterImage::ReportDecoderError() {
1702 nsCOMPtr<nsIConsoleService> consoleService =
1703 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1704 nsCOMPtr<nsIScriptError> errorObject =
1705 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
1707 if (consoleService && errorObject) {
1708 nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated."));
1709 nsAutoString src;
1710 if (GetURI()) {
1711 nsAutoCString uri;
1712 if (!GetSpecTruncatedTo1k(uri)) {
1713 msg += NS_LITERAL_STRING(" URI in this note truncated due to length.");
1715 src = NS_ConvertUTF8toUTF16(uri);
1717 if (NS_SUCCEEDED(errorObject->InitWithWindowID(msg, src, EmptyString(), 0,
1718 0, nsIScriptError::errorFlag,
1719 "Image", InnerWindowID()))) {
1720 consoleService->LogMessage(errorObject);
1725 already_AddRefed<imgIContainer> RasterImage::Unwrap() {
1726 nsCOMPtr<imgIContainer> self(this);
1727 return self.forget();
1730 void RasterImage::PropagateUseCounters(nsIDocument*) {
1731 // No use counters.
1734 IntSize RasterImage::OptimalImageSizeForDest(const gfxSize& aDest,
1735 uint32_t aWhichFrame,
1736 SamplingFilter aSamplingFilter,
1737 uint32_t aFlags) {
1738 MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
1739 aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
1740 "Unexpected destination size");
1742 if (mSize.IsEmpty() || aDest.IsEmpty()) {
1743 return IntSize(0, 0);
1746 IntSize destSize = IntSize::Ceil(aDest.width, aDest.height);
1748 if (aSamplingFilter == SamplingFilter::GOOD &&
1749 CanDownscaleDuringDecode(destSize, aFlags)) {
1750 return destSize;
1753 // We can't scale to this size. Use our intrinsic size for now.
1754 return mSize;
1757 } // namespace image
1758 } // namespace mozilla