зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1705877 - image-set() should influence intrinsic size of the image. r=dholbert,layout-reviewers
https://drafts.csswg.org/css-images-4/#image-set-notation has: > [...] it also specifies the image’s natural resolution, overriding any other > source of data that might supply a natural resolution. Astounding that there was literally no WPT for this at all. I added three: one for backgrounds, one for list-style-image, and one for `content`. Cursor is not handled on this patch because that one requires a fair amount of extra work. Differential Revision: https://phabricator.services.mozilla.com/D112474
This commit is contained in:
Родитель
ca0d2c502e
Коммит
8e47e5abc2
|
@ -6322,13 +6322,13 @@ ImgDrawResult nsLayoutUtils::DrawSingleUnscaledImage(
|
||||||
/* static */
|
/* static */
|
||||||
ImgDrawResult nsLayoutUtils::DrawSingleImage(
|
ImgDrawResult nsLayoutUtils::DrawSingleImage(
|
||||||
gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage,
|
gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage,
|
||||||
const SamplingFilter aSamplingFilter, const nsRect& aDest,
|
float aResolution, SamplingFilter aSamplingFilter, const nsRect& aDest,
|
||||||
const nsRect& aDirty, const Maybe<SVGImageContext>& aSVGContext,
|
const nsRect& aDirty, const Maybe<SVGImageContext>& aSVGContext,
|
||||||
uint32_t aImageFlags, const nsPoint* aAnchorPoint,
|
uint32_t aImageFlags, const nsPoint* aAnchorPoint,
|
||||||
const nsRect* aSourceArea) {
|
const nsRect* aSourceArea) {
|
||||||
nscoord appUnitsPerCSSPixel = AppUnitsPerCSSPixel();
|
nscoord appUnitsPerCSSPixel = AppUnitsPerCSSPixel();
|
||||||
CSSIntSize pixelImageSize(
|
CSSIntSize pixelImageSize(
|
||||||
ComputeSizeForDrawingWithFallback(aImage, aDest.Size()));
|
ComputeSizeForDrawingWithFallback(aImage, aResolution, aDest.Size()));
|
||||||
if (pixelImageSize.width < 1 || pixelImageSize.height < 1) {
|
if (pixelImageSize.width < 1 || pixelImageSize.height < 1) {
|
||||||
NS_ASSERTION(pixelImageSize.width >= 0 && pixelImageSize.height >= 0,
|
NS_ASSERTION(pixelImageSize.width >= 0 && pixelImageSize.height >= 0,
|
||||||
"Image width or height is negative");
|
"Image width or height is negative");
|
||||||
|
@ -6369,7 +6369,8 @@ ImgDrawResult nsLayoutUtils::DrawSingleImage(
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
void nsLayoutUtils::ComputeSizeForDrawing(
|
void nsLayoutUtils::ComputeSizeForDrawing(
|
||||||
imgIContainer* aImage, /* outparam */ CSSIntSize& aImageSize,
|
imgIContainer* aImage, float aResolution,
|
||||||
|
/* outparam */ CSSIntSize& aImageSize,
|
||||||
/* outparam */ AspectRatio& aIntrinsicRatio,
|
/* outparam */ AspectRatio& aIntrinsicRatio,
|
||||||
/* outparam */ bool& aGotWidth,
|
/* outparam */ bool& aGotWidth,
|
||||||
/* outparam */ bool& aGotHeight) {
|
/* outparam */ bool& aGotHeight) {
|
||||||
|
@ -6378,6 +6379,15 @@ void nsLayoutUtils::ComputeSizeForDrawing(
|
||||||
Maybe<AspectRatio> intrinsicRatio = aImage->GetIntrinsicRatio();
|
Maybe<AspectRatio> intrinsicRatio = aImage->GetIntrinsicRatio();
|
||||||
aIntrinsicRatio = intrinsicRatio.valueOr(AspectRatio());
|
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()) {
|
if (!(aGotWidth && aGotHeight) && intrinsicRatio.isNothing()) {
|
||||||
// We hit an error (say, because the image failed to load or couldn't be
|
// We hit an error (say, because the image failed to load or couldn't be
|
||||||
// decoded) and should return zero size.
|
// decoded) and should return zero size.
|
||||||
|
@ -6388,11 +6398,12 @@ void nsLayoutUtils::ComputeSizeForDrawing(
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
CSSIntSize nsLayoutUtils::ComputeSizeForDrawingWithFallback(
|
CSSIntSize nsLayoutUtils::ComputeSizeForDrawingWithFallback(
|
||||||
imgIContainer* aImage, const nsSize& aFallbackSize) {
|
imgIContainer* aImage, float aResolution, const nsSize& aFallbackSize) {
|
||||||
CSSIntSize imageSize;
|
CSSIntSize imageSize;
|
||||||
AspectRatio imageRatio;
|
AspectRatio imageRatio;
|
||||||
bool gotHeight, gotWidth;
|
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
|
// If we didn't get both width and height, try to compute them using the
|
||||||
// intrinsic ratio of the image.
|
// intrinsic ratio of the image.
|
||||||
|
|
|
@ -1901,6 +1901,11 @@ class nsLayoutUtils {
|
||||||
* appropriate scale and transform for drawing in
|
* appropriate scale and transform for drawing in
|
||||||
* app units.
|
* app units.
|
||||||
* @param aImage The image.
|
* @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 aDest The area that the image should fill.
|
||||||
* @param aDirty Pixels outside this area may be skipped.
|
* @param aDirty Pixels outside this area may be skipped.
|
||||||
* @param aSVGContext Optionally provides an SVGImageContext.
|
* @param aSVGContext Optionally provides an SVGImageContext.
|
||||||
|
@ -1922,7 +1927,7 @@ class nsLayoutUtils {
|
||||||
*/
|
*/
|
||||||
static ImgDrawResult DrawSingleImage(
|
static ImgDrawResult DrawSingleImage(
|
||||||
gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage,
|
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<SVGImageContext>& aSVGContext,
|
const nsRect& aDirty, const mozilla::Maybe<SVGImageContext>& aSVGContext,
|
||||||
uint32_t aImageFlags, const nsPoint* aAnchorPoint = nullptr,
|
uint32_t aImageFlags, const nsPoint* aAnchorPoint = nullptr,
|
||||||
const nsRect* aSourceArea = 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
|
* difference is that this one is simpler and is suited to places where we
|
||||||
* have less information about the frame tree.
|
* have less information about the frame tree.
|
||||||
*/
|
*/
|
||||||
static void ComputeSizeForDrawing(imgIContainer* aImage,
|
static void ComputeSizeForDrawing(imgIContainer* aImage, float aResolution,
|
||||||
CSSIntSize& aImageSize,
|
CSSIntSize& aImageSize,
|
||||||
AspectRatio& aIntrinsicRatio,
|
AspectRatio& aIntrinsicRatio,
|
||||||
bool& aGotWidth, bool& aGotHeight);
|
bool& aGotWidth, bool& aGotHeight);
|
||||||
|
@ -1957,7 +1962,7 @@ class nsLayoutUtils {
|
||||||
* dimensions, the corresponding dimension of aFallbackSize is used instead.
|
* dimensions, the corresponding dimension of aFallbackSize is used instead.
|
||||||
*/
|
*/
|
||||||
static CSSIntSize ComputeSizeForDrawingWithFallback(
|
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
|
* Given the image container, frame, and dest rect, determine the best fitting
|
||||||
|
|
|
@ -781,12 +781,9 @@ void nsBulletFrame::GetDesiredSize(nsPresContext* aCX,
|
||||||
|
|
||||||
RemoveStateBits(BULLET_FRAME_IMAGE_LOADING);
|
RemoveStateBits(BULLET_FRAME_IMAGE_LOADING);
|
||||||
|
|
||||||
if (RefPtr<imgIContainer> image = GetImage()) {
|
if (Maybe<CSSIntSize> intrinsicSize =
|
||||||
int32_t w = 0;
|
StyleList()->mListStyleImage.GetIntrinsicSize()) {
|
||||||
int32_t h = 0;
|
LogicalSize size(GetWritingMode(), CSSPixel::ToAppUnits(*intrinsicSize));
|
||||||
image->GetWidth(&w);
|
|
||||||
image->GetHeight(&h);
|
|
||||||
LogicalSize size(GetWritingMode(), CSSPixel::ToAppUnits(CSSIntSize(w, h)));
|
|
||||||
// auto size the image
|
// auto size the image
|
||||||
finalSize.ISize(wm) = size.ISize(wm);
|
finalSize.ISize(wm) = size.ISize(wm);
|
||||||
finalSize.BSize(wm) = size.BSize(wm);
|
finalSize.BSize(wm) = size.BSize(wm);
|
||||||
|
|
|
@ -398,11 +398,13 @@ void nsImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
|
||||||
}
|
}
|
||||||
MOZ_RELEASE_ASSERT(contentIndex < styleContent->ContentCount());
|
MOZ_RELEASE_ASSERT(contentIndex < styleContent->ContentCount());
|
||||||
MOZ_RELEASE_ASSERT(styleContent->ContentAt(contentIndex).IsImage());
|
MOZ_RELEASE_ASSERT(styleContent->ContentAt(contentIndex).IsImage());
|
||||||
auto& imageUrl = styleContent->ContentAt(contentIndex).AsImage();
|
const StyleImage& image = styleContent->ContentAt(contentIndex).AsImage();
|
||||||
MOZ_ASSERT(imageUrl.IsImageRequestType(),
|
MOZ_ASSERT(image.IsImageRequestType(),
|
||||||
"Content image should only parse url() type");
|
"Content image should only parse url() type");
|
||||||
|
auto [finalImage, resolution] = image.FinalImageAndResolution();
|
||||||
Document* doc = PresContext()->Document();
|
Document* doc = PresContext()->Document();
|
||||||
if (imgRequestProxy* proxy = imageUrl.GetImageRequest()) {
|
if (imgRequestProxy* proxy = finalImage->GetImageRequest()) {
|
||||||
|
mContentURLRequestResolution = resolution;
|
||||||
proxy->Clone(mListener, doc, getter_AddRefs(mContentURLRequest));
|
proxy->Clone(mListener, doc, getter_AddRefs(mContentURLRequest));
|
||||||
SetupForContentURLRequest();
|
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,
|
static void ScaleIntrinsicSizeForDensity(nsIContent& aContent,
|
||||||
IntrinsicSize& aSize) {
|
IntrinsicSize& aSize) {
|
||||||
auto* image = HTMLImageElement::FromNode(aContent);
|
auto* image = HTMLImageElement::FromNode(aContent);
|
||||||
|
@ -468,16 +484,7 @@ static void ScaleIntrinsicSizeForDensity(nsIContent& aContent,
|
||||||
|
|
||||||
double density = selector->GetSelectedImageDensity();
|
double density = selector->GetSelectedImageDensity();
|
||||||
MOZ_ASSERT(density >= 0.0);
|
MOZ_ASSERT(density >= 0.0);
|
||||||
if (density == 1.0) {
|
ScaleIntrinsicSizeForDensity(aSize, density);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aSize.width) {
|
|
||||||
aSize.width = Some(NSToCoordRound(double(*aSize.width) / density));
|
|
||||||
}
|
|
||||||
if (aSize.height) {
|
|
||||||
aSize.height = Some(NSToCoordRound(double(*aSize.height) / density));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage,
|
static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage,
|
||||||
|
@ -496,6 +503,9 @@ static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage,
|
||||||
intrinsicSize.height = size.height == -1 ? Nothing() : Some(size.height);
|
intrinsicSize.height = size.height == -1 ? Nothing() : Some(size.height);
|
||||||
if (aKind == nsImageFrame::Kind::ImageElement) {
|
if (aKind == nsImageFrame::Kind::ImageElement) {
|
||||||
ScaleIntrinsicSizeForDensity(*aFrame.GetContent(), intrinsicSize);
|
ScaleIntrinsicSizeForDensity(*aFrame.GetContent(), intrinsicSize);
|
||||||
|
} else {
|
||||||
|
ScaleIntrinsicSizeForDensity(intrinsicSize,
|
||||||
|
aFrame.GetContentURLRequestResolution());
|
||||||
}
|
}
|
||||||
return intrinsicSize;
|
return intrinsicSize;
|
||||||
}
|
}
|
||||||
|
@ -1476,7 +1486,7 @@ ImgDrawResult nsImageFrame::DisplayAltFeedback(gfxContext& aRenderingContext,
|
||||||
nsRect dest(flushRight ? inner.XMost() - size : inner.x, inner.y, size,
|
nsRect dest(flushRight ? inner.XMost() - size : inner.x, inner.y, size,
|
||||||
size);
|
size);
|
||||||
result = nsLayoutUtils::DrawSingleImage(
|
result = nsLayoutUtils::DrawSingleImage(
|
||||||
aRenderingContext, PresContext(), imgCon,
|
aRenderingContext, PresContext(), imgCon, /* aResolution = */ 1.0f,
|
||||||
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
|
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
|
||||||
/* no SVGImageContext */ Nothing(), aFlags);
|
/* no SVGImageContext */ Nothing(), aFlags);
|
||||||
}
|
}
|
||||||
|
@ -2080,8 +2090,10 @@ ImgDrawResult nsImageFrame::PaintImage(gfxContext& aRenderingContext,
|
||||||
Maybe<SVGImageContext> svgContext;
|
Maybe<SVGImageContext> svgContext;
|
||||||
SVGImageContext::MaybeStoreContextPaint(svgContext, this, aImage);
|
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(
|
ImgDrawResult result = nsLayoutUtils::DrawSingleImage(
|
||||||
aRenderingContext, PresContext(), aImage,
|
aRenderingContext, PresContext(), aImage, /* aResolution = */ 1.0f,
|
||||||
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
|
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
|
||||||
svgContext, flags, &anchorPoint);
|
svgContext, flags, &anchorPoint);
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,12 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
|
||||||
void SetupForContentURLRequest();
|
void SetupForContentURLRequest();
|
||||||
bool ShouldShowBrokenImageIcon() const;
|
bool ShouldShowBrokenImageIcon() const;
|
||||||
|
|
||||||
|
// Get the resolution, in dppx, for the image that mContentURLRequest
|
||||||
|
// represents.
|
||||||
|
float GetContentURLRequestResolution() const {
|
||||||
|
return mContentURLRequestResolution;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef ACCESSIBILITY
|
#ifdef ACCESSIBILITY
|
||||||
mozilla::a11y::AccType AccessibleType() override;
|
mozilla::a11y::AccType AccessibleType() override;
|
||||||
#endif
|
#endif
|
||||||
|
@ -353,6 +359,7 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
|
||||||
|
|
||||||
// An image request created for content: url(..).
|
// An image request created for content: url(..).
|
||||||
RefPtr<imgRequestProxy> mContentURLRequest;
|
RefPtr<imgRequestProxy> mContentURLRequest;
|
||||||
|
float mContentURLRequestResolution = 1.0f;
|
||||||
|
|
||||||
nsCOMPtr<imgIContainer> mImage;
|
nsCOMPtr<imgIContainer> mImage;
|
||||||
nsCOMPtr<imgIContainer> mPrevImage;
|
nsCOMPtr<imgIContainer> mPrevImage;
|
||||||
|
|
|
@ -50,8 +50,6 @@ nsSize CSSSizeOrRatio::ComputeConcreteSize() const {
|
||||||
nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage,
|
nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage,
|
||||||
uint32_t aFlags)
|
uint32_t aFlags)
|
||||||
: mForFrame(aForFrame),
|
: mForFrame(aForFrame),
|
||||||
mImage(&aImage->FinalImage()),
|
|
||||||
mType(mImage->tag),
|
|
||||||
mImageContainer(nullptr),
|
mImageContainer(nullptr),
|
||||||
mGradientData(nullptr),
|
mGradientData(nullptr),
|
||||||
mPaintServerFrame(nullptr),
|
mPaintServerFrame(nullptr),
|
||||||
|
@ -59,7 +57,12 @@ nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage,
|
||||||
mSize(0, 0),
|
mSize(0, 0),
|
||||||
mFlags(aFlags),
|
mFlags(aFlags),
|
||||||
mExtendMode(ExtendMode::CLAMP),
|
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() {
|
bool nsImageRenderer::PrepareImage() {
|
||||||
if (mImage->IsNone()) {
|
if (mImage->IsNone()) {
|
||||||
|
@ -198,14 +201,14 @@ CSSSizeOrRatio nsImageRenderer::ComputeIntrinsicSize() {
|
||||||
case StyleImage::Tag::Url: {
|
case StyleImage::Tag::Url: {
|
||||||
bool haveWidth, haveHeight;
|
bool haveWidth, haveHeight;
|
||||||
CSSIntSize imageIntSize;
|
CSSIntSize imageIntSize;
|
||||||
nsLayoutUtils::ComputeSizeForDrawing(
|
nsLayoutUtils::ComputeSizeForDrawing(mImageContainer, mImageResolution,
|
||||||
mImageContainer, imageIntSize, result.mRatio, haveWidth, haveHeight);
|
imageIntSize, result.mRatio,
|
||||||
|
haveWidth, haveHeight);
|
||||||
if (haveWidth) {
|
if (haveWidth) {
|
||||||
result.SetWidth(nsPresContext::CSSPixelsToAppUnits(imageIntSize.width));
|
result.SetWidth(CSSPixel::ToAppUnits(imageIntSize.width));
|
||||||
}
|
}
|
||||||
if (haveHeight) {
|
if (haveHeight) {
|
||||||
result.SetHeight(
|
result.SetHeight(CSSPixel::ToAppUnits(imageIntSize.height));
|
||||||
nsPresContext::CSSPixelsToAppUnits(imageIntSize.height));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we know the aspect ratio and one of the dimensions,
|
// If we know the aspect ratio and one of the dimensions,
|
||||||
|
@ -937,8 +940,8 @@ ImgDrawResult nsImageRenderer::DrawBorderImageComponent(
|
||||||
|
|
||||||
if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) {
|
if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) {
|
||||||
ImgDrawResult result = nsLayoutUtils::DrawSingleImage(
|
ImgDrawResult result = nsLayoutUtils::DrawSingleImage(
|
||||||
aRenderingContext, aPresContext, subImage, samplingFilter, aFill,
|
aRenderingContext, aPresContext, subImage, mImageResolution,
|
||||||
aDirtyRect,
|
samplingFilter, aFill, aDirtyRect,
|
||||||
/* no SVGImageContext */ Nothing(), drawFlags);
|
/* no SVGImageContext */ Nothing(), drawFlags);
|
||||||
|
|
||||||
if (!mImage->IsComplete()) {
|
if (!mImage->IsComplete()) {
|
||||||
|
@ -1007,8 +1010,9 @@ ImgDrawResult nsImageRenderer::DrawShapeImage(nsPresContext* aPresContext,
|
||||||
// rendered pixel has an alpha that precisely matches the alpha of the
|
// rendered pixel has an alpha that precisely matches the alpha of the
|
||||||
// closest pixel in the image.
|
// closest pixel in the image.
|
||||||
return nsLayoutUtils::DrawSingleImage(
|
return nsLayoutUtils::DrawSingleImage(
|
||||||
aRenderingContext, aPresContext, mImageContainer, SamplingFilter::POINT,
|
aRenderingContext, aPresContext, mImageContainer, mImageResolution,
|
||||||
dest, dest, Nothing(), drawFlags, nullptr, nullptr);
|
SamplingFilter::POINT, dest, dest, Nothing(), drawFlags, nullptr,
|
||||||
|
nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mImage->IsGradient()) {
|
if (mImage->IsGradient()) {
|
||||||
|
|
|
@ -299,6 +299,7 @@ class nsImageRenderer {
|
||||||
|
|
||||||
nsIFrame* mForFrame;
|
nsIFrame* mForFrame;
|
||||||
const mozilla::StyleImage* mImage;
|
const mozilla::StyleImage* mImage;
|
||||||
|
float mImageResolution;
|
||||||
mozilla::StyleImage::Tag mType;
|
mozilla::StyleImage::Tag mType;
|
||||||
nsCOMPtr<imgIContainer> mImageContainer;
|
nsCOMPtr<imgIContainer> mImageContainer;
|
||||||
const mozilla::StyleGradient* mGradientData;
|
const mozilla::StyleGradient* mGradientData;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "mozilla/ServoBindingTypes.h"
|
#include "mozilla/ServoBindingTypes.h"
|
||||||
#include "mozilla/css/DocumentMatchingFunction.h"
|
#include "mozilla/css/DocumentMatchingFunction.h"
|
||||||
#include "mozilla/css/SheetLoadData.h"
|
#include "mozilla/css/SheetLoadData.h"
|
||||||
|
#include "mozilla/dom/Document.h"
|
||||||
#include "mozilla/EffectCompositor.h"
|
#include "mozilla/EffectCompositor.h"
|
||||||
#include "mozilla/ComputedTimingFunction.h"
|
#include "mozilla/ComputedTimingFunction.h"
|
||||||
#include "mozilla/PreferenceSheet.h"
|
#include "mozilla/PreferenceSheet.h"
|
||||||
|
|
|
@ -942,14 +942,25 @@ inline bool RestyleHint::DefinitelyRecascadesAllSubtree() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline const StyleImage& StyleImage::FinalImage() const {
|
inline std::pair<const StyleImage*, float> StyleImage::FinalImageAndResolution()
|
||||||
|
const {
|
||||||
if (!IsImageSet()) {
|
if (!IsImageSet()) {
|
||||||
return *this;
|
return {this, 1.0f};
|
||||||
}
|
}
|
||||||
auto& set = AsImageSet();
|
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<CSSIntSize> StyleImage::GetIntrinsicSize() const;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline bool StyleImage::IsImageRequestType() const {
|
inline bool StyleImage::IsImageRequestType() const {
|
||||||
auto& finalImage = FinalImage();
|
auto& finalImage = FinalImage();
|
||||||
|
|
|
@ -1629,6 +1629,31 @@ void StyleImage::ResolveImage(Document& aDoc, const StyleImage* aOld) {
|
||||||
const_cast<StyleComputedImageUrl*>(url)->ResolveImage(aDoc, old);
|
const_cast<StyleComputedImageUrl*>(url)->ResolveImage(aDoc, old);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
Maybe<CSSIntSize> StyleImage::GetIntrinsicSize() const {
|
||||||
|
auto [finalImage, resolution] = FinalImageAndResolution();
|
||||||
|
imgRequestProxy* request = finalImage->GetImageRequest();
|
||||||
|
if (!request) {
|
||||||
|
return Nothing();
|
||||||
|
}
|
||||||
|
RefPtr<imgIContainer> 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
|
// nsStyleImageLayers
|
||||||
//
|
//
|
||||||
|
@ -1882,8 +1907,11 @@ static bool SizeDependsOnPositioningAreaSize(const StyleBackgroundSize& aSize,
|
||||||
CSSIntSize imageSize;
|
CSSIntSize imageSize;
|
||||||
AspectRatio imageRatio;
|
AspectRatio imageRatio;
|
||||||
bool hasWidth, hasHeight;
|
bool hasWidth, hasHeight;
|
||||||
nsLayoutUtils::ComputeSizeForDrawing(imgContainer, imageSize, imageRatio,
|
// We could bother getting the right resolution here but it doesn't matter
|
||||||
hasWidth, hasHeight);
|
// 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
|
// If the image has a fixed width and height, rendering never depends on
|
||||||
// the frame size.
|
// the frame size.
|
||||||
|
|
|
@ -405,7 +405,7 @@ void SVGImageFrame::PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform,
|
||||||
// That method needs our image to have a fixed native width & height,
|
// That method needs our image to have a fixed native width & height,
|
||||||
// and that's not always true for TYPE_VECTOR images.
|
// and that's not always true for TYPE_VECTOR images.
|
||||||
aImgParams.result &= nsLayoutUtils::DrawSingleImage(
|
aImgParams.result &= nsLayoutUtils::DrawSingleImage(
|
||||||
aContext, PresContext(), mImageContainer,
|
aContext, PresContext(), mImageContainer, /* aResolution = */ 1.0f,
|
||||||
nsLayoutUtils::GetSamplingFilterForFrame(this), destRect,
|
nsLayoutUtils::GetSamplingFilterForFrame(this), destRect,
|
||||||
aDirtyRect ? dirtyRect : destRect, context, flags);
|
aDirtyRect ? dirtyRect : destRect, context, flags);
|
||||||
} else { // mImageContainer->GetType() == TYPE_RASTER
|
} else { // mImageContainer->GetType() == TYPE_RASTER
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "nsStyleConsts.h"
|
#include "nsStyleConsts.h"
|
||||||
#include "nsStyleUtil.h"
|
#include "nsStyleUtil.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
|
#include "nsLayoutUtils.h"
|
||||||
#include "nsPresContext.h"
|
#include "nsPresContext.h"
|
||||||
#include "nsBoxLayoutState.h"
|
#include "nsBoxLayoutState.h"
|
||||||
|
|
||||||
|
@ -239,6 +240,7 @@ void nsImageBoxFrame::UpdateImage() {
|
||||||
mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
|
mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
|
||||||
mUseSrcAttr = !src.IsEmpty();
|
mUseSrcAttr = !src.IsEmpty();
|
||||||
if (mUseSrcAttr) {
|
if (mUseSrcAttr) {
|
||||||
|
mImageResolution = 1.0f;
|
||||||
nsContentPolicyType contentPolicyType;
|
nsContentPolicyType contentPolicyType;
|
||||||
nsCOMPtr<nsIPrincipal> triggeringPrincipal;
|
nsCOMPtr<nsIPrincipal> triggeringPrincipal;
|
||||||
uint64_t requestContextID = 0;
|
uint64_t requestContextID = 0;
|
||||||
|
@ -267,10 +269,14 @@ void nsImageBoxFrame::UpdateImage() {
|
||||||
doc->ImageTracker()->Add(mImageRequest);
|
doc->ImageTracker()->Add(mImageRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (auto* styleRequest = GetRequestFromStyle()) {
|
} else if (auto* styleImage = GetImageFromStyle()) {
|
||||||
|
auto [finalImage, resolution] = styleImage->FinalImageAndResolution();
|
||||||
|
mImageResolution = resolution;
|
||||||
|
if (auto* styleRequest = finalImage->GetImageRequest()) {
|
||||||
styleRequest->SyncClone(mListener, mContent->GetComposedDoc(),
|
styleRequest->SyncClone(mListener, mContent->GetComposedDoc(),
|
||||||
getter_AddRefs(mImageRequest));
|
getter_AddRefs(mImageRequest));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!mImageRequest) {
|
if (!mImageRequest) {
|
||||||
// We have no image, so size to 0
|
// We have no image, so size to 0
|
||||||
|
@ -388,7 +394,7 @@ ImgDrawResult nsImageBoxFrame::PaintImage(gfxContext& aRenderingContext,
|
||||||
Maybe<SVGImageContext> svgContext;
|
Maybe<SVGImageContext> svgContext;
|
||||||
SVGImageContext::MaybeStoreContextPaint(svgContext, this, imgCon);
|
SVGImageContext::MaybeStoreContextPaint(svgContext, this, imgCon);
|
||||||
return nsLayoutUtils::DrawSingleImage(
|
return nsLayoutUtils::DrawSingleImage(
|
||||||
aRenderingContext, PresContext(), imgCon,
|
aRenderingContext, PresContext(), imgCon, mImageResolution,
|
||||||
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, dirty, svgContext,
|
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, dirty, svgContext,
|
||||||
aFlags, anchorPoint.ptrOr(nullptr), hasSubRect ? &mSubRect : nullptr);
|
aFlags, anchorPoint.ptrOr(nullptr), hasSubRect ? &mSubRect : nullptr);
|
||||||
}
|
}
|
||||||
|
@ -603,8 +609,9 @@ bool nsImageBoxFrame::CanOptimizeToImageLayer() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
imgRequestProxy* nsImageBoxFrame::GetRequestFromStyle() {
|
const mozilla::StyleImage* nsImageBoxFrame::GetImageFromStyle(
|
||||||
const nsStyleDisplay* disp = StyleDisplay();
|
const ComputedStyle& aStyle) {
|
||||||
|
const nsStyleDisplay* disp = aStyle.StyleDisplay();
|
||||||
if (disp->HasAppearance()) {
|
if (disp->HasAppearance()) {
|
||||||
nsPresContext* pc = PresContext();
|
nsPresContext* pc = PresContext();
|
||||||
if (pc->Theme()->ThemeSupportsWidget(pc, this,
|
if (pc->Theme()->ThemeSupportsWidget(pc, this,
|
||||||
|
@ -612,7 +619,11 @@ imgRequestProxy* nsImageBoxFrame::GetRequestFromStyle() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return StyleList()->mListStyleImage.GetImageRequest();
|
auto& image = aStyle.StyleList()->mListStyleImage;
|
||||||
|
if (!image.IsImageRequestType()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return ℑ
|
||||||
}
|
}
|
||||||
|
|
||||||
/* virtual */
|
/* virtual */
|
||||||
|
@ -623,26 +634,18 @@ void nsImageBoxFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
|
||||||
const nsStyleList* myList = StyleList();
|
const nsStyleList* myList = StyleList();
|
||||||
mSubRect = myList->GetImageRegion(); // before |mSuppressStyleCheck| test!
|
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.
|
return; // No more work required, since the image isn't specified by style.
|
||||||
|
}
|
||||||
|
|
||||||
// If the image to use changes, we have a new image.
|
auto* oldImage = aOldStyle ? GetImageFromStyle(*aOldStyle) : nullptr;
|
||||||
nsCOMPtr<nsIURI> oldURI, newURI;
|
auto* newImage = GetImageFromStyle();
|
||||||
if (mImageRequest) {
|
if (newImage == oldImage ||
|
||||||
mImageRequest->GetURI(getter_AddRefs(oldURI));
|
(newImage && oldImage && *oldImage == *newImage)) {
|
||||||
}
|
|
||||||
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)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateImage();
|
UpdateImage();
|
||||||
} // DidSetComputedStyle
|
}
|
||||||
|
|
||||||
void nsImageBoxFrame::GetImageSize() {
|
void nsImageBoxFrame::GetImageSize() {
|
||||||
if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) {
|
if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) {
|
||||||
|
@ -796,12 +799,16 @@ void nsImageBoxFrame::OnSizeAvailable(imgIRequest* aRequest,
|
||||||
|
|
||||||
aImage->SetAnimationMode(PresContext()->ImageAnimationMode());
|
aImage->SetAnimationMode(PresContext()->ImageAnimationMode());
|
||||||
|
|
||||||
nscoord w, h;
|
int32_t w = 0, h = 0;
|
||||||
aImage->GetWidth(&w);
|
aImage->GetWidth(&w);
|
||||||
aImage->GetHeight(&h);
|
aImage->GetHeight(&h);
|
||||||
|
|
||||||
mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(w),
|
if (mImageResolution != 0.0f && mImageResolution != 1.0f) {
|
||||||
nsPresContext::CSSPixelsToAppUnits(h));
|
w = std::round(w / mImageResolution);
|
||||||
|
h = std::round(h / mImageResolution);
|
||||||
|
}
|
||||||
|
|
||||||
|
mIntrinsicSize.SizeTo(CSSPixel::ToAppUnits(w), CSSPixel::ToAppUnits(h));
|
||||||
|
|
||||||
if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
|
if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
|
||||||
PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
|
PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
|
||||||
|
|
|
@ -73,11 +73,15 @@ class nsImageBoxFrame final : public nsLeafBoxFrame {
|
||||||
#endif
|
#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
|
* Update mUseSrcAttr from appropriate content attributes or from
|
||||||
|
@ -137,6 +141,7 @@ class nsImageBoxFrame final : public nsLeafBoxFrame {
|
||||||
nsSize mImageSize;
|
nsSize mImageSize;
|
||||||
|
|
||||||
RefPtr<imgRequestProxy> mImageRequest;
|
RefPtr<imgRequestProxy> mImageRequest;
|
||||||
|
float mImageResolution = 1.0f;
|
||||||
nsCOMPtr<imgINotificationObserver> mListener;
|
nsCOMPtr<imgINotificationObserver> mListener;
|
||||||
|
|
||||||
int32_t mLoadFlags;
|
int32_t mLoadFlags;
|
||||||
|
|
|
@ -790,6 +790,17 @@ renaming_overrides_prefixing = true
|
||||||
|
|
||||||
"GenericImage" = """
|
"GenericImage" = """
|
||||||
public:
|
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<const StyleGenericImage*, float> FinalImageAndResolution() const;
|
||||||
|
|
||||||
|
// Returns the intrinsic size of the image, if there's one, accounting for
|
||||||
|
// resolution as needed.
|
||||||
|
Maybe<CSSIntSize> GetIntrinsicSize() const;
|
||||||
|
|
||||||
// If this is an image-set(), the final image we've selected, otherwise it
|
// If this is an image-set(), the final image we've selected, otherwise it
|
||||||
// returns *this.
|
// returns *this.
|
||||||
const StyleGenericImage& FinalImage() const;
|
const StyleGenericImage& FinalImage() const;
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<!doctype html>
|
||||||
|
<title>CSS Test Reference</title>
|
||||||
|
<style>
|
||||||
|
body { margin: 0 }
|
||||||
|
</style>
|
||||||
|
<img srcset="/images/green.png 0.5x">
|
|
@ -0,0 +1,16 @@
|
||||||
|
<!doctype html>
|
||||||
|
<title>Image set resolution affects intrinsic size of the image</title>
|
||||||
|
<link rel="match" href="image-set-resolution-001-ref.html">
|
||||||
|
<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
|
||||||
|
<link rel="author" href="https://mozilla.org" title="Mozilla">
|
||||||
|
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1705877">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-images-4/#image-set-notation">
|
||||||
|
<style>
|
||||||
|
body { margin: 0 }
|
||||||
|
div {
|
||||||
|
/* green.png is 100x50, should be 200x100 instead */
|
||||||
|
content: -webkit-image-set(url('/images/green.png') 0.5x);
|
||||||
|
content: image-set(url('/images/green.png') 0.5x);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div></div>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!doctype html>
|
||||||
|
<title>Image set resolution affects intrinsic size of the image</title>
|
||||||
|
<link rel="match" href="image-set-resolution-001-ref.html">
|
||||||
|
<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
|
||||||
|
<link rel="author" href="https://mozilla.org" title="Mozilla">
|
||||||
|
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1705877">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-images-4/#image-set-notation">
|
||||||
|
<style>
|
||||||
|
body { margin: 0 }
|
||||||
|
div {
|
||||||
|
/* green.png is 100x50, should be 200x100 instead */
|
||||||
|
background-image: -webkit-image-set(url('/images/green.png') 0.5x);
|
||||||
|
background-image: image-set(url('/images/green.png') 0.5x);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-origin: 0 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div></div>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!doctype html>
|
||||||
|
<title>Image set resolution affects intrinsic size of the image</title>
|
||||||
|
<link rel="match" href="image-set-resolution-001-ref.html">
|
||||||
|
<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
|
||||||
|
<link rel="author" href="https://mozilla.org" title="Mozilla">
|
||||||
|
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1705877">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-images-4/#image-set-notation">
|
||||||
|
<style>
|
||||||
|
body { margin: 0 }
|
||||||
|
ul, li { margin: 0; padding: 0 }
|
||||||
|
li {
|
||||||
|
list-style-position: inside;
|
||||||
|
/* green.png is 100x50, should be 200x100 instead */
|
||||||
|
list-style-image: -webkit-image-set(url('/images/green.png') 0.5x);
|
||||||
|
list-style-image: image-set(url('/images/green.png') 0.5x);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<ul>
|
||||||
|
<li></li>
|
||||||
|
</ul>
|
Загрузка…
Ссылка в новой задаче