12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- // Must #include ImageLogging.h before any IPDL-generated files or other files
- // that #include prlog.h
- #include "ImageLogging.h"
- #include "RasterImage.h"
- #include "gfxPlatform.h"
- #include "nsComponentManagerUtils.h"
- #include "nsError.h"
- #include "Decoder.h"
- #include "prenv.h"
- #include "prsystem.h"
- #include "IDecodingTask.h"
- #include "ImageContainer.h"
- #include "ImageRegion.h"
- #include "Layers.h"
- #include "LookupResult.h"
- #include "nsIConsoleService.h"
- #include "nsIInputStream.h"
- #include "nsIScriptError.h"
- #include "nsISupportsPrimitives.h"
- #include "nsPresContext.h"
- #include "SourceBuffer.h"
- #include "SurfaceCache.h"
- #include "FrameAnimator.h"
- #include "gfxContext.h"
- #include "mozilla/gfx/2D.h"
- #include "mozilla/DebugOnly.h"
- #include "mozilla/Likely.h"
- #include "mozilla/RefPtr.h"
- #include "mozilla/Move.h"
- #include "mozilla/MemoryReporting.h"
- #include "mozilla/Services.h"
- #include <stdint.h>
- #include "mozilla/TimeStamp.h"
- #include "mozilla/Telemetry.h"
- #include "mozilla/Tuple.h"
- #include "mozilla/ClearOnShutdown.h"
- #include "mozilla/gfx/Scale.h"
- #include "GeckoProfiler.h"
- #include "gfx2DGlue.h"
- #include "gfxPrefs.h"
- #include <algorithm>
- namespace mozilla {
- using namespace gfx;
- using namespace layers;
- namespace image {
- using std::ceil;
- using std::min;
- #ifndef DEBUG
- NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties)
- #else
- NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties,
- imgIContainerDebug)
- #endif
- //******************************************************************************
- RasterImage::RasterImage(ImageURL* aURI /* = nullptr */) :
- ImageResource(aURI), // invoke superclass's constructor
- mSize(0,0),
- mLockCount(0),
- mDecodeCount(0),
- mRequestedSampleSize(0),
- mImageProducerID(ImageContainer::AllocateProducerID()),
- mLastFrameID(0),
- mLastImageContainerDrawResult(DrawResult::NOT_READY),
- #ifdef DEBUG
- mFramesNotified(0),
- #endif
- mSourceBuffer(WrapNotNull(new SourceBuffer())),
- mHasSize(false),
- mTransient(false),
- mSyncLoad(false),
- mDiscardable(false),
- mHasSourceData(false),
- mHasBeenDecoded(false),
- mPendingAnimation(false),
- mAnimationFinished(false),
- mWantFullDecode(false)
- {
- }
- //******************************************************************************
- RasterImage::~RasterImage()
- {
- // Make sure our SourceBuffer is marked as complete. This will ensure that any
- // outstanding decoders terminate.
- if (!mSourceBuffer->IsComplete()) {
- mSourceBuffer->Complete(NS_ERROR_ABORT);
- }
- // Release all frames from the surface cache.
- SurfaceCache::RemoveImage(ImageKey(this));
- }
- nsresult
- RasterImage::Init(const char* aMimeType,
- uint32_t aFlags)
- {
- // We don't support re-initialization
- if (mInitialized) {
- return NS_ERROR_ILLEGAL_VALUE;
- }
- // Not sure an error can happen before init, but be safe
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- // We want to avoid redecodes for transient images.
- MOZ_ASSERT_IF(aFlags & INIT_FLAG_TRANSIENT,
- !(aFlags & INIT_FLAG_DISCARDABLE));
- // Store initialization data
- mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
- mWantFullDecode = !!(aFlags & INIT_FLAG_DECODE_IMMEDIATELY);
- mTransient = !!(aFlags & INIT_FLAG_TRANSIENT);
- mSyncLoad = !!(aFlags & INIT_FLAG_SYNC_LOAD);
- // Use the MIME type to select a decoder type, and make sure there *is* a
- // decoder for this MIME type.
- NS_ENSURE_ARG_POINTER(aMimeType);
- mDecoderType = DecoderFactory::GetDecoderType(aMimeType);
- if (mDecoderType == DecoderType::UNKNOWN) {
- return NS_ERROR_FAILURE;
- }
- // Lock this image's surfaces in the SurfaceCache if we're not discardable.
- if (!mDiscardable) {
- mLockCount++;
- SurfaceCache::LockImage(ImageKey(this));
- }
- if (!mSyncLoad) {
- // Create an async metadata decoder and verify we succeed in doing so.
- nsresult rv = DecodeMetadata(DECODE_FLAGS_DEFAULT);
- if (NS_FAILED(rv)) {
- return NS_ERROR_FAILURE;
- }
- }
- // Mark us as initialized
- mInitialized = true;
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP_(void)
- RasterImage::RequestRefresh(const TimeStamp& aTime)
- {
- if (HadRecentRefresh(aTime)) {
- return;
- }
- EvaluateAnimation();
- if (!mAnimating) {
- return;
- }
- RefreshResult res;
- if (mAnimationState) {
- MOZ_ASSERT(mFrameAnimator);
- res = mFrameAnimator->RequestRefresh(*mAnimationState, aTime);
- }
- if (res.mFrameAdvanced) {
- // Notify listeners that our frame has actually changed, but do this only
- // once for all frames that we've now passed (if AdvanceFrame() was called
- // more than once).
- #ifdef DEBUG
- mFramesNotified++;
- #endif
- NotifyProgress(NoProgress, res.mDirtyRect);
- }
- if (res.mAnimationFinished) {
- mAnimationFinished = true;
- EvaluateAnimation();
- }
- }
- //******************************************************************************
- NS_IMETHODIMP
- RasterImage::GetWidth(int32_t* aWidth)
- {
- NS_ENSURE_ARG_POINTER(aWidth);
- if (mError) {
- *aWidth = 0;
- return NS_ERROR_FAILURE;
- }
- *aWidth = mSize.width;
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP
- RasterImage::GetHeight(int32_t* aHeight)
- {
- NS_ENSURE_ARG_POINTER(aHeight);
- if (mError) {
- *aHeight = 0;
- return NS_ERROR_FAILURE;
- }
- *aHeight = mSize.height;
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP
- RasterImage::GetIntrinsicSize(nsSize* aSize)
- {
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- *aSize = nsSize(nsPresContext::CSSPixelsToAppUnits(mSize.width),
- nsPresContext::CSSPixelsToAppUnits(mSize.height));
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP
- RasterImage::GetIntrinsicRatio(AspectRatio* aRatio)
- {
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- *aRatio = AspectRatio::FromSize(mSize);
- return NS_OK;
- }
- NS_IMETHODIMP_(Orientation)
- RasterImage::GetOrientation()
- {
- return mOrientation;
- }
- //******************************************************************************
- NS_IMETHODIMP
- RasterImage::GetType(uint16_t* aType)
- {
- NS_ENSURE_ARG_POINTER(aType);
- *aType = imgIContainer::TYPE_RASTER;
- return NS_OK;
- }
- LookupResult
- RasterImage::LookupFrameInternal(const IntSize& aSize,
- uint32_t aFlags,
- PlaybackType aPlaybackType)
- {
- if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
- MOZ_ASSERT(mFrameAnimator);
- MOZ_ASSERT(ToSurfaceFlags(aFlags) == DefaultSurfaceFlags(),
- "Can't composite frames with non-default surface flags");
- const size_t index = mAnimationState->GetCurrentAnimationFrameIndex();
- return mFrameAnimator->GetCompositedFrame(index);
- }
- SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
- // We don't want any substitution for sync decodes, and substitution would be
- // illegal when high quality downscaling is disabled, so we use
- // SurfaceCache::Lookup in this case.
- if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
- return SurfaceCache::Lookup(ImageKey(this),
- RasterSurfaceKey(aSize,
- surfaceFlags,
- PlaybackType::eStatic));
- }
- // We'll return the best match we can find to the requested frame.
- return SurfaceCache::LookupBestMatch(ImageKey(this),
- RasterSurfaceKey(aSize,
- surfaceFlags,
- PlaybackType::eStatic));
- }
- DrawableSurface
- RasterImage::LookupFrame(const IntSize& aSize,
- uint32_t aFlags,
- PlaybackType aPlaybackType)
- {
- MOZ_ASSERT(NS_IsMainThread());
- // If we're opaque, we don't need to care about premultiplied alpha, because
- // that can only matter for frames with transparency.
- if (IsOpaque()) {
- aFlags &= ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
- }
- IntSize requestedSize = CanDownscaleDuringDecode(aSize, aFlags)
- ? aSize : mSize;
- if (requestedSize.IsEmpty()) {
- return DrawableSurface(); // Can't decode to a surface of zero size.
- }
- LookupResult result =
- LookupFrameInternal(requestedSize, aFlags, aPlaybackType);
- if (!result && !mHasSize) {
- // We can't request a decode without knowing our intrinsic size. Give up.
- return DrawableSurface();
- }
- if (result.Type() == MatchType::NOT_FOUND ||
- result.Type() == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND ||
- ((aFlags & FLAG_SYNC_DECODE) && !result)) {
- // We don't have a copy of this frame, and there's no decoder working on
- // one. (Or we're sync decoding and the existing decoder hasn't even started
- // yet.) Trigger decoding so it'll be available next time.
- MOZ_ASSERT(aPlaybackType != PlaybackType::eAnimated ||
- !mAnimationState || mAnimationState->KnownFrameCount() < 1,
- "Animated frames should be locked");
- Decode(requestedSize, aFlags, aPlaybackType);
- // If we can sync decode, we should already have the frame.
- if (aFlags & FLAG_SYNC_DECODE) {
- result = LookupFrameInternal(requestedSize, aFlags, aPlaybackType);
- }
- }
- if (!result) {
- // We still weren't able to get a frame. Give up.
- return DrawableSurface();
- }
- if (result.Surface()->GetCompositingFailed()) {
- return DrawableSurface();
- }
- MOZ_ASSERT(!result.Surface()->GetIsPaletted(),
- "Should not have a paletted frame");
- // Sync decoding guarantees that we got the frame, but if it's owned by an
- // async decoder that's currently running, the contents of the frame may not
- // be available yet. Make sure we get everything.
- if (mHasSourceData && (aFlags & FLAG_SYNC_DECODE)) {
- result.Surface()->WaitUntilFinished();
- }
- // If we could have done some decoding in this function we need to check if
- // that decoding encountered an error and hence aborted the surface. We want
- // to avoid calling IsAborted if we weren't passed any sync decode flag because
- // IsAborted acquires the monitor for the imgFrame.
- if (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST) &&
- result.Surface()->IsAborted()) {
- return DrawableSurface();
- }
- return Move(result.Surface());
- }
- bool
- RasterImage::IsOpaque()
- {
- if (mError) {
- return false;
- }
- Progress progress = mProgressTracker->GetProgress();
- // If we haven't yet finished decoding, the safe answer is "not opaque".
- if (!(progress & FLAG_DECODE_COMPLETE)) {
- return false;
- }
- // Other, we're opaque if FLAG_HAS_TRANSPARENCY is not set.
- return !(progress & FLAG_HAS_TRANSPARENCY);
- }
- NS_IMETHODIMP_(bool)
- RasterImage::WillDrawOpaqueNow()
- {
- if (!IsOpaque()) {
- return false;
- }
- if (mAnimationState) {
- // We never discard frames of animated images.
- return true;
- }
- // If we are not locked our decoded data could get discard at any time (ie
- // between the call to this function and when we are asked to draw), so we
- // have to return false if we are unlocked.
- if (IsUnlocked()) {
- return false;
- }
- LookupResult result =
- SurfaceCache::LookupBestMatch(ImageKey(this),
- RasterSurfaceKey(mSize,
- DefaultSurfaceFlags(),
- PlaybackType::eStatic));
- MatchType matchType = result.Type();
- if (matchType == MatchType::NOT_FOUND || matchType == MatchType::PENDING ||
- !result.Surface()->IsFinished()) {
- return false;
- }
- return true;
- }
- void
- RasterImage::OnSurfaceDiscarded()
- {
- MOZ_ASSERT(mProgressTracker);
- NS_DispatchToMainThread(NewRunnableMethod(mProgressTracker, &ProgressTracker::OnDiscard));
- }
- //******************************************************************************
- NS_IMETHODIMP
- RasterImage::GetAnimated(bool* aAnimated)
- {
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- NS_ENSURE_ARG_POINTER(aAnimated);
- // If we have an AnimationState, we can know for sure.
- if (mAnimationState) {
- *aAnimated = true;
- return NS_OK;
- }
- // Otherwise, we need to have been decoded to know for sure, since if we were
- // decoded at least once mAnimationState would have been created for animated
- // images. This is true even though we check for animation during the
- // metadata decode, because we may still discover animation only during the
- // full decode for corrupt images.
- if (!mHasBeenDecoded) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- // We know for sure
- *aAnimated = false;
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP_(int32_t)
- RasterImage::GetFirstFrameDelay()
- {
- if (mError) {
- return -1;
- }
- bool animated = false;
- if (NS_FAILED(GetAnimated(&animated)) || !animated) {
- return -1;
- }
- MOZ_ASSERT(mAnimationState, "Animated images should have an AnimationState");
- return mAnimationState->FirstFrameTimeout().AsEncodedValueDeprecated();
- }
- NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
- RasterImage::GetFrame(uint32_t aWhichFrame,
- uint32_t aFlags)
- {
- return GetFrameInternal(mSize, aWhichFrame, aFlags).second().forget();
- }
- NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
- RasterImage::GetFrameAtSize(const IntSize& aSize,
- uint32_t aWhichFrame,
- uint32_t aFlags)
- {
- return GetFrameInternal(aSize, aWhichFrame, aFlags).second().forget();
- }
- Pair<DrawResult, RefPtr<SourceSurface>>
- RasterImage::GetFrameInternal(const IntSize& aSize,
- uint32_t aWhichFrame,
- uint32_t aFlags)
- {
- MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
- if (aSize.IsEmpty()) {
- return MakePair(DrawResult::BAD_ARGS, RefPtr<SourceSurface>());
- }
- if (aWhichFrame > FRAME_MAX_VALUE) {
- return MakePair(DrawResult::BAD_ARGS, RefPtr<SourceSurface>());
- }
- if (mError) {
- return MakePair(DrawResult::BAD_IMAGE, RefPtr<SourceSurface>());
- }
- // Get the frame. If it's not there, it's probably the caller's fault for
- // not waiting for the data to be loaded from the network or not passing
- // FLAG_SYNC_DECODE.
- DrawableSurface surface =
- LookupFrame(aSize, aFlags, ToPlaybackType(aWhichFrame));
- if (!surface) {
- // The OS threw this frame away and we couldn't redecode it.
- return MakePair(DrawResult::TEMPORARY_ERROR, RefPtr<SourceSurface>());
- }
- RefPtr<SourceSurface> sourceSurface = surface->GetSourceSurface();
- if (!surface->IsFinished()) {
- return MakePair(DrawResult::INCOMPLETE, Move(sourceSurface));
- }
- return MakePair(DrawResult::SUCCESS, Move(sourceSurface));
- }
- Pair<DrawResult, RefPtr<layers::Image>>
- RasterImage::GetCurrentImage(ImageContainer* aContainer, uint32_t aFlags)
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(aContainer);
- DrawResult drawResult;
- RefPtr<SourceSurface> surface;
- Tie(drawResult, surface) =
- GetFrameInternal(mSize, FRAME_CURRENT, aFlags | FLAG_ASYNC_NOTIFY);
- if (!surface) {
- // The OS threw out some or all of our buffer. We'll need to wait for the
- // redecode (which was automatically triggered by GetFrame) to complete.
- return MakePair(drawResult, RefPtr<layers::Image>());
- }
- RefPtr<layers::Image> image = new layers::SourceSurfaceImage(surface);
- return MakePair(drawResult, Move(image));
- }
- NS_IMETHODIMP_(bool)
- RasterImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
- {
- int32_t maxTextureSize = aManager->GetMaxTextureSize();
- if (!mHasSize ||
- mSize.width > maxTextureSize ||
- mSize.height > maxTextureSize) {
- return false;
- }
- return true;
- }
- NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
- RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(aManager);
- MOZ_ASSERT((aFlags & ~(FLAG_SYNC_DECODE |
- FLAG_SYNC_DECODE_IF_FAST |
- FLAG_ASYNC_NOTIFY))
- == FLAG_NONE,
- "Unsupported flag passed to GetImageContainer");
- int32_t maxTextureSize = aManager->GetMaxTextureSize();
- if (!mHasSize ||
- mSize.width > maxTextureSize ||
- mSize.height > maxTextureSize) {
- return nullptr;
- }
- if (IsUnlocked() && mProgressTracker) {
- mProgressTracker->OnUnlockedDraw();
- }
- RefPtr<layers::ImageContainer> container = mImageContainer.get();
- bool mustRedecode =
- (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST)) &&
- mLastImageContainerDrawResult != DrawResult::SUCCESS &&
- mLastImageContainerDrawResult != DrawResult::BAD_IMAGE;
- if (container && !mustRedecode) {
- return container.forget();
- }
- // We need a new ImageContainer, so create one.
- container = LayerManager::CreateImageContainer();
- DrawResult drawResult;
- RefPtr<layers::Image> image;
- Tie(drawResult, image) = GetCurrentImage(container, aFlags);
- if (!image) {
- return nullptr;
- }
- // |image| holds a reference to a SourceSurface which in turn holds a lock on
- // the current frame's VolatileBuffer, ensuring that it doesn't get freed as
- // long as the layer system keeps this ImageContainer alive.
- AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
- imageList.AppendElement(ImageContainer::NonOwningImage(image, TimeStamp(),
- mLastFrameID++,
- mImageProducerID));
- container->SetCurrentImagesInTransaction(imageList);
- mLastImageContainerDrawResult = drawResult;
- mImageContainer = container;
- return container.forget();
- }
- void
- RasterImage::UpdateImageContainer()
- {
- MOZ_ASSERT(NS_IsMainThread());
- RefPtr<layers::ImageContainer> container = mImageContainer.get();
- if (!container) {
- return;
- }
- DrawResult drawResult;
- RefPtr<layers::Image> image;
- Tie(drawResult, image) = GetCurrentImage(container, FLAG_NONE);
- if (!image) {
- return;
- }
- mLastImageContainerDrawResult = drawResult;
- AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
- imageList.AppendElement(ImageContainer::NonOwningImage(image, TimeStamp(),
- mLastFrameID++,
- mImageProducerID));
- container->SetCurrentImages(imageList);
- }
- size_t
- RasterImage::SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
- {
- return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(aMallocSizeOf);
- }
- void
- RasterImage::CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
- MallocSizeOf aMallocSizeOf) const
- {
- SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
- if (mFrameAnimator) {
- mFrameAnimator->CollectSizeOfCompositingSurfaces(aCounters, aMallocSizeOf);
- }
- }
- bool
- RasterImage::SetMetadata(const ImageMetadata& aMetadata,
- bool aFromMetadataDecode)
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (mError) {
- return true;
- }
- if (aMetadata.HasSize()) {
- IntSize size = aMetadata.GetSize();
- if (size.width < 0 || size.height < 0) {
- NS_WARNING("Image has negative intrinsic size");
- DoError();
- return true;
- }
- MOZ_ASSERT(aMetadata.HasOrientation());
- Orientation orientation = aMetadata.GetOrientation();
- // If we already have a size, check the new size against the old one.
- if (mHasSize && (size != mSize || orientation != mOrientation)) {
- NS_WARNING("Image changed size or orientation on redecode! "
- "This should not happen!");
- DoError();
- return true;
- }
- // Set the size and flag that we have it.
- mSize = size;
- mOrientation = orientation;
- mHasSize = true;
- }
- if (mHasSize && aMetadata.HasAnimation() && !mAnimationState) {
- // We're becoming animated, so initialize animation stuff.
- mAnimationState.emplace(mAnimationMode);
- mFrameAnimator = MakeUnique<FrameAnimator>(this, mSize);
- // We don't support discarding animated images (See bug 414259).
- // Lock the image and throw away the key.
- LockImage();
- if (!aFromMetadataDecode) {
- // The metadata decode reported that this image isn't animated, but we
- // discovered that it actually was during the full decode. This is a
- // rare failure that only occurs for corrupt images. To recover, we need
- // to discard all existing surfaces and redecode.
- return false;
- }
- }
- if (mAnimationState) {
- mAnimationState->SetLoopCount(aMetadata.GetLoopCount());
- mAnimationState->SetFirstFrameTimeout(aMetadata.GetFirstFrameTimeout());
- if (aMetadata.HasLoopLength()) {
- mAnimationState->SetLoopLength(aMetadata.GetLoopLength());
- }
- if (aMetadata.HasFirstFrameRefreshArea()) {
- mAnimationState
- ->SetFirstFrameRefreshArea(aMetadata.GetFirstFrameRefreshArea());
- }
- }
- if (aMetadata.HasHotspot()) {
- IntPoint hotspot = aMetadata.GetHotspot();
- nsCOMPtr<nsISupportsPRUint32> intwrapx =
- do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
- nsCOMPtr<nsISupportsPRUint32> intwrapy =
- do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
- intwrapx->SetData(hotspot.x);
- intwrapy->SetData(hotspot.y);
- Set("hotspotX", intwrapx);
- Set("hotspotY", intwrapy);
- }
- return true;
- }
- NS_IMETHODIMP
- RasterImage::SetAnimationMode(uint16_t aAnimationMode)
- {
- if (mAnimationState) {
- mAnimationState->SetAnimationMode(aAnimationMode);
- }
- return SetAnimationModeInternal(aAnimationMode);
- }
- //******************************************************************************
- nsresult
- RasterImage::StartAnimation()
- {
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
- // If we're not ready to animate, then set mPendingAnimation, which will cause
- // us to start animating if and when we do become ready.
- mPendingAnimation = !mAnimationState || mAnimationState->KnownFrameCount() < 1;
- if (mPendingAnimation) {
- return NS_OK;
- }
- // Don't bother to animate if we're displaying the first frame forever.
- if (mAnimationState->GetCurrentAnimationFrameIndex() == 0 &&
- mAnimationState->FirstFrameTimeout() == FrameTimeout::Forever()) {
- mAnimationFinished = true;
- return NS_ERROR_ABORT;
- }
- // We need to set the time that this initial frame was first displayed, as
- // this is used in AdvanceFrame().
- mAnimationState->InitAnimationFrameTimeIfNecessary();
- return NS_OK;
- }
- //******************************************************************************
- nsresult
- RasterImage::StopAnimation()
- {
- MOZ_ASSERT(mAnimating, "Should be animating!");
- nsresult rv = NS_OK;
- if (mError) {
- rv = NS_ERROR_FAILURE;
- } else {
- mAnimationState->SetAnimationFrameTime(TimeStamp());
- }
- mAnimating = false;
- return rv;
- }
- //******************************************************************************
- NS_IMETHODIMP
- RasterImage::ResetAnimation()
- {
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- mPendingAnimation = false;
- if (mAnimationMode == kDontAnimMode || !mAnimationState ||
- mAnimationState->GetCurrentAnimationFrameIndex() == 0) {
- return NS_OK;
- }
- mAnimationFinished = false;
- if (mAnimating) {
- StopAnimation();
- }
- MOZ_ASSERT(mAnimationState, "Should have AnimationState");
- mAnimationState->ResetAnimation();
- NotifyProgress(NoProgress, mAnimationState->FirstFrameRefreshArea());
- // Start the animation again. It may not have been running before, if
- // mAnimationFinished was true before entering this function.
- EvaluateAnimation();
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP_(void)
- RasterImage::SetAnimationStartTime(const TimeStamp& aTime)
- {
- if (mError || mAnimationMode == kDontAnimMode || mAnimating || !mAnimationState) {
- return;
- }
- mAnimationState->SetAnimationFrameTime(aTime);
- }
- NS_IMETHODIMP_(float)
- RasterImage::GetFrameIndex(uint32_t aWhichFrame)
- {
- MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
- return (aWhichFrame == FRAME_FIRST || !mAnimationState)
- ? 0.0f
- : mAnimationState->GetCurrentAnimationFrameIndex();
- }
- NS_IMETHODIMP_(IntRect)
- RasterImage::GetImageSpaceInvalidationRect(const IntRect& aRect)
- {
- return aRect;
- }
- nsresult
- RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult aStatus,
- bool aLastPart)
- {
- MOZ_ASSERT(NS_IsMainThread());
- // Record that we have all the data we're going to get now.
- mHasSourceData = true;
- // Let decoders know that there won't be any more data coming.
- mSourceBuffer->Complete(aStatus);
- // Allow a synchronous metadata decode if mSyncLoad was set, or if we're
- // running on a single thread (in which case waiting for the async metadata
- // decoder could delay this image's load event quite a bit), or if this image
- // is transient.
- bool canSyncDecodeMetadata = mSyncLoad || mTransient ||
- DecodePool::NumberOfCores() < 2;
- if (canSyncDecodeMetadata && !mHasSize) {
- // We're loading this image synchronously, so it needs to be usable after
- // this call returns. Since we haven't gotten our size yet, we need to do a
- // synchronous metadata decode here.
- DecodeMetadata(FLAG_SYNC_DECODE);
- }
- // Determine our final status, giving precedence to Necko failure codes. We
- // check after running the metadata decode in case it triggered an error.
- nsresult finalStatus = mError ? NS_ERROR_FAILURE : NS_OK;
- if (NS_FAILED(aStatus)) {
- finalStatus = aStatus;
- }
- // If loading failed, report an error.
- if (NS_FAILED(finalStatus)) {
- DoError();
- }
- Progress loadProgress = LoadCompleteProgress(aLastPart, mError, finalStatus);
- if (!mHasSize && !mError) {
- // We don't have our size yet, so we'll fire the load event in SetSize().
- MOZ_ASSERT(!canSyncDecodeMetadata,
- "Firing load async after metadata sync decode?");
- NotifyProgress(FLAG_ONLOAD_BLOCKED);
- mLoadProgress = Some(loadProgress);
- return finalStatus;
- }
- NotifyForLoadEvent(loadProgress);
- return finalStatus;
- }
- void
- RasterImage::NotifyForLoadEvent(Progress aProgress)
- {
- MOZ_ASSERT(mHasSize || mError, "Need to know size before firing load event");
- MOZ_ASSERT(!mHasSize ||
- (mProgressTracker->GetProgress() & FLAG_SIZE_AVAILABLE),
- "Should have notified that the size is available if we have it");
- // If we encountered an error, make sure we notify for that as well.
- if (mError) {
- aProgress |= FLAG_HAS_ERROR;
- }
- // Notify our listeners, which will fire this image's load event.
- NotifyProgress(aProgress);
- }
- nsresult
- RasterImage::OnImageDataAvailable(nsIRequest*,
- nsISupports*,
- nsIInputStream* aInputStream,
- uint64_t,
- uint32_t aCount)
- {
- nsresult rv = mSourceBuffer->AppendFromInputStream(aInputStream, aCount);
- if (NS_FAILED(rv)) {
- DoError();
- }
- return rv;
- }
- nsresult
- RasterImage::SetSourceSizeHint(uint32_t aSizeHint)
- {
- return mSourceBuffer->ExpectLength(aSizeHint);
- }
- /********* Methods to implement lazy allocation of nsIProperties object *******/
- NS_IMETHODIMP
- RasterImage::Get(const char* prop, const nsIID& iid, void** result)
- {
- if (!mProperties) {
- return NS_ERROR_FAILURE;
- }
- return mProperties->Get(prop, iid, result);
- }
- NS_IMETHODIMP
- RasterImage::Set(const char* prop, nsISupports* value)
- {
- if (!mProperties) {
- mProperties = do_CreateInstance("@mozilla.org/properties;1");
- }
- if (!mProperties) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- return mProperties->Set(prop, value);
- }
- NS_IMETHODIMP
- RasterImage::Has(const char* prop, bool* _retval)
- {
- NS_ENSURE_ARG_POINTER(_retval);
- if (!mProperties) {
- *_retval = false;
- return NS_OK;
- }
- return mProperties->Has(prop, _retval);
- }
- NS_IMETHODIMP
- RasterImage::Undefine(const char* prop)
- {
- if (!mProperties) {
- return NS_ERROR_FAILURE;
- }
- return mProperties->Undefine(prop);
- }
- NS_IMETHODIMP
- RasterImage::GetKeys(uint32_t* count, char*** keys)
- {
- if (!mProperties) {
- *count = 0;
- *keys = nullptr;
- return NS_OK;
- }
- return mProperties->GetKeys(count, keys);
- }
- void
- RasterImage::Discard()
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
- MOZ_ASSERT(!mAnimationState, "Asked to discard for animated image");
- // Delete all the decoded frames.
- SurfaceCache::RemoveImage(ImageKey(this));
- // Notify that we discarded.
- if (mProgressTracker) {
- mProgressTracker->OnDiscard();
- }
- }
- bool
- RasterImage::CanDiscard() {
- return mHasSourceData && // ...have the source data...
- !mAnimationState; // Can never discard animated images
- }
- NS_IMETHODIMP
- RasterImage::StartDecoding()
- {
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- if (!mHasSize) {
- mWantFullDecode = true;
- return NS_OK;
- }
- return RequestDecodeForSize(mSize, FLAG_SYNC_DECODE_IF_FAST);
- }
- NS_IMETHODIMP
- RasterImage::RequestDecodeForSize(const IntSize& aSize, uint32_t aFlags)
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- if (!mHasSize) {
- return NS_OK;
- }
- // Decide whether to sync decode images we can decode quickly. Here we are
- // explicitly trading off flashing for responsiveness in the case that we're
- // redecoding an image (see bug 845147).
- bool shouldSyncDecodeIfFast =
- !mHasBeenDecoded && (aFlags & FLAG_SYNC_DECODE_IF_FAST);
- uint32_t flags = shouldSyncDecodeIfFast
- ? aFlags
- : aFlags & ~FLAG_SYNC_DECODE_IF_FAST;
- // Perform a frame lookup, which will implicitly start decoding if needed.
- LookupFrame(aSize, flags, mAnimationState ? PlaybackType::eAnimated
- : PlaybackType::eStatic);
- return NS_OK;
- }
- static void
- LaunchDecodingTask(IDecodingTask* aTask,
- RasterImage* aImage,
- uint32_t aFlags,
- bool aHaveSourceData)
- {
- if (aHaveSourceData) {
- // If we have all the data, we can sync decode if requested.
- if (aFlags & imgIContainer::FLAG_SYNC_DECODE) {
- PROFILER_LABEL_PRINTF("DecodePool", "SyncRunIfPossible",
- js::ProfileEntry::Category::GRAPHICS,
- "%s", aImage->GetURIString().get());
- DecodePool::Singleton()->SyncRunIfPossible(aTask);
- return;
- }
- if (aFlags & imgIContainer::FLAG_SYNC_DECODE_IF_FAST) {
- PROFILER_LABEL_PRINTF("DecodePool", "SyncRunIfPreferred",
- js::ProfileEntry::Category::GRAPHICS,
- "%s", aImage->GetURIString().get());
- DecodePool::Singleton()->SyncRunIfPreferred(aTask);
- return;
- }
- }
- // Perform an async decode. We also take this path if we don't have all the
- // source data yet, since sync decoding is impossible in that situation.
- DecodePool::Singleton()->AsyncRun(aTask);
- }
- NS_IMETHODIMP
- RasterImage::Decode(const IntSize& aSize,
- uint32_t aFlags,
- PlaybackType aPlaybackType)
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- // If we don't have a size yet, we can't do any other decoding.
- if (!mHasSize) {
- mWantFullDecode = true;
- return NS_OK;
- }
- // We're about to decode again, which may mean that some of the previous sizes
- // we've decoded at aren't useful anymore. We can allow them to expire from
- // the cache by unlocking them here. When the decode finishes, it will send an
- // invalidation that will cause all instances of this image to redraw. If this
- // image is locked, any surfaces that are still useful will become locked
- // again when LookupFrame touches them, and the remainder will eventually
- // expire.
- SurfaceCache::UnlockEntries(ImageKey(this));
- // Determine which flags we need to decode this image with.
- DecoderFlags decoderFlags = DefaultDecoderFlags();
- if (aFlags & FLAG_ASYNC_NOTIFY) {
- decoderFlags |= DecoderFlags::ASYNC_NOTIFY;
- }
- if (mTransient) {
- decoderFlags |= DecoderFlags::IMAGE_IS_TRANSIENT;
- }
- if (mHasBeenDecoded) {
- decoderFlags |= DecoderFlags::IS_REDECODE;
- }
- SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
- if (IsOpaque()) {
- // If there's no transparency, it doesn't matter whether we premultiply
- // alpha or not.
- surfaceFlags &= ~SurfaceFlags::NO_PREMULTIPLY_ALPHA;
- }
- // Create a decoder.
- RefPtr<IDecodingTask> task;
- if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
- task = DecoderFactory::CreateAnimationDecoder(mDecoderType, WrapNotNull(this),
- mSourceBuffer, mSize,
- decoderFlags, surfaceFlags);
- } else {
- task = DecoderFactory::CreateDecoder(mDecoderType, WrapNotNull(this),
- mSourceBuffer, mSize, aSize,
- decoderFlags, surfaceFlags,
- mRequestedSampleSize);
- }
- // Make sure DecoderFactory was able to create a decoder successfully.
- if (!task) {
- return NS_ERROR_FAILURE;
- }
- mDecodeCount++;
- // We're ready to decode; start the decoder.
- LaunchDecodingTask(task, this, aFlags, mHasSourceData);
- return NS_OK;
- }
- NS_IMETHODIMP
- RasterImage::DecodeMetadata(uint32_t aFlags)
- {
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- MOZ_ASSERT(!mHasSize, "Should not do unnecessary metadata decodes");
- // Create a decoder.
- RefPtr<IDecodingTask> task =
- DecoderFactory::CreateMetadataDecoder(mDecoderType, WrapNotNull(this),
- mSourceBuffer, mRequestedSampleSize);
- // Make sure DecoderFactory was able to create a decoder successfully.
- if (!task) {
- return NS_ERROR_FAILURE;
- }
- // We're ready to decode; start the decoder.
- LaunchDecodingTask(task, this, aFlags, mHasSourceData);
- return NS_OK;
- }
- void
- RasterImage::RecoverFromInvalidFrames(const IntSize& aSize, uint32_t aFlags)
- {
- if (!mHasSize) {
- return;
- }
- NS_WARNING("A RasterImage's frames became invalid. Attempting to recover...");
- // Discard all existing frames, since they're probably all now invalid.
- SurfaceCache::RemoveImage(ImageKey(this));
- // Relock the image if it's supposed to be locked.
- if (mLockCount > 0) {
- SurfaceCache::LockImage(ImageKey(this));
- }
- // Animated images require some special handling, because we normally require
- // that they never be discarded.
- if (mAnimationState) {
- Decode(mSize, aFlags | FLAG_SYNC_DECODE, PlaybackType::eAnimated);
- ResetAnimation();
- return;
- }
- // For non-animated images, it's fine to recover using an async decode.
- Decode(aSize, aFlags, PlaybackType::eStatic);
- }
- static bool
- HaveSkia()
- {
- #ifdef MOZ_ENABLE_SKIA
- return true;
- #else
- return false;
- #endif
- }
- bool
- RasterImage::CanDownscaleDuringDecode(const IntSize& aSize, uint32_t aFlags)
- {
- // Check basic requirements: downscale-during-decode is enabled, Skia is
- // available, this image isn't transient, we have all the source data and know
- // our size, and the flags allow us to do it.
- if (!mHasSize || mTransient || !HaveSkia() ||
- !gfxPrefs::ImageDownscaleDuringDecodeEnabled() ||
- !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
- return false;
- }
- // We don't downscale animated images during decode.
- if (mAnimationState) {
- return false;
- }
- // Never upscale.
- if (aSize.width >= mSize.width || aSize.height >= mSize.height) {
- return false;
- }
- // Zero or negative width or height is unacceptable.
- if (aSize.width < 1 || aSize.height < 1) {
- return false;
- }
- // There's no point in scaling if we can't store the result.
- if (!SurfaceCache::CanHold(aSize)) {
- return false;
- }
- return true;
- }
- DrawResult
- RasterImage::DrawInternal(DrawableSurface&& aSurface,
- gfxContext* aContext,
- const IntSize& aSize,
- const ImageRegion& aRegion,
- SamplingFilter aSamplingFilter,
- uint32_t aFlags)
- {
- gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
- ImageRegion region(aRegion);
- bool frameIsFinished = aSurface->IsFinished();
- // By now we may have a frame with the requested size. If not, we need to
- // adjust the drawing parameters accordingly.
- IntSize finalSize = aSurface->GetImageSize();
- bool couldRedecodeForBetterFrame = false;
- if (finalSize != aSize) {
- gfx::Size scale(double(aSize.width) / finalSize.width,
- double(aSize.height) / finalSize.height);
- aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
- region.Scale(1.0 / scale.width, 1.0 / scale.height);
- couldRedecodeForBetterFrame = CanDownscaleDuringDecode(aSize, aFlags);
- }
- if (!aSurface->Draw(aContext, region, aSamplingFilter, aFlags)) {
- RecoverFromInvalidFrames(aSize, aFlags);
- return DrawResult::TEMPORARY_ERROR;
- }
- if (!frameIsFinished) {
- return DrawResult::INCOMPLETE;
- }
- if (couldRedecodeForBetterFrame) {
- return DrawResult::WRONG_SIZE;
- }
- return DrawResult::SUCCESS;
- }
- //******************************************************************************
- NS_IMETHODIMP_(DrawResult)
- RasterImage::Draw(gfxContext* aContext,
- const IntSize& aSize,
- const ImageRegion& aRegion,
- uint32_t aWhichFrame,
- SamplingFilter aSamplingFilter,
- const Maybe<SVGImageContext>& /*aSVGContext - ignored*/,
- uint32_t aFlags)
- {
- if (aWhichFrame > FRAME_MAX_VALUE) {
- return DrawResult::BAD_ARGS;
- }
- if (mError) {
- return DrawResult::BAD_IMAGE;
- }
- // Illegal -- you can't draw with non-default decode flags.
- // (Disabling colorspace conversion might make sense to allow, but
- // we don't currently.)
- if (ToSurfaceFlags(aFlags) != DefaultSurfaceFlags()) {
- return DrawResult::BAD_ARGS;
- }
- if (!aContext) {
- return DrawResult::BAD_ARGS;
- }
- if (IsUnlocked() && mProgressTracker) {
- mProgressTracker->OnUnlockedDraw();
- }
- // If we're not using SamplingFilter::GOOD, we shouldn't high-quality scale or
- // downscale during decode.
- uint32_t flags = aSamplingFilter == SamplingFilter::GOOD
- ? aFlags
- : aFlags & ~FLAG_HIGH_QUALITY_SCALING;
- DrawableSurface surface =
- LookupFrame(aSize, flags, ToPlaybackType(aWhichFrame));
- if (!surface) {
- // Getting the frame (above) touches the image and kicks off decoding.
- if (mDrawStartTime.IsNull()) {
- mDrawStartTime = TimeStamp::Now();
- }
- return DrawResult::NOT_READY;
- }
- auto result = DrawInternal(Move(surface), aContext, aSize,
- aRegion, aSamplingFilter, flags);
- return result;
- }
- //******************************************************************************
- NS_IMETHODIMP
- RasterImage::LockImage()
- {
- MOZ_ASSERT(NS_IsMainThread(),
- "Main thread to encourage serialization with UnlockImage");
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- // Increment the lock count
- mLockCount++;
- // Lock this image's surfaces in the SurfaceCache.
- if (mLockCount == 1) {
- SurfaceCache::LockImage(ImageKey(this));
- }
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP
- RasterImage::UnlockImage()
- {
- MOZ_ASSERT(NS_IsMainThread(),
- "Main thread to encourage serialization with LockImage");
- if (mError) {
- return NS_ERROR_FAILURE;
- }
- // It's an error to call this function if the lock count is 0
- MOZ_ASSERT(mLockCount > 0,
- "Calling UnlockImage with mLockCount == 0!");
- if (mLockCount == 0) {
- return NS_ERROR_ABORT;
- }
- // Decrement our lock count
- mLockCount--;
- // Unlock this image's surfaces in the SurfaceCache.
- if (mLockCount == 0 ) {
- SurfaceCache::UnlockImage(ImageKey(this));
- }
- return NS_OK;
- }
- //******************************************************************************
- NS_IMETHODIMP
- RasterImage::RequestDiscard()
- {
- if (mDiscardable && // Enabled at creation time...
- mLockCount == 0 && // ...not temporarily disabled...
- CanDiscard()) {
- Discard();
- }
- return NS_OK;
- }
- // Indempotent error flagging routine. If a decoder is open, shuts it down.
- void
- RasterImage::DoError()
- {
- // If we've flagged an error before, we have nothing to do
- if (mError) {
- return;
- }
- // We can't safely handle errors off-main-thread, so dispatch a worker to
- // do it.
- if (!NS_IsMainThread()) {
- HandleErrorWorker::DispatchIfNeeded(this);
- return;
- }
- // Put the container in an error state.
- mError = true;
- // Stop animation and release our FrameAnimator.
- if (mAnimating) {
- StopAnimation();
- }
- mAnimationState = Nothing();
- mFrameAnimator = nullptr;
- // Release all locks.
- mLockCount = 0;
- SurfaceCache::UnlockImage(ImageKey(this));
- // Release all frames from the surface cache.
- SurfaceCache::RemoveImage(ImageKey(this));
- // Invalidate to get rid of any partially-drawn image content.
- NotifyProgress(NoProgress, IntRect(0, 0, mSize.width, mSize.height));
- MOZ_LOG(gImgLog, LogLevel::Error,
- ("RasterImage: [this=%p] Error detected for image\n", this));
- }
- /* static */ void
- RasterImage::HandleErrorWorker::DispatchIfNeeded(RasterImage* aImage)
- {
- RefPtr<HandleErrorWorker> worker = new HandleErrorWorker(aImage);
- NS_DispatchToMainThread(worker);
- }
- RasterImage::HandleErrorWorker::HandleErrorWorker(RasterImage* aImage)
- : mImage(aImage)
- {
- MOZ_ASSERT(mImage, "Should have image");
- }
- NS_IMETHODIMP
- RasterImage::HandleErrorWorker::Run()
- {
- mImage->DoError();
- return NS_OK;
- }
- bool
- RasterImage::ShouldAnimate()
- {
- return ImageResource::ShouldAnimate() &&
- mAnimationState &&
- mAnimationState->KnownFrameCount() >= 1 &&
- !mAnimationFinished;
- }
- #ifdef DEBUG
- NS_IMETHODIMP
- RasterImage::GetFramesNotified(uint32_t* aFramesNotified)
- {
- NS_ENSURE_ARG_POINTER(aFramesNotified);
- *aFramesNotified = mFramesNotified;
- return NS_OK;
- }
- #endif
- void
- RasterImage::NotifyProgress(Progress aProgress,
- const IntRect& aInvalidRect /* = IntRect() */,
- const Maybe<uint32_t>& aFrameCount /* = Nothing() */,
- DecoderFlags aDecoderFlags
- /* = DefaultDecoderFlags() */,
- SurfaceFlags aSurfaceFlags
- /* = DefaultSurfaceFlags() */)
- {
- MOZ_ASSERT(NS_IsMainThread());
- // Ensure that we stay alive long enough to finish notifying.
- RefPtr<RasterImage> image = this;
- const bool wasDefaultFlags = aSurfaceFlags == DefaultSurfaceFlags();
- if (!aInvalidRect.IsEmpty() && wasDefaultFlags) {
- // Update our image container since we're invalidating.
- UpdateImageContainer();
- }
- if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY)) {
- // We may have decoded new animation frames; update our animation state.
- MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError);
- if (mAnimationState && aFrameCount) {
- mAnimationState->UpdateKnownFrameCount(*aFrameCount);
- }
- // If we should start animating right now, do so.
- if (mAnimationState && aFrameCount == Some(1u) &&
- mPendingAnimation && ShouldAnimate()) {
- StartAnimation();
- }
- }
- // Tell the observers what happened.
- image->mProgressTracker->SyncNotifyProgress(aProgress, aInvalidRect);
- }
- void
- RasterImage::NotifyDecodeComplete(const DecoderFinalStatus& aStatus,
- const ImageMetadata& aMetadata,
- const DecoderTelemetry& aTelemetry,
- Progress aProgress,
- const IntRect& aInvalidRect,
- const Maybe<uint32_t>& aFrameCount,
- DecoderFlags aDecoderFlags,
- SurfaceFlags aSurfaceFlags)
- {
- MOZ_ASSERT(NS_IsMainThread());
- // If the decoder detected an error, log it to the error console.
- if (aStatus.mShouldReportError) {
- ReportDecoderError();
- }
- // Record all the metadata the decoder gathered about this image.
- bool metadataOK = SetMetadata(aMetadata, aStatus.mWasMetadataDecode);
- if (!metadataOK) {
- // This indicates a serious error that requires us to discard all existing
- // surfaces and redecode to recover. We'll drop the results from this
- // decoder on the floor, since they aren't valid.
- RecoverFromInvalidFrames(mSize,
- FromSurfaceFlags(aSurfaceFlags));
- return;
- }
- MOZ_ASSERT(mError || mHasSize || !aMetadata.HasSize(),
- "SetMetadata should've gotten a size");
- if (!aStatus.mWasMetadataDecode && aStatus.mFinished) {
- // Flag that we've been decoded before.
- mHasBeenDecoded = true;
- }
- // Send out any final notifications.
- NotifyProgress(aProgress, aInvalidRect, aFrameCount,
- aDecoderFlags, aSurfaceFlags);
- if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY) &&
- mHasBeenDecoded && mAnimationState) {
- // We've finished a full decode of all animation frames and our AnimationState
- // has been notified about them all, so let it know not to expect anymore.
- mAnimationState->SetDoneDecoding(true);
- }
- // Only act on errors if we have no usable frames from the decoder.
- if (aStatus.mHadError &&
- (!mAnimationState || mAnimationState->KnownFrameCount() == 0)) {
- DoError();
- } else if (aStatus.mWasMetadataDecode && !mHasSize) {
- DoError();
- }
- // XXX(aosmond): Can we get this far without mFinished == true?
- if (aStatus.mFinished && aStatus.mWasMetadataDecode) {
- // If we were waiting to fire the load event, go ahead and fire it now.
- if (mLoadProgress) {
- NotifyForLoadEvent(*mLoadProgress);
- mLoadProgress = Nothing();
- NotifyProgress(FLAG_ONLOAD_UNBLOCKED);
- }
- // If we were a metadata decode and a full decode was requested, do it.
- if (mWantFullDecode) {
- mWantFullDecode = false;
- RequestDecodeForSize(mSize, DECODE_FLAGS_DEFAULT);
- }
- }
- }
- void
- RasterImage::ReportDecoderError()
- {
- nsCOMPtr<nsIConsoleService> consoleService =
- do_GetService(NS_CONSOLESERVICE_CONTRACTID);
- nsCOMPtr<nsIScriptError> errorObject =
- do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
- if (consoleService && errorObject) {
- nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated."));
- nsAutoString src;
- if (GetURI()) {
- nsCString uri;
- if (GetURI()->GetSpecTruncatedTo1k(uri) == ImageURL::TruncatedTo1k) {
- msg += NS_LITERAL_STRING(" URI in this note truncated due to length.");
- }
- src = NS_ConvertUTF8toUTF16(uri);
- }
- if (NS_SUCCEEDED(errorObject->InitWithWindowID(
- msg,
- src,
- EmptyString(), 0, 0, nsIScriptError::errorFlag,
- "Image", InnerWindowID()
- ))) {
- consoleService->LogMessage(errorObject);
- }
- }
- }
- already_AddRefed<imgIContainer>
- RasterImage::Unwrap()
- {
- nsCOMPtr<imgIContainer> self(this);
- return self.forget();
- }
- void
- RasterImage::PropagateUseCounters(nsIDocument*)
- {
- // No use counters.
- }
- IntSize
- RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
- SamplingFilter aSamplingFilter, uint32_t aFlags)
- {
- MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
- aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
- "Unexpected destination size");
- if (mSize.IsEmpty() || aDest.IsEmpty()) {
- return IntSize(0, 0);
- }
- IntSize destSize = IntSize::Ceil(aDest.width, aDest.height);
- if (aSamplingFilter == SamplingFilter::GOOD &&
- CanDownscaleDuringDecode(destSize, aFlags)) {
- return destSize;
- }
- // We can't scale to this size. Use our intrinsic size for now.
- return mSize;
- }
- } // namespace image
- } // namespace mozilla
|