diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 2024e05486a2..d0277606fb93 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -6322,13 +6322,13 @@ ImgDrawResult nsLayoutUtils::DrawSingleUnscaledImage( /* static */ ImgDrawResult nsLayoutUtils::DrawSingleImage( gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, - const SamplingFilter aSamplingFilter, const nsRect& aDest, + float aResolution, SamplingFilter aSamplingFilter, const nsRect& aDest, const nsRect& aDirty, const Maybe& aSVGContext, uint32_t aImageFlags, const nsPoint* aAnchorPoint, const nsRect* aSourceArea) { nscoord appUnitsPerCSSPixel = AppUnitsPerCSSPixel(); CSSIntSize pixelImageSize( - ComputeSizeForDrawingWithFallback(aImage, aDest.Size())); + ComputeSizeForDrawingWithFallback(aImage, aResolution, aDest.Size())); if (pixelImageSize.width < 1 || pixelImageSize.height < 1) { NS_ASSERTION(pixelImageSize.width >= 0 && pixelImageSize.height >= 0, "Image width or height is negative"); @@ -6369,7 +6369,8 @@ ImgDrawResult nsLayoutUtils::DrawSingleImage( /* static */ void nsLayoutUtils::ComputeSizeForDrawing( - imgIContainer* aImage, /* outparam */ CSSIntSize& aImageSize, + imgIContainer* aImage, float aResolution, + /* outparam */ CSSIntSize& aImageSize, /* outparam */ AspectRatio& aIntrinsicRatio, /* outparam */ bool& aGotWidth, /* outparam */ bool& aGotHeight) { @@ -6378,6 +6379,15 @@ void nsLayoutUtils::ComputeSizeForDrawing( Maybe intrinsicRatio = aImage->GetIntrinsicRatio(); aIntrinsicRatio = intrinsicRatio.valueOr(AspectRatio()); + if (aResolution != 0.0f && aResolution != 1.0f) { + if (aGotWidth) { + aImageSize.width = std::round(float(aImageSize.width) / aResolution); + } + if (aGotHeight) { + aImageSize.height = std::round(float(aImageSize.height) / aResolution); + } + } + if (!(aGotWidth && aGotHeight) && intrinsicRatio.isNothing()) { // We hit an error (say, because the image failed to load or couldn't be // decoded) and should return zero size. @@ -6388,11 +6398,12 @@ void nsLayoutUtils::ComputeSizeForDrawing( /* static */ CSSIntSize nsLayoutUtils::ComputeSizeForDrawingWithFallback( - imgIContainer* aImage, const nsSize& aFallbackSize) { + imgIContainer* aImage, float aResolution, const nsSize& aFallbackSize) { CSSIntSize imageSize; AspectRatio imageRatio; bool gotHeight, gotWidth; - ComputeSizeForDrawing(aImage, imageSize, imageRatio, gotWidth, gotHeight); + ComputeSizeForDrawing(aImage, aResolution, imageSize, imageRatio, gotWidth, + gotHeight); // If we didn't get both width and height, try to compute them using the // intrinsic ratio of the image. diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index b727fdf7038d..47477cbf3b86 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -1901,6 +1901,11 @@ class nsLayoutUtils { * appropriate scale and transform for drawing in * app units. * @param aImage The image. + * @param aResolution The resolution specified by the author for the + * image, in dppx. This will affect the intrinsic + * size of the image (so e.g., if resolution is 2, + * and the image is 100x100, the intrinsic size of + * the image will be 50x50). * @param aDest The area that the image should fill. * @param aDirty Pixels outside this area may be skipped. * @param aSVGContext Optionally provides an SVGImageContext. @@ -1922,7 +1927,7 @@ class nsLayoutUtils { */ static ImgDrawResult DrawSingleImage( gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, - const SamplingFilter aSamplingFilter, const nsRect& aDest, + float aResolution, SamplingFilter aSamplingFilter, const nsRect& aDest, const nsRect& aDirty, const mozilla::Maybe& aSVGContext, uint32_t aImageFlags, const nsPoint* aAnchorPoint = nullptr, const nsRect* aSourceArea = nullptr); @@ -1943,7 +1948,7 @@ class nsLayoutUtils { * difference is that this one is simpler and is suited to places where we * have less information about the frame tree. */ - static void ComputeSizeForDrawing(imgIContainer* aImage, + static void ComputeSizeForDrawing(imgIContainer* aImage, float aResolution, CSSIntSize& aImageSize, AspectRatio& aIntrinsicRatio, bool& aGotWidth, bool& aGotHeight); @@ -1957,7 +1962,7 @@ class nsLayoutUtils { * dimensions, the corresponding dimension of aFallbackSize is used instead. */ static CSSIntSize ComputeSizeForDrawingWithFallback( - imgIContainer* aImage, const nsSize& aFallbackSize); + imgIContainer* aImage, float aResolution, const nsSize& aFallbackSize); /** * Given the image container, frame, and dest rect, determine the best fitting diff --git a/layout/generic/nsBulletFrame.cpp b/layout/generic/nsBulletFrame.cpp index 0cb71c78c641..853508663fd6 100644 --- a/layout/generic/nsBulletFrame.cpp +++ b/layout/generic/nsBulletFrame.cpp @@ -781,12 +781,9 @@ void nsBulletFrame::GetDesiredSize(nsPresContext* aCX, RemoveStateBits(BULLET_FRAME_IMAGE_LOADING); - if (RefPtr image = GetImage()) { - int32_t w = 0; - int32_t h = 0; - image->GetWidth(&w); - image->GetHeight(&h); - LogicalSize size(GetWritingMode(), CSSPixel::ToAppUnits(CSSIntSize(w, h))); + if (Maybe intrinsicSize = + StyleList()->mListStyleImage.GetIntrinsicSize()) { + LogicalSize size(GetWritingMode(), CSSPixel::ToAppUnits(*intrinsicSize)); // auto size the image finalSize.ISize(wm) = size.ISize(wm); finalSize.BSize(wm) = size.BSize(wm); diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index b3a53641f28f..0b72bbc2e816 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -398,11 +398,13 @@ void nsImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, } MOZ_RELEASE_ASSERT(contentIndex < styleContent->ContentCount()); MOZ_RELEASE_ASSERT(styleContent->ContentAt(contentIndex).IsImage()); - auto& imageUrl = styleContent->ContentAt(contentIndex).AsImage(); - MOZ_ASSERT(imageUrl.IsImageRequestType(), + const StyleImage& image = styleContent->ContentAt(contentIndex).AsImage(); + MOZ_ASSERT(image.IsImageRequestType(), "Content image should only parse url() type"); + auto [finalImage, resolution] = image.FinalImageAndResolution(); Document* doc = PresContext()->Document(); - if (imgRequestProxy* proxy = imageUrl.GetImageRequest()) { + if (imgRequestProxy* proxy = finalImage->GetImageRequest()) { + mContentURLRequestResolution = resolution; proxy->Clone(mListener, doc, getter_AddRefs(mContentURLRequest)); SetupForContentURLRequest(); } @@ -454,6 +456,20 @@ void nsImageFrame::SetupForContentURLRequest() { } } +static void ScaleIntrinsicSizeForDensity(IntrinsicSize& aSize, + double aDensity) { + if (aDensity == 1.0) { + return; + } + + if (aSize.width) { + aSize.width = Some(NSToCoordRound(double(*aSize.width) / aDensity)); + } + if (aSize.height) { + aSize.height = Some(NSToCoordRound(double(*aSize.height) / aDensity)); + } +} + static void ScaleIntrinsicSizeForDensity(nsIContent& aContent, IntrinsicSize& aSize) { auto* image = HTMLImageElement::FromNode(aContent); @@ -468,16 +484,7 @@ static void ScaleIntrinsicSizeForDensity(nsIContent& aContent, double density = selector->GetSelectedImageDensity(); MOZ_ASSERT(density >= 0.0); - if (density == 1.0) { - return; - } - - if (aSize.width) { - aSize.width = Some(NSToCoordRound(double(*aSize.width) / density)); - } - if (aSize.height) { - aSize.height = Some(NSToCoordRound(double(*aSize.height) / density)); - } + ScaleIntrinsicSizeForDensity(aSize, density); } static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage, @@ -496,6 +503,9 @@ static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage, intrinsicSize.height = size.height == -1 ? Nothing() : Some(size.height); if (aKind == nsImageFrame::Kind::ImageElement) { ScaleIntrinsicSizeForDensity(*aFrame.GetContent(), intrinsicSize); + } else { + ScaleIntrinsicSizeForDensity(intrinsicSize, + aFrame.GetContentURLRequestResolution()); } return intrinsicSize; } @@ -1476,7 +1486,7 @@ ImgDrawResult nsImageFrame::DisplayAltFeedback(gfxContext& aRenderingContext, nsRect dest(flushRight ? inner.XMost() - size : inner.x, inner.y, size, size); result = nsLayoutUtils::DrawSingleImage( - aRenderingContext, PresContext(), imgCon, + aRenderingContext, PresContext(), imgCon, /* aResolution = */ 1.0f, nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect, /* no SVGImageContext */ Nothing(), aFlags); } @@ -2080,8 +2090,10 @@ ImgDrawResult nsImageFrame::PaintImage(gfxContext& aRenderingContext, Maybe svgContext; SVGImageContext::MaybeStoreContextPaint(svgContext, this, aImage); + // We've already accounted for resolution via mIntrinsicSize, which influences + // the dest rect, so we don't need to worry about it here.. ImgDrawResult result = nsLayoutUtils::DrawSingleImage( - aRenderingContext, PresContext(), aImage, + aRenderingContext, PresContext(), aImage, /* aResolution = */ 1.0f, nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect, svgContext, flags, &anchorPoint); diff --git a/layout/generic/nsImageFrame.h b/layout/generic/nsImageFrame.h index cb4366e448d4..3bc90ecafb26 100644 --- a/layout/generic/nsImageFrame.h +++ b/layout/generic/nsImageFrame.h @@ -103,6 +103,12 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback { void SetupForContentURLRequest(); bool ShouldShowBrokenImageIcon() const; + // Get the resolution, in dppx, for the image that mContentURLRequest + // represents. + float GetContentURLRequestResolution() const { + return mContentURLRequestResolution; + } + #ifdef ACCESSIBILITY mozilla::a11y::AccType AccessibleType() override; #endif @@ -353,6 +359,7 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback { // An image request created for content: url(..). RefPtr mContentURLRequest; + float mContentURLRequestResolution = 1.0f; nsCOMPtr mImage; nsCOMPtr mPrevImage; diff --git a/layout/painting/nsImageRenderer.cpp b/layout/painting/nsImageRenderer.cpp index a23a90086064..f32d50a2c808 100644 --- a/layout/painting/nsImageRenderer.cpp +++ b/layout/painting/nsImageRenderer.cpp @@ -50,8 +50,6 @@ nsSize CSSSizeOrRatio::ComputeConcreteSize() const { nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage, uint32_t aFlags) : mForFrame(aForFrame), - mImage(&aImage->FinalImage()), - mType(mImage->tag), mImageContainer(nullptr), mGradientData(nullptr), mPaintServerFrame(nullptr), @@ -59,7 +57,12 @@ nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage, mSize(0, 0), mFlags(aFlags), mExtendMode(ExtendMode::CLAMP), - mMaskOp(StyleMaskMode::MatchSource) {} + mMaskOp(StyleMaskMode::MatchSource) { + auto pair = aImage->FinalImageAndResolution(); + mImage = pair.first; + mType = mImage->tag; + mImageResolution = pair.second; +} bool nsImageRenderer::PrepareImage() { if (mImage->IsNone()) { @@ -198,14 +201,14 @@ CSSSizeOrRatio nsImageRenderer::ComputeIntrinsicSize() { case StyleImage::Tag::Url: { bool haveWidth, haveHeight; CSSIntSize imageIntSize; - nsLayoutUtils::ComputeSizeForDrawing( - mImageContainer, imageIntSize, result.mRatio, haveWidth, haveHeight); + nsLayoutUtils::ComputeSizeForDrawing(mImageContainer, mImageResolution, + imageIntSize, result.mRatio, + haveWidth, haveHeight); if (haveWidth) { - result.SetWidth(nsPresContext::CSSPixelsToAppUnits(imageIntSize.width)); + result.SetWidth(CSSPixel::ToAppUnits(imageIntSize.width)); } if (haveHeight) { - result.SetHeight( - nsPresContext::CSSPixelsToAppUnits(imageIntSize.height)); + result.SetHeight(CSSPixel::ToAppUnits(imageIntSize.height)); } // If we know the aspect ratio and one of the dimensions, @@ -937,8 +940,8 @@ ImgDrawResult nsImageRenderer::DrawBorderImageComponent( if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) { ImgDrawResult result = nsLayoutUtils::DrawSingleImage( - aRenderingContext, aPresContext, subImage, samplingFilter, aFill, - aDirtyRect, + aRenderingContext, aPresContext, subImage, mImageResolution, + samplingFilter, aFill, aDirtyRect, /* no SVGImageContext */ Nothing(), drawFlags); if (!mImage->IsComplete()) { @@ -1007,8 +1010,9 @@ ImgDrawResult nsImageRenderer::DrawShapeImage(nsPresContext* aPresContext, // rendered pixel has an alpha that precisely matches the alpha of the // closest pixel in the image. return nsLayoutUtils::DrawSingleImage( - aRenderingContext, aPresContext, mImageContainer, SamplingFilter::POINT, - dest, dest, Nothing(), drawFlags, nullptr, nullptr); + aRenderingContext, aPresContext, mImageContainer, mImageResolution, + SamplingFilter::POINT, dest, dest, Nothing(), drawFlags, nullptr, + nullptr); } if (mImage->IsGradient()) { diff --git a/layout/painting/nsImageRenderer.h b/layout/painting/nsImageRenderer.h index 6d5843a73f8c..fcff08d181e3 100644 --- a/layout/painting/nsImageRenderer.h +++ b/layout/painting/nsImageRenderer.h @@ -299,6 +299,7 @@ class nsImageRenderer { nsIFrame* mForFrame; const mozilla::StyleImage* mImage; + float mImageResolution; mozilla::StyleImage::Tag mType; nsCOMPtr mImageContainer; const mozilla::StyleGradient* mGradientData; diff --git a/layout/style/GeckoBindings.h b/layout/style/GeckoBindings.h index dd61a521aa16..79da76e80059 100644 --- a/layout/style/GeckoBindings.h +++ b/layout/style/GeckoBindings.h @@ -15,6 +15,7 @@ #include "mozilla/ServoBindingTypes.h" #include "mozilla/css/DocumentMatchingFunction.h" #include "mozilla/css/SheetLoadData.h" +#include "mozilla/dom/Document.h" #include "mozilla/EffectCompositor.h" #include "mozilla/ComputedTimingFunction.h" #include "mozilla/PreferenceSheet.h" diff --git a/layout/style/ServoStyleConstsInlines.h b/layout/style/ServoStyleConstsInlines.h index a1d7f642c823..8a344472fae4 100644 --- a/layout/style/ServoStyleConstsInlines.h +++ b/layout/style/ServoStyleConstsInlines.h @@ -942,14 +942,25 @@ inline bool RestyleHint::DefinitelyRecascadesAllSubtree() const { } template <> -inline const StyleImage& StyleImage::FinalImage() const { +inline std::pair StyleImage::FinalImageAndResolution() + const { if (!IsImageSet()) { - return *this; + return {this, 1.0f}; } auto& set = AsImageSet(); - return set->items.AsSpan()[set->selected_index].image.FinalImage(); + auto& selectedItem = set->items.AsSpan()[set->selected_index]; + return {selectedItem.image.FinalImageAndResolution().first, + selectedItem.resolution._0}; } +template <> +inline const StyleImage& StyleImage::FinalImage() const { + return *FinalImageAndResolution().first; +} + +template <> +Maybe StyleImage::GetIntrinsicSize() const; + template <> inline bool StyleImage::IsImageRequestType() const { auto& finalImage = FinalImage(); diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index b8ec46b78c9b..454b11529961 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1629,6 +1629,31 @@ void StyleImage::ResolveImage(Document& aDoc, const StyleImage* aOld) { const_cast(url)->ResolveImage(aDoc, old); } +template <> +Maybe StyleImage::GetIntrinsicSize() const { + auto [finalImage, resolution] = FinalImageAndResolution(); + imgRequestProxy* request = finalImage->GetImageRequest(); + if (!request) { + return Nothing(); + } + RefPtr image; + request->GetImage(getter_AddRefs(image)); + if (!image) { + return Nothing(); + } + // FIXME(emilio): Seems like this should be smarter about unspecified width / + // height, aspect ratio, etc, but this preserves the current behavior of our + // only caller for now... + int32_t w = 0, h = 0; + image->GetWidth(&w); + image->GetHeight(&h); + if (resolution != 0.0f && resolution != 1.0f) { + w = std::round(float(w) / resolution); + h = std::round(float(h) / resolution); + } + return Some(CSSIntSize{w, h}); +} + // -------------------- // nsStyleImageLayers // @@ -1882,8 +1907,11 @@ static bool SizeDependsOnPositioningAreaSize(const StyleBackgroundSize& aSize, CSSIntSize imageSize; AspectRatio imageRatio; bool hasWidth, hasHeight; - nsLayoutUtils::ComputeSizeForDrawing(imgContainer, imageSize, imageRatio, - hasWidth, hasHeight); + // We could bother getting the right resolution here but it doesn't matter + // since we ignore `imageSize`. + nsLayoutUtils::ComputeSizeForDrawing(imgContainer, + /* aResolution = */ 1.0f, imageSize, + imageRatio, hasWidth, hasHeight); // If the image has a fixed width and height, rendering never depends on // the frame size. diff --git a/layout/svg/SVGImageFrame.cpp b/layout/svg/SVGImageFrame.cpp index ccfaeced62dc..4c4e63c44ad7 100644 --- a/layout/svg/SVGImageFrame.cpp +++ b/layout/svg/SVGImageFrame.cpp @@ -405,7 +405,7 @@ void SVGImageFrame::PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform, // That method needs our image to have a fixed native width & height, // and that's not always true for TYPE_VECTOR images. aImgParams.result &= nsLayoutUtils::DrawSingleImage( - aContext, PresContext(), mImageContainer, + aContext, PresContext(), mImageContainer, /* aResolution = */ 1.0f, nsLayoutUtils::GetSamplingFilterForFrame(this), destRect, aDirtyRect ? dirtyRect : destRect, context, flags); } else { // mImageContainer->GetType() == TYPE_RASTER diff --git a/layout/xul/nsImageBoxFrame.cpp b/layout/xul/nsImageBoxFrame.cpp index 17d1b357c336..ee3e91cbed3a 100644 --- a/layout/xul/nsImageBoxFrame.cpp +++ b/layout/xul/nsImageBoxFrame.cpp @@ -18,6 +18,7 @@ #include "nsStyleConsts.h" #include "nsStyleUtil.h" #include "nsCOMPtr.h" +#include "nsLayoutUtils.h" #include "nsPresContext.h" #include "nsBoxLayoutState.h" @@ -239,6 +240,7 @@ void nsImageBoxFrame::UpdateImage() { mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src); mUseSrcAttr = !src.IsEmpty(); if (mUseSrcAttr) { + mImageResolution = 1.0f; nsContentPolicyType contentPolicyType; nsCOMPtr triggeringPrincipal; uint64_t requestContextID = 0; @@ -267,9 +269,13 @@ void nsImageBoxFrame::UpdateImage() { doc->ImageTracker()->Add(mImageRequest); } } - } else if (auto* styleRequest = GetRequestFromStyle()) { - styleRequest->SyncClone(mListener, mContent->GetComposedDoc(), - getter_AddRefs(mImageRequest)); + } else if (auto* styleImage = GetImageFromStyle()) { + auto [finalImage, resolution] = styleImage->FinalImageAndResolution(); + mImageResolution = resolution; + if (auto* styleRequest = finalImage->GetImageRequest()) { + styleRequest->SyncClone(mListener, mContent->GetComposedDoc(), + getter_AddRefs(mImageRequest)); + } } if (!mImageRequest) { @@ -388,7 +394,7 @@ ImgDrawResult nsImageBoxFrame::PaintImage(gfxContext& aRenderingContext, Maybe svgContext; SVGImageContext::MaybeStoreContextPaint(svgContext, this, imgCon); return nsLayoutUtils::DrawSingleImage( - aRenderingContext, PresContext(), imgCon, + aRenderingContext, PresContext(), imgCon, mImageResolution, nsLayoutUtils::GetSamplingFilterForFrame(this), dest, dirty, svgContext, aFlags, anchorPoint.ptrOr(nullptr), hasSubRect ? &mSubRect : nullptr); } @@ -603,8 +609,9 @@ bool nsImageBoxFrame::CanOptimizeToImageLayer() { return true; } -imgRequestProxy* nsImageBoxFrame::GetRequestFromStyle() { - const nsStyleDisplay* disp = StyleDisplay(); +const mozilla::StyleImage* nsImageBoxFrame::GetImageFromStyle( + const ComputedStyle& aStyle) { + const nsStyleDisplay* disp = aStyle.StyleDisplay(); if (disp->HasAppearance()) { nsPresContext* pc = PresContext(); if (pc->Theme()->ThemeSupportsWidget(pc, this, @@ -612,7 +619,11 @@ imgRequestProxy* nsImageBoxFrame::GetRequestFromStyle() { return nullptr; } } - return StyleList()->mListStyleImage.GetImageRequest(); + auto& image = aStyle.StyleList()->mListStyleImage; + if (!image.IsImageRequestType()) { + return nullptr; + } + return ℑ } /* virtual */ @@ -623,26 +634,18 @@ void nsImageBoxFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) { const nsStyleList* myList = StyleList(); mSubRect = myList->GetImageRegion(); // before |mSuppressStyleCheck| test! - if (mUseSrcAttr || mSuppressStyleCheck) + if (mUseSrcAttr || mSuppressStyleCheck) { return; // No more work required, since the image isn't specified by style. + } - // If the image to use changes, we have a new image. - nsCOMPtr oldURI, newURI; - if (mImageRequest) { - mImageRequest->GetURI(getter_AddRefs(oldURI)); - } - if (auto* newImage = GetRequestFromStyle()) { - newImage->GetURI(getter_AddRefs(newURI)); - } - bool equal; - if (newURI == oldURI || // handles null==null - (newURI && oldURI && NS_SUCCEEDED(newURI->Equals(oldURI, &equal)) && - equal)) { + auto* oldImage = aOldStyle ? GetImageFromStyle(*aOldStyle) : nullptr; + auto* newImage = GetImageFromStyle(); + if (newImage == oldImage || + (newImage && oldImage && *oldImage == *newImage)) { return; } - UpdateImage(); -} // DidSetComputedStyle +} void nsImageBoxFrame::GetImageSize() { if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) { @@ -796,12 +799,16 @@ void nsImageBoxFrame::OnSizeAvailable(imgIRequest* aRequest, aImage->SetAnimationMode(PresContext()->ImageAnimationMode()); - nscoord w, h; + int32_t w = 0, h = 0; aImage->GetWidth(&w); aImage->GetHeight(&h); - mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(w), - nsPresContext::CSSPixelsToAppUnits(h)); + if (mImageResolution != 0.0f && mImageResolution != 1.0f) { + w = std::round(w / mImageResolution); + h = std::round(h / mImageResolution); + } + + mIntrinsicSize.SizeTo(CSSPixel::ToAppUnits(w), CSSPixel::ToAppUnits(h)); if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange, diff --git a/layout/xul/nsImageBoxFrame.h b/layout/xul/nsImageBoxFrame.h index 3e7a15dd3b31..75d25691ad3e 100644 --- a/layout/xul/nsImageBoxFrame.h +++ b/layout/xul/nsImageBoxFrame.h @@ -73,11 +73,15 @@ class nsImageBoxFrame final : public nsLeafBoxFrame { #endif /** - * Gets the image request to be loaded from the current style. + * Gets the image to be loaded from the current style. May be null if themed, + * or if not an url image. * - * May be null if themed. + * TODO(emilio): Maybe support list-style-image: linear-gradient() etc? */ - imgRequestProxy* GetRequestFromStyle(); + const mozilla::StyleImage* GetImageFromStyle(const ComputedStyle&); + const mozilla::StyleImage* GetImageFromStyle() { + return GetImageFromStyle(*Style()); + } /** * Update mUseSrcAttr from appropriate content attributes or from @@ -137,6 +141,7 @@ class nsImageBoxFrame final : public nsLeafBoxFrame { nsSize mImageSize; RefPtr mImageRequest; + float mImageResolution = 1.0f; nsCOMPtr mListener; int32_t mLoadFlags; diff --git a/servo/ports/geckolib/cbindgen.toml b/servo/ports/geckolib/cbindgen.toml index c891333a15fd..e8bfcfe6f536 100644 --- a/servo/ports/geckolib/cbindgen.toml +++ b/servo/ports/geckolib/cbindgen.toml @@ -790,6 +790,17 @@ renaming_overrides_prefixing = true "GenericImage" = """ public: + // Returns the final image we've selected taken from the image-set, along with + // its resolution, or `this` and `1.0` if this image is not an image-set. + // + // The resolution is in dppx, and should be used to impact the intrinsic size + // of the image. + std::pair FinalImageAndResolution() const; + + // Returns the intrinsic size of the image, if there's one, accounting for + // resolution as needed. + Maybe GetIntrinsicSize() const; + // If this is an image-set(), the final image we've selected, otherwise it // returns *this. const StyleGenericImage& FinalImage() const; diff --git a/testing/web-platform/tests/css/css-images/image-set/image-set-resolution-001-ref.html b/testing/web-platform/tests/css/css-images/image-set/image-set-resolution-001-ref.html new file mode 100644 index 000000000000..46c4d729ed59 --- /dev/null +++ b/testing/web-platform/tests/css/css-images/image-set/image-set-resolution-001-ref.html @@ -0,0 +1,6 @@ + +CSS Test Reference + + diff --git a/testing/web-platform/tests/css/css-images/image-set/image-set-resolution-001.html b/testing/web-platform/tests/css/css-images/image-set/image-set-resolution-001.html new file mode 100644 index 000000000000..aaa750217d70 --- /dev/null +++ b/testing/web-platform/tests/css/css-images/image-set/image-set-resolution-001.html @@ -0,0 +1,16 @@ + +Image set resolution affects intrinsic size of the image + + + + + + +
diff --git a/testing/web-platform/tests/css/css-images/image-set/image-set-resolution-002.html b/testing/web-platform/tests/css/css-images/image-set/image-set-resolution-002.html new file mode 100644 index 000000000000..e5795e172f31 --- /dev/null +++ b/testing/web-platform/tests/css/css-images/image-set/image-set-resolution-002.html @@ -0,0 +1,20 @@ + +Image set resolution affects intrinsic size of the image + + + + + + +
diff --git a/testing/web-platform/tests/css/css-images/image-set/image-set-resolution-003.html b/testing/web-platform/tests/css/css-images/image-set/image-set-resolution-003.html new file mode 100644 index 000000000000..734ee05ecd18 --- /dev/null +++ b/testing/web-platform/tests/css/css-images/image-set/image-set-resolution-003.html @@ -0,0 +1,20 @@ + +Image set resolution affects intrinsic size of the image + + + + + + +
    +
  • +