diff --git a/image/ClippedImage.cpp b/image/ClippedImage.cpp index eeb71263f703..de24f49d34ec 100644 --- a/image/ClippedImage.cpp +++ b/image/ClippedImage.cpp @@ -220,6 +220,16 @@ ClippedImage::GetFrame(uint32_t aWhichFrame, return GetFrameInternal(mClip.Size(), Nothing(), aWhichFrame, aFlags); } +NS_IMETHODIMP_(already_AddRefed) +ClippedImage::GetFrameAtSize(const IntSize& aSize, + uint32_t aWhichFrame, + uint32_t aFlags) +{ + // XXX(seth): It'd be nice to support downscale-during-decode for this case, + // but right now we just fall back to the intrinsic size. + return GetFrame(aWhichFrame, aFlags); +} + already_AddRefed ClippedImage::GetFrameInternal(const nsIntSize& aSize, const Maybe& aSVGContext, diff --git a/image/ClippedImage.h b/image/ClippedImage.h index 88f7d476f33a..c4dba021f7cd 100644 --- a/image/ClippedImage.h +++ b/image/ClippedImage.h @@ -37,6 +37,10 @@ public: NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) override; NS_IMETHOD_(already_AddRefed) GetFrame(uint32_t aWhichFrame, uint32_t aFlags) override; + NS_IMETHOD_(already_AddRefed) + GetFrameAtSize(const gfx::IntSize& aSize, + uint32_t aWhichFrame, + uint32_t aFlags) override; NS_IMETHOD_(bool) IsImageContainerAvailable(layers::LayerManager* aManager, uint32_t aFlags) override; NS_IMETHOD_(already_AddRefed) diff --git a/image/DynamicImage.cpp b/image/DynamicImage.cpp index 52dcf3316a4e..87608f41bb84 100644 --- a/image/DynamicImage.cpp +++ b/image/DynamicImage.cpp @@ -168,10 +168,18 @@ DynamicImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) { gfxIntSize size(mDrawable->Size()); + return GetFrameAtSize(IntSize(size.width, size.height), + aWhichFrame, + aFlags); +} +NS_IMETHODIMP_(already_AddRefed) +DynamicImage::GetFrameAtSize(const IntSize& aSize, + uint32_t aWhichFrame, + uint32_t aFlags) +{ RefPtr dt = gfxPlatform::GetPlatform()-> - CreateOffscreenContentDrawTarget(IntSize(size.width, size.height), - SurfaceFormat::B8G8R8A8); + CreateOffscreenContentDrawTarget(aSize, SurfaceFormat::B8G8R8A8); if (!dt) { gfxWarning() << "DynamicImage::GetFrame failed in CreateOffscreenContentDrawTarget"; @@ -179,7 +187,7 @@ DynamicImage::GetFrame(uint32_t aWhichFrame, } nsRefPtr context = new gfxContext(dt); - auto result = Draw(context, size, ImageRegion::Create(size), + auto result = Draw(context, aSize, ImageRegion::Create(aSize), aWhichFrame, GraphicsFilter::FILTER_NEAREST, Nothing(), aFlags); diff --git a/image/FrozenImage.cpp b/image/FrozenImage.cpp index 628e33d74d1a..cf0dbf72b587 100644 --- a/image/FrozenImage.cpp +++ b/image/FrozenImage.cpp @@ -44,6 +44,14 @@ FrozenImage::GetFrame(uint32_t aWhichFrame, return InnerImage()->GetFrame(FRAME_FIRST, aFlags); } +NS_IMETHODIMP_(already_AddRefed) +FrozenImage::GetFrameAtSize(const IntSize& aSize, + uint32_t aWhichFrame, + uint32_t aFlags) +{ + return InnerImage()->GetFrameAtSize(aSize, FRAME_FIRST, aFlags); +} + NS_IMETHODIMP_(bool) FrozenImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags) { diff --git a/image/FrozenImage.h b/image/FrozenImage.h index ee6e4bd6315a..ff54ce666dff 100644 --- a/image/FrozenImage.h +++ b/image/FrozenImage.h @@ -37,6 +37,10 @@ public: NS_IMETHOD GetAnimated(bool* aAnimated) override; NS_IMETHOD_(already_AddRefed) GetFrame(uint32_t aWhichFrame, uint32_t aFlags) override; + NS_IMETHOD_(already_AddRefed) + GetFrameAtSize(const gfx::IntSize& aSize, + uint32_t aWhichFrame, + uint32_t aFlags) override; NS_IMETHOD_(bool) IsImageContainerAvailable(layers::LayerManager* aManager, uint32_t aFlags) override; NS_IMETHOD_(already_AddRefed) diff --git a/image/ImageWrapper.cpp b/image/ImageWrapper.cpp index 89878f6fae20..02139b730647 100644 --- a/image/ImageWrapper.cpp +++ b/image/ImageWrapper.cpp @@ -174,6 +174,14 @@ ImageWrapper::GetFrame(uint32_t aWhichFrame, return mInnerImage->GetFrame(aWhichFrame, aFlags); } +NS_IMETHODIMP_(already_AddRefed) +ImageWrapper::GetFrameAtSize(const IntSize& aSize, + uint32_t aWhichFrame, + uint32_t aFlags) +{ + return mInnerImage->GetFrameAtSize(aSize, aWhichFrame, aFlags); +} + NS_IMETHODIMP_(bool) ImageWrapper::IsOpaque() { diff --git a/image/OrientedImage.cpp b/image/OrientedImage.cpp index b2d13bee3844..5de600b8b337 100644 --- a/image/OrientedImage.cpp +++ b/image/OrientedImage.cpp @@ -122,6 +122,16 @@ OrientedImage::GetFrame(uint32_t aWhichFrame, return target->Snapshot(); } +NS_IMETHODIMP_(already_AddRefed) +OrientedImage::GetFrameAtSize(const IntSize& aSize, + uint32_t aWhichFrame, + uint32_t aFlags) +{ + // XXX(seth): It'd be nice to support downscale-during-decode for this case, + // but right now we just fall back to the intrinsic size. + return GetFrame(aWhichFrame, aFlags); +} + NS_IMETHODIMP_(bool) OrientedImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags) { diff --git a/image/OrientedImage.h b/image/OrientedImage.h index e1f321fc15f1..cbfe308d2092 100644 --- a/image/OrientedImage.h +++ b/image/OrientedImage.h @@ -34,6 +34,10 @@ public: NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) override; NS_IMETHOD_(already_AddRefed) GetFrame(uint32_t aWhichFrame, uint32_t aFlags) override; + NS_IMETHOD_(already_AddRefed) + GetFrameAtSize(const gfx::IntSize& aSize, + uint32_t aWhichFrame, + uint32_t aFlags) override; NS_IMETHOD_(bool) IsImageContainerAvailable(layers::LayerManager* aManager, uint32_t aFlags) override; NS_IMETHOD_(already_AddRefed) diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp index f8e67dbd0c18..b1ca7ec64428 100644 --- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -698,14 +698,28 @@ NS_IMETHODIMP_(already_AddRefed) RasterImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) { - return GetFrameInternal(aWhichFrame, aFlags).second().forget(); + return GetFrameInternal(mSize, aWhichFrame, aFlags).second().forget(); +} + +NS_IMETHODIMP_(already_AddRefed) +RasterImage::GetFrameAtSize(const IntSize& aSize, + uint32_t aWhichFrame, + uint32_t aFlags) +{ + return GetFrameInternal(aSize, aWhichFrame, aFlags).second().forget(); } Pair> -RasterImage::GetFrameInternal(uint32_t aWhichFrame, uint32_t aFlags) +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()); + } + if (aWhichFrame > FRAME_MAX_VALUE) { return MakePair(DrawResult::BAD_ARGS, RefPtr()); } @@ -718,7 +732,7 @@ RasterImage::GetFrameInternal(uint32_t aWhichFrame, uint32_t aFlags) // not waiting for the data to be loaded from the network or not passing // FLAG_SYNC_DECODE DrawableFrameRef frameRef = - LookupFrame(GetRequestedFrameIndex(aWhichFrame), mSize, aFlags); + LookupFrame(GetRequestedFrameIndex(aWhichFrame), aSize, aFlags); if (!frameRef) { // The OS threw this frame away and we couldn't redecode it. return MakePair(DrawResult::TEMPORARY_ERROR, RefPtr()); @@ -727,15 +741,15 @@ RasterImage::GetFrameInternal(uint32_t aWhichFrame, uint32_t aFlags) // If this frame covers the entire image, we can just reuse its existing // surface. RefPtr frameSurf; - IntRect frameRect = frameRef->GetRect(); - if (frameRect.x == 0 && frameRect.y == 0 && - frameRect.width == mSize.width && - frameRect.height == mSize.height) { + if (!frameRef->NeedsPadding() && + frameRef->GetSize() == aSize) { frameSurf = frameRef->GetSurface(); } // The image doesn't have a usable surface because it's been optimized away or - // because it's a partial update frame from an animation. Create one. + // because it's a partial update frame from an animation. Create one. (In this + // case we fall back to returning a surface at our intrinsic size, even if a + // different size was originally specified.) if (!frameSurf) { frameSurf = CopyFrame(aWhichFrame, aFlags); } @@ -756,7 +770,7 @@ RasterImage::GetCurrentImage(ImageContainer* aContainer, uint32_t aFlags) DrawResult drawResult; RefPtr surface; Tie(drawResult, surface) = - GetFrameInternal(FRAME_CURRENT, aFlags | FLAG_ASYNC_NOTIFY); + 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. diff --git a/image/RasterImage.h b/image/RasterImage.h index 27f58fafe32e..2ad7852e2dc9 100644 --- a/image/RasterImage.h +++ b/image/RasterImage.h @@ -265,7 +265,9 @@ private: uint32_t aFlags); Pair> - GetFrameInternal(uint32_t aWhichFrame, uint32_t aFlags); + GetFrameInternal(const gfx::IntSize& aSize, + uint32_t aWhichFrame, + uint32_t aFlags); LookupResult LookupFrameInternal(uint32_t aFrameNum, const gfx::IntSize& aSize, diff --git a/image/VectorImage.cpp b/image/VectorImage.cpp index 46be36c3ec44..5eca76ba4db6 100644 --- a/image/VectorImage.cpp +++ b/image/VectorImage.cpp @@ -668,19 +668,8 @@ VectorImage::IsOpaque() /* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame, * in uint32_t aFlags; */ NS_IMETHODIMP_(already_AddRefed) -VectorImage::GetFrame(uint32_t aWhichFrame, - uint32_t aFlags) +VectorImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) { - MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE); - - if (aWhichFrame > FRAME_MAX_VALUE) { - return nullptr; - } - - if (mError || !mIsFullyLoaded) { - return nullptr; - } - // Look up height & width // ---------------------- SVGSVGElement* svgElem = mSVGDocumentWrapper->GetRootSVGElem(); @@ -695,12 +684,32 @@ VectorImage::GetFrame(uint32_t aWhichFrame, return nullptr; } + return GetFrameAtSize(imageIntSize, aWhichFrame, aFlags); +} + +NS_IMETHODIMP_(already_AddRefed) +VectorImage::GetFrameAtSize(const IntSize& aSize, + uint32_t aWhichFrame, + uint32_t aFlags) +{ + MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE); + + if (aSize.IsEmpty()) { + return nullptr; + } + + if (aWhichFrame > FRAME_MAX_VALUE) { + return nullptr; + } + + if (mError || !mIsFullyLoaded) { + return nullptr; + } + // Make our surface the size of what will ultimately be drawn to it. // (either the full image size, or the restricted region) RefPtr dt = gfxPlatform::GetPlatform()-> - CreateOffscreenContentDrawTarget(IntSize(imageIntSize.width, - imageIntSize.height), - SurfaceFormat::B8G8R8A8); + CreateOffscreenContentDrawTarget(aSize, SurfaceFormat::B8G8R8A8); if (!dt) { NS_ERROR("Could not create a DrawTarget"); return nullptr; @@ -708,8 +717,8 @@ VectorImage::GetFrame(uint32_t aWhichFrame, nsRefPtr context = new gfxContext(dt); - auto result = Draw(context, imageIntSize, - ImageRegion::Create(imageIntSize), + auto result = Draw(context, aSize, + ImageRegion::Create(aSize), aWhichFrame, GraphicsFilter::FILTER_NEAREST, Nothing(), aFlags); diff --git a/image/imgIContainer.idl b/image/imgIContainer.idl index 6495d863129e..53d2a1829a70 100644 --- a/image/imgIContainer.idl +++ b/image/imgIContainer.idl @@ -119,7 +119,7 @@ native nsIntSizeByVal(nsIntSize); * * Internally, imgIContainer also manages animation of images. */ -[scriptable, builtinclass, uuid(4880727a-5673-44f7-b248-f6c86e22a434)] +[scriptable, builtinclass, uuid(4e5a0547-6c54-4051-8b52-1f2fdd667696)] interface imgIContainer : nsISupports { /** @@ -267,6 +267,21 @@ interface imgIContainer : nsISupports [noscript, notxpcom] TempRefSourceSurface getFrame(in uint32_t aWhichFrame, in uint32_t aFlags); + /** + * Get a surface for the given frame at the specified size. Matching the + * requested size is best effort; it's not guaranteed that the surface you get + * will be a perfect match. (Some reasons you may get a surface of a different + * size include: if you requested upscaling, if downscale-during-decode is + * disabled, or if you didn't request the first frame.) + * + * @param aSize The desired size. + * @param aWhichFrame Frame specifier of the FRAME_* variety. + * @param aFlags Flags of the FLAG_* variety + */ + [noscript, notxpcom] TempRefSourceSurface getFrameAtSize([const] in nsIntSize aSize, + in uint32_t aWhichFrame, + in uint32_t aFlags); + /** * Whether this image is opaque (i.e., needs a background painted behind it). */