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)); }
LookupResult FrameAnimator::GetCompositedFrame(AnimationState& aState) { // If we have a composited version of this frame, return that. if (mLastCompositedFrameIndex >= 0 && (uint32_t(mLastCompositedFrameIndex) == aState.mCurrentAnimationFrameIndex)) { return LookupResult(DrawableSurface(mCompositingFrame->DrawableRef()), MatchType::EXACT); } // Otherwise return the raw frame. DoBlend is required to ensure that we only // hit this case if the frame is not paletted and doesn't require compositing. LookupResult result = SurfaceCache::Lookup(ImageKey(mImage), RasterSurfaceKey(mSize, DefaultSurfaceFlags(), PlaybackType::eAnimated)); if (!result) { return result; } // Seek to the appropriate frame. If seeking fails, it means that we couldn't // get the frame we're looking for; treat this as if the lookup failed. if (NS_FAILED(result.Surface().Seek(aState.mCurrentAnimationFrameIndex))) { return LookupResult(MatchType::NOT_FOUND); } MOZ_ASSERT(!result.Surface()->GetIsPaletted(), "About to return a paletted frame"); return result; }
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; }
RawAccessFrameRef FrameAnimator::GetRawFrame(uint32_t aFrameNum) const { LookupResult result = SurfaceCache::Lookup(ImageKey(mImage), RasterSurfaceKey(mSize, DefaultSurfaceFlags(), aFrameNum)); return result ? result.DrawableRef()->RawAccessRef() : RawAccessFrameRef(); }
/* static */ already_AddRefed<IDecodingTask> DecoderFactory::CreateDecoder(DecoderType aType, NotNull<RasterImage*> aImage, NotNull<SourceBuffer*> aSourceBuffer, const IntSize& aIntrinsicSize, const IntSize& aOutputSize, DecoderFlags aDecoderFlags, SurfaceFlags aSurfaceFlags) { if (aType == DecoderType::UNKNOWN) { return nullptr; } // Create an anonymous decoder. Interaction with the SurfaceCache and the // owning RasterImage will be mediated by DecodedSurfaceProvider. RefPtr<Decoder> decoder = GetDecoder(aType, nullptr, bool(aDecoderFlags & DecoderFlags::IS_REDECODE)); MOZ_ASSERT(decoder, "Should have a decoder now"); // Initialize the decoder. decoder->SetMetadataDecode(false); decoder->SetIterator(aSourceBuffer->Iterator()); decoder->SetOutputSize(aOutputSize); decoder->SetDecoderFlags(aDecoderFlags | DecoderFlags::FIRST_FRAME_ONLY); decoder->SetSurfaceFlags(aSurfaceFlags); if (NS_FAILED(decoder->Init())) { return nullptr; } // Create a DecodedSurfaceProvider which will manage the decoding process and // make this decoder's output available in the surface cache. SurfaceKey surfaceKey = RasterSurfaceKey(aOutputSize, aSurfaceFlags, PlaybackType::eStatic); auto provider = MakeNotNull<RefPtr<DecodedSurfaceProvider>>( aImage, surfaceKey, WrapNotNull(decoder)); if (aDecoderFlags & DecoderFlags::CANNOT_SUBSTITUTE) { provider->Availability().SetCannotSubstitute(); } // Attempt to insert the surface provider into the surface cache right away so // we won't trigger any more decoders with the same parameters. if (SurfaceCache::Insert(provider) != InsertOutcome::SUCCESS) { return nullptr; } // Return the surface provider in its IDecodingTask guise. RefPtr<IDecodingTask> task = provider.get(); return task.forget(); }
/* static */ already_AddRefed<IDecodingTask> DecoderFactory::CreateAnimationDecoder(DecoderType aType, NotNull<RasterImage*> aImage, NotNull<SourceBuffer*> aSourceBuffer, const IntSize& aIntrinsicSize, DecoderFlags aDecoderFlags, SurfaceFlags aSurfaceFlags) { if (aType == DecoderType::UNKNOWN) { return nullptr; } MOZ_ASSERT(aType == DecoderType::GIF || aType == DecoderType::PNG, "Calling CreateAnimationDecoder for non-animating DecoderType"); // Create an anonymous decoder. Interaction with the SurfaceCache and the // owning RasterImage will be mediated by AnimationSurfaceProvider. RefPtr<Decoder> decoder = GetDecoder(aType, nullptr, /* aIsRedecode = */ true); MOZ_ASSERT(decoder, "Should have a decoder now"); // Initialize the decoder. decoder->SetMetadataDecode(false); decoder->SetIterator(aSourceBuffer->Iterator()); decoder->SetDecoderFlags(aDecoderFlags | DecoderFlags::IS_REDECODE); decoder->SetSurfaceFlags(aSurfaceFlags); if (NS_FAILED(decoder->Init())) { return nullptr; } // Create an AnimationSurfaceProvider which will manage the decoding process // and make this decoder's output available in the surface cache. SurfaceKey surfaceKey = RasterSurfaceKey(aIntrinsicSize, aSurfaceFlags, PlaybackType::eAnimated); auto provider = MakeNotNull<RefPtr<AnimationSurfaceProvider>>( aImage, surfaceKey, WrapNotNull(decoder)); // Attempt to insert the surface provider into the surface cache right away so // we won't trigger any more decoders with the same parameters. if (SurfaceCache::Insert(provider) != InsertOutcome::SUCCESS) { return nullptr; } // Return the surface provider in its IDecodingTask guise. RefPtr<IDecodingTask> task = provider.get(); return task.forget(); }
RawAccessFrameRef FrameAnimator::GetRawFrame(uint32_t aFrameNum) const { LookupResult result = SurfaceCache::Lookup(ImageKey(mImage), RasterSurfaceKey(mSize, DefaultSurfaceFlags(), PlaybackType::eAnimated)); if (!result) { return RawAccessFrameRef(); } // Seek to the frame we want. If seeking fails, it means we couldn't get the // frame we're looking for, so we bail here to avoid returning the wrong frame // to the caller. if (NS_FAILED(result.Surface().Seek(aFrameNum))) { return RawAccessFrameRef(); // Not available yet. } return result.Surface()->RawAccessRef(); }
LookupResult FrameAnimator::GetCompositedFrame(uint32_t aFrameNum) { MOZ_ASSERT(aFrameNum != 0, "First frame is never composited"); // If we have a composited version of this frame, return that. if (mLastCompositedFrameIndex == int32_t(aFrameNum)) { return LookupResult(mCompositingFrame->DrawableRef(), MatchType::EXACT); } // Otherwise return the raw frame. DoBlend is required to ensure that we only // hit this case if the frame is not paletted and doesn't require compositing. LookupResult result = SurfaceCache::Lookup(ImageKey(mImage), RasterSurfaceKey(mSize, DefaultSurfaceFlags(), aFrameNum)); MOZ_ASSERT(!result || !result.DrawableRef()->GetIsPaletted(), "About to return a paletted frame"); return result; }
static void DoCollectSizeOfCompositingSurfaces(const RawAccessFrameRef& aSurface, SurfaceMemoryCounterType aType, nsTArray<SurfaceMemoryCounter>& aCounters, MallocSizeOf aMallocSizeOf) { // Concoct a SurfaceKey for this surface. SurfaceKey key = RasterSurfaceKey(aSurface->GetImageSize(), DefaultSurfaceFlags(), PlaybackType::eStatic); // Create a counter for this surface. SurfaceMemoryCounter counter(key, /* aIsLocked = */ true, aType); // Extract the surface's memory usage information. size_t heap = 0, nonHeap = 0; aSurface->AddSizeOfExcludingThis(aMallocSizeOf, heap, nonHeap); counter.Values().SetDecodedHeap(heap); counter.Values().SetDecodedNonHeap(nonHeap); // Record it. aCounters.AppendElement(counter); }
RawAccessFrameRef Decoder::AllocateFrameInternal(uint32_t aFrameNum, const nsIntSize& aTargetSize, const nsIntRect& aFrameRect, SurfaceFormat aFormat, uint8_t aPaletteDepth, imgFrame* aPreviousFrame) { if (mDataError || NS_FAILED(mFailCode)) { return RawAccessFrameRef(); } if (aFrameNum != mFrameCount) { MOZ_ASSERT_UNREACHABLE("Allocating frames out of order"); return RawAccessFrameRef(); } if (aTargetSize.width <= 0 || aTargetSize.height <= 0 || aFrameRect.width <= 0 || aFrameRect.height <= 0) { NS_WARNING("Trying to add frame with zero or negative size"); return RawAccessFrameRef(); } const uint32_t bytesPerPixel = aPaletteDepth == 0 ? 4 : 1; if (ShouldUseSurfaceCache() && !SurfaceCache::CanHold(aFrameRect.Size(), bytesPerPixel)) { NS_WARNING("Trying to add frame that's too large for the SurfaceCache"); return RawAccessFrameRef(); } RefPtr<imgFrame> frame = new imgFrame(); bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA); if (NS_FAILED(frame->InitForDecoder(aTargetSize, aFrameRect, aFormat, aPaletteDepth, nonPremult))) { NS_WARNING("imgFrame::Init should succeed"); return RawAccessFrameRef(); } RawAccessFrameRef ref = frame->RawAccessRef(); if (!ref) { frame->Abort(); return RawAccessFrameRef(); } if (ShouldUseSurfaceCache()) { InsertOutcome outcome = SurfaceCache::Insert(frame, ImageKey(mImage.get()), RasterSurfaceKey(aTargetSize, mSurfaceFlags, aFrameNum)); if (outcome == InsertOutcome::FAILURE) { // We couldn't insert the surface, almost certainly due to low memory. We // treat this as a permanent error to help the system recover; otherwise, // we might just end up attempting to decode this image again immediately. ref->Abort(); return RawAccessFrameRef(); } else if (outcome == InsertOutcome::FAILURE_ALREADY_PRESENT) { // Another decoder beat us to decoding this frame. We abort this decoder // rather than treat this as a real error. mDecodeAborted = true; ref->Abort(); return RawAccessFrameRef(); } } nsIntRect refreshArea; if (aFrameNum == 1) { MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated"); aPreviousFrame->SetRawAccessOnly(); // If we dispose of the first frame by clearing it, then the first frame's // refresh area is all of itself. // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR). AnimationData previousFrameData = aPreviousFrame->GetAnimationData(); if (previousFrameData.mDisposalMethod == DisposalMethod::CLEAR || previousFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL || previousFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) { refreshArea = previousFrameData.mRect; } } if (aFrameNum > 0) { ref->SetRawAccessOnly(); // Some GIFs are huge but only have a small area that they animate. We only // need to refresh that small area when frame 0 comes around again. refreshArea.UnionRect(refreshArea, frame->GetRect()); } mFrameCount++; if (mImage) { mImage->OnAddedFrame(mFrameCount, refreshArea); } return ref; }