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)); }
/* static */ already_AddRefed<gfx::SourceSurface> ImageOps::DecodeToSurface(nsIInputStream* aInputStream, const nsACString& aMimeType, uint32_t aFlags) { MOZ_ASSERT(aInputStream); nsresult rv; // Prepare the input stream. nsCOMPtr<nsIInputStream> inputStream = aInputStream; if (!NS_InputStreamIsBuffered(aInputStream)) { nsCOMPtr<nsIInputStream> bufStream; rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), aInputStream, 1024); if (NS_SUCCEEDED(rv)) { inputStream = bufStream; } } // Figure out how much data we've been passed. uint64_t length; rv = inputStream->Available(&length); if (NS_FAILED(rv) || length > UINT32_MAX) { return nullptr; } // Write the data into a SourceBuffer. RefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(); sourceBuffer->ExpectLength(length); rv = sourceBuffer->AppendFromInputStream(inputStream, length); if (NS_FAILED(rv)) { return nullptr; } sourceBuffer->Complete(NS_OK); // Create a decoder. DecoderType decoderType = DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get()); RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer, ToSurfaceFlags(aFlags)); if (!decoder) { return nullptr; } // Run the decoder synchronously. decoder->Decode(); if (!decoder->GetDecodeDone() || decoder->HasError()) { return nullptr; } // Pull out the surface. RawAccessFrameRef frame = decoder->GetCurrentFrameRef(); if (!frame) { return nullptr; } RefPtr<SourceSurface> surface = frame->GetSurface(); if (!surface) { return nullptr; } return surface.forget(); }
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; } bool shouldRecordTelemetry = !mDrawStartTime.IsNull() && surface->IsFinished(); auto result = DrawInternal(Move(surface), aContext, aSize, aRegion, aSamplingFilter, flags); if (shouldRecordTelemetry) { TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime; Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY, int32_t(drawLatency.ToMicroseconds())); mDrawStartTime = TimeStamp(); } return result; }
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; }