Bug 1680387 - Apply intrinsic image resolution as appropriate in layout/style/dom, and update test expectations. r=tnikkel

This should be mostly straight-forward, since we have code for this
anyways for image-set() and srcset.

The only thing is that we were using floats for resolution, but since
EXIF allows you to scale each axis separately, we now need to pass an
image::Resolution instead.

The main outstanding issue is the spec comment mentioned in the previous
patch, about what happens if you have srcset/image-set and the image
density specified together. For now I've implemented what the
image-set() spec says, but this is subject to change before shipping of
course.

Differential Revision: https://phabricator.services.mozilla.com/D113265
This commit is contained in:
Emilio Cobos Álvarez 2021-05-05 09:41:24 +00:00
parent 6c4266f7f7
commit 1cdf344860
38 changed files with 245 additions and 244 deletions

View File

@ -288,7 +288,7 @@ bool PointerLockManager::SetPointerLock(Element* aElement, Document* aDocument,
// Hide the cursor and set pointer lock for future mouse events
RefPtr<EventStateManager> esm = presContext->EventStateManager();
esm->SetCursor(aCursorStyle, nullptr, 1.0f, Nothing(), widget, true);
esm->SetCursor(aCursorStyle, nullptr, {}, Nothing(), widget, true);
EventStateManager::SetPointerLock(widget, aElement);
return true;

View File

@ -7426,7 +7426,7 @@ void nsGlobalWindowOuter::SetCursorOuter(const nsACString& aCursor,
// Call esm and set cursor.
aError = presContext->EventStateManager()->SetCursor(
cursor, nullptr, 1.0f, Nothing(), widget, true);
cursor, nullptr, {}, Nothing(), widget, true);
}
}

View File

@ -1216,32 +1216,6 @@ void nsImageLoadingContent::ForceImageState(bool aForce,
mForcedImageState = EventStates(aState);
}
uint32_t nsImageLoadingContent::NaturalWidth() {
nsCOMPtr<imgIContainer> image;
if (mCurrentRequest) {
mCurrentRequest->GetImage(getter_AddRefs(image));
}
int32_t size = 0;
if (image) {
Unused << image->GetWidth(&size);
}
return size;
}
uint32_t nsImageLoadingContent::NaturalHeight() {
nsCOMPtr<imgIContainer> image;
if (mCurrentRequest) {
mCurrentRequest->GetImage(getter_AddRefs(image));
}
int32_t size = 0;
if (image) {
Unused << image->GetHeight(&size);
}
return size;
}
CSSIntSize nsImageLoadingContent::GetWidthHeightForImage() {
Element* element = AsContent()->AsElement();
if (nsIFrame* frame = element->GetPrimaryFrame(FlushType::Layout)) {

View File

@ -232,9 +232,6 @@ class nsImageLoadingContent : public nsIImageLoadingContent {
// want a non-const nsIContent.
virtual nsIContent* AsContent() = 0;
// Hooks for subclasses to call to get the intrinsic width and height.
uint32_t NaturalWidth();
uint32_t NaturalHeight();
/**
* Get width and height of the current request, using given image request if
* attributes are unset.

View File

@ -3906,7 +3906,7 @@ void EventStateManager::ClearFrameRefs(nsIFrame* aFrame) {
struct CursorImage {
gfx::IntPoint mHotspot;
nsCOMPtr<imgIContainer> mContainer;
float mResolution = 1.0f;
ImageResolution mResolution;
bool mEarlierCursorLoading = false;
};
@ -3934,10 +3934,7 @@ static bool ShouldBlockCustomCursor(nsPresContext* aPresContext,
int32_t height = 0;
aCursor.mContainer->GetWidth(&width);
aCursor.mContainer->GetHeight(&height);
if (aCursor.mResolution != 0.0f && aCursor.mResolution != 1.0f) {
width = std::round(width / aCursor.mResolution);
height = std::round(height / aCursor.mResolution);
}
aCursor.mResolution.ApplyTo(width, height);
int32_t maxSize = StaticPrefs::layout_cursor_block_max_size();
@ -4010,8 +4007,7 @@ static CursorImage ComputeCustomCursor(nsPresContext* aPresContext,
MOZ_ASSERT(image.image.IsImageRequestType(),
"Cursor image should only parse url() types");
uint32_t status;
auto [finalImage, resolution] = image.image.FinalImageAndResolution();
imgRequestProxy* req = finalImage->GetImageRequest();
imgRequestProxy* req = image.image.GetImageRequest();
if (!req || NS_FAILED(req->GetImageStatus(&status))) {
continue;
}
@ -4033,14 +4029,15 @@ static CursorImage ComputeCustomCursor(nsPresContext* aPresContext,
image.has_hotspot ? Some(gfx::Point{image.hotspot_x, image.hotspot_y})
: Nothing();
gfx::IntPoint hotspot = ComputeHotspot(container, specifiedHotspot);
CursorImage result{hotspot, std::move(container), resolution, loading};
CursorImage result{hotspot, std::move(container),
image.image.GetResolution(), loading};
if (ShouldBlockCustomCursor(aPresContext, aEvent, result)) {
continue;
}
// This is the one we want!
return result;
}
return {{}, nullptr, 1.0f, loading};
return {{}, nullptr, {}, loading};
}
void EventStateManager::UpdateCursor(nsPresContext* aPresContext,
@ -4053,7 +4050,7 @@ void EventStateManager::UpdateCursor(nsPresContext* aPresContext,
auto cursor = StyleCursorKind::Default;
nsCOMPtr<imgIContainer> container;
float resolution = 1.0f;
ImageResolution resolution;
Maybe<gfx::IntPoint> hotspot;
// If cursor is locked just use the locked one
@ -4138,7 +4135,7 @@ void EventStateManager::ClearCachedWidgetCursor(nsIFrame* aTargetFrame) {
nsresult EventStateManager::SetCursor(StyleCursorKind aCursor,
imgIContainer* aContainer,
float aResolution,
const ImageResolution& aResolution,
const Maybe<gfx::IntPoint>& aHotspot,
nsIWidget* aWidget, bool aLockCursor) {
EnsureDocument(mPresContext);

View File

@ -243,9 +243,9 @@ class EventStateManager : public nsSupportsWeakReference, public nsIObserver {
bool CheckIfEventMatchesAccessKey(WidgetKeyboardEvent* aEvent,
nsPresContext* aPresContext);
nsresult SetCursor(StyleCursorKind aCursor, imgIContainer* aContainer,
float aResolution, const Maybe<gfx::IntPoint>& aHotspot,
nsIWidget* aWidget, bool aLockCursor);
nsresult SetCursor(StyleCursorKind, imgIContainer*, const ImageResolution&,
const Maybe<gfx::IntPoint>& aHotspot, nsIWidget* aWidget,
bool aLockCursor);
/**
* Checks if the current mouse over element matches the given

View File

@ -710,28 +710,33 @@ uint32_t HTMLImageElement::Height() { return GetWidthHeightForImage().height; }
uint32_t HTMLImageElement::Width() { return GetWidthHeightForImage().width; }
uint32_t HTMLImageElement::NaturalHeight() {
uint32_t height = nsImageLoadingContent::NaturalHeight();
if (mResponsiveSelector) {
double density = mResponsiveSelector->GetSelectedImageDensity();
MOZ_ASSERT(density >= 0.0);
height = NSToIntRound(double(height) / density);
nsIntSize HTMLImageElement::NaturalSize() {
if (!mCurrentRequest) {
return {};
}
return height;
}
uint32_t HTMLImageElement::NaturalWidth() {
uint32_t width = nsImageLoadingContent::NaturalWidth();
if (mResponsiveSelector) {
double density = mResponsiveSelector->GetSelectedImageDensity();
MOZ_ASSERT(density >= 0.0);
width = NSToIntRound(double(width) / density);
nsCOMPtr<imgIContainer> image;
mCurrentRequest->GetImage(getter_AddRefs(image));
if (!image) {
return {};
}
return width;
nsIntSize size;
Unused << image->GetHeight(&size.height);
Unused << image->GetWidth(&size.width);
ImageResolution resolution = image->GetResolution();
// NOTE(emilio): What we implement here matches the image-set() spec, but it's
// unclear whether this is the right thing to do, see
// https://github.com/whatwg/html/pull/5574#issuecomment-826335244.
if (mResponsiveSelector) {
float density = mResponsiveSelector->GetSelectedImageDensity();
MOZ_ASSERT(density >= 0.0);
resolution = {density, density};
}
resolution.ApplyTo(size.width, size.height);
return size;
}
nsresult HTMLImageElement::CopyInnerTo(HTMLImageElement* aDest) {

View File

@ -97,8 +97,11 @@ class HTMLImageElement final : public nsGenericHTMLElement,
void SetHeight(uint32_t aHeight, ErrorResult& aError) {
SetUnsignedIntAttr(nsGkAtoms::height, aHeight, 0, aError);
}
uint32_t NaturalWidth();
uint32_t NaturalHeight();
nsIntSize NaturalSize();
uint32_t NaturalHeight() { return NaturalSize().height; }
uint32_t NaturalWidth() { return NaturalSize().width; }
bool Complete();
uint32_t Hspace() {
return GetDimensionAttrAsUnsignedInt(nsGkAtoms::hspace, 0);

View File

@ -2183,7 +2183,8 @@ mozilla::ipc::IPCResult BrowserParent::RecvAsyncMessage(
mozilla::ipc::IPCResult BrowserParent::RecvSetCursor(
const nsCursor& aCursor, const bool& aHasCustomCursor,
const nsCString& aCursorData, const uint32_t& aWidth,
const uint32_t& aHeight, const float& aResolution, const uint32_t& aStride,
const uint32_t& aHeight, const float& aResolutionX,
const float& aResolutionY, const uint32_t& aStride,
const gfx::SurfaceFormat& aFormat, const uint32_t& aHotspotX,
const uint32_t& aHotspotY, const bool& aForce) {
nsCOMPtr<nsIWidget> widget = GetWidget();
@ -2212,8 +2213,11 @@ mozilla::ipc::IPCResult BrowserParent::RecvSetCursor(
cursorImage = image::ImageOps::CreateFromDrawable(drawable);
}
mCursor = nsIWidget::Cursor{aCursor, std::move(cursorImage), aHotspotX,
aHotspotY, aResolution};
mCursor = nsIWidget::Cursor{aCursor,
std::move(cursorImage),
aHotspotX,
aHotspotY,
{aResolutionX, aResolutionY}};
if (!mRemoteTargetSetsCursor) {
return IPC_OK();
}

View File

@ -386,9 +386,10 @@ class BrowserParent final : public PBrowserParent,
mozilla::ipc::IPCResult RecvSetCursor(
const nsCursor& aValue, const bool& aHasCustomCursor,
const nsCString& aCursorData, const uint32_t& aWidth,
const uint32_t& aHeight, const float& aResolution,
const uint32_t& aStride, const gfx::SurfaceFormat& aFormat,
const uint32_t& aHotspotX, const uint32_t& aHotspotY, const bool& aForce);
const uint32_t& aHeight, const float& aResolutionX,
const float& aResolutionY, const uint32_t& aStride,
const gfx::SurfaceFormat& aFormat, const uint32_t& aHotspotX,
const uint32_t& aHotspotY, const bool& aForce);
mozilla::ipc::IPCResult RecvSetLinkStatus(const nsString& aStatus);

View File

@ -389,8 +389,10 @@ parent:
* Width of the image.
* @param height
* Height of the image.
* @param resolution
* Resolution of the image in dppx units.
* @param resolutionX
* Resolution of the image X axis in dppx units.
* @param resolutionY
* Resolution of the image Y axis in dppx units.
* @param stride
* Stride used in the image data.
* @param format
@ -407,7 +409,8 @@ parent:
bool hasCustomCursor,
nsCString customCursorData,
uint32_t width, uint32_t height,
float resolution, uint32_t stride, SurfaceFormat format,
float resolutionX, float resolutionY,
uint32_t stride, SurfaceFormat format,
uint32_t hotspotX, uint32_t hotspotY, bool force);
/**

View File

@ -63,6 +63,15 @@ struct Resolution {
ApplyXTo(aWidth);
ApplyYTo(aHeight);
}
void ApplyInverseTo(int32_t& aWidth, int32_t& aHeight) {
if (mX != 1.0f) {
aWidth = std::round(float(aWidth) * mX);
}
if (mY != 1.0f) {
aHeight = std::round(float(aHeight) * mY);
}
}
};
} // namespace image

View File

@ -6294,6 +6294,8 @@ ImgDrawResult nsLayoutUtils::DrawSingleUnscaledImage(
CSSIntSize imageSize;
aImage->GetWidth(&imageSize.width);
aImage->GetHeight(&imageSize.height);
aImage->GetResolution().ApplyTo(imageSize.width, imageSize.height);
if (imageSize.width < 1 || imageSize.height < 1) {
NS_WARNING("Image width or height is non-positive");
return ImgDrawResult::TEMPORARY_ERROR;
@ -6321,13 +6323,15 @@ ImgDrawResult nsLayoutUtils::DrawSingleUnscaledImage(
/* static */
ImgDrawResult nsLayoutUtils::DrawSingleImage(
gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage,
float aResolution, SamplingFilter aSamplingFilter, const nsRect& aDest,
const nsRect& aDirty, const Maybe<SVGImageContext>& aSVGContext,
uint32_t aImageFlags, const nsPoint* aAnchorPoint,
const nsRect* aSourceArea) {
SamplingFilter aSamplingFilter, const nsRect& aDest, const nsRect& aDirty,
const Maybe<SVGImageContext>& aSVGContext, uint32_t aImageFlags,
const nsPoint* aAnchorPoint, const nsRect* aSourceArea) {
nscoord appUnitsPerCSSPixel = AppUnitsPerCSSPixel();
CSSIntSize pixelImageSize(
ComputeSizeForDrawingWithFallback(aImage, aResolution, aDest.Size()));
// NOTE(emilio): We can hardcode resolution to 1 here, since we're interested
// in the actual image pixels, for snapping purposes, not on the adjusted
// size.
CSSIntSize pixelImageSize(ComputeSizeForDrawingWithFallback(
aImage, ImageResolution(), aDest.Size()));
if (pixelImageSize.width < 1 || pixelImageSize.height < 1) {
NS_ASSERTION(pixelImageSize.width >= 0 && pixelImageSize.height >= 0,
"Image width or height is negative");
@ -6368,7 +6372,7 @@ ImgDrawResult nsLayoutUtils::DrawSingleImage(
/* static */
void nsLayoutUtils::ComputeSizeForDrawing(
imgIContainer* aImage, float aResolution,
imgIContainer* aImage, const ImageResolution& aResolution,
/* outparam */ CSSIntSize& aImageSize,
/* outparam */ AspectRatio& aIntrinsicRatio,
/* outparam */ bool& aGotWidth,
@ -6378,13 +6382,11 @@ void nsLayoutUtils::ComputeSizeForDrawing(
Maybe<AspectRatio> 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) {
aResolution.ApplyXTo(aImageSize.width);
}
if (aGotHeight) {
aResolution.ApplyYTo(aImageSize.height);
}
if (!(aGotWidth && aGotHeight) && intrinsicRatio.isNothing()) {
@ -6397,7 +6399,8 @@ void nsLayoutUtils::ComputeSizeForDrawing(
/* static */
CSSIntSize nsLayoutUtils::ComputeSizeForDrawingWithFallback(
imgIContainer* aImage, float aResolution, const nsSize& aFallbackSize) {
imgIContainer* aImage, const ImageResolution& aResolution,
const nsSize& aFallbackSize) {
CSSIntSize imageSize;
AspectRatio imageRatio;
bool gotHeight, gotWidth;

View File

@ -103,6 +103,9 @@ namespace gfx {
struct RectCornerRadii;
enum class ShapedTextFlags : uint16_t;
} // namespace gfx
namespace image {
struct Resolution;
}
namespace layers {
struct FrameMetrics;
struct ScrollMetadata;
@ -1901,11 +1904,6 @@ 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.
@ -1927,9 +1925,9 @@ class nsLayoutUtils {
*/
static ImgDrawResult DrawSingleImage(
gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage,
float aResolution, SamplingFilter aSamplingFilter, const nsRect& aDest,
const nsRect& aDirty, const mozilla::Maybe<SVGImageContext>& aSVGContext,
uint32_t aImageFlags, const nsPoint* aAnchorPoint = nullptr,
SamplingFilter aSamplingFilter, const nsRect& aDest, const nsRect& aDirty,
const mozilla::Maybe<SVGImageContext>& aSVGContext, uint32_t aImageFlags,
const nsPoint* aAnchorPoint = nullptr,
const nsRect* aSourceArea = nullptr);
/**
@ -1947,8 +1945,16 @@ class nsLayoutUtils {
* NOTE: This method is similar to ComputeSizeWithIntrinsicDimensions. The
* difference is that this one is simpler and is suited to places where we
* have less information about the frame tree.
*
* @param aResolution The resolution specified by the author for the image, or
* its intrinsic resolution.
*
* This will affect the intrinsic size 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).
*/
static void ComputeSizeForDrawing(imgIContainer* aImage, float aResolution,
static void ComputeSizeForDrawing(imgIContainer* aImage,
const mozilla::image::Resolution&,
CSSIntSize& aImageSize,
AspectRatio& aIntrinsicRatio,
bool& aGotWidth, bool& aGotHeight);
@ -1962,7 +1968,8 @@ class nsLayoutUtils {
* dimensions, the corresponding dimension of aFallbackSize is used instead.
*/
static CSSIntSize ComputeSizeForDrawingWithFallback(
imgIContainer* aImage, float aResolution, const nsSize& aFallbackSize);
imgIContainer* aImage, const mozilla::image::Resolution&,
const nsSize& aFallbackSize);
/**
* Given the image container, frame, and dest rect, determine the best fitting

View File

@ -356,6 +356,38 @@ static bool SizeIsAvailable(imgIRequest* aRequest) {
return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE);
}
const StyleImage* nsImageFrame::GetImageFromStyle() const {
if (mKind == Kind::ImageElement) {
return nullptr;
}
uint32_t contentIndex = 0;
const nsStyleContent* styleContent = StyleContent();
if (mKind == Kind::ContentPropertyAtIndex) {
MOZ_RELEASE_ASSERT(
mContent->IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage));
contentIndex = static_cast<GeneratedImageContent*>(mContent.get())->Index();
// TODO(emilio): Consider inheriting the `content` property instead of doing
// this parent traversal?
nsIFrame* parent = GetParent();
MOZ_DIAGNOSTIC_ASSERT(
parent->GetContent()->IsGeneratedContentContainerForMarker() ||
parent->GetContent()->IsGeneratedContentContainerForAfter() ||
parent->GetContent()->IsGeneratedContentContainerForBefore());
nsIFrame* nonAnonymousParent = parent;
while (nonAnonymousParent->Style()->IsAnonBox()) {
nonAnonymousParent = nonAnonymousParent->GetParent();
}
MOZ_DIAGNOSTIC_ASSERT(parent->GetContent() ==
nonAnonymousParent->GetContent());
styleContent = nonAnonymousParent->StyleContent();
}
MOZ_RELEASE_ASSERT(contentIndex < styleContent->ContentCount());
auto& contentItem = styleContent->ContentAt(contentIndex);
MOZ_RELEASE_ASSERT(contentItem.IsImage());
return &contentItem.AsImage();
}
void nsImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) {
MOZ_ASSERT_IF(aPrevInFlow,
@ -378,33 +410,11 @@ void nsImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
// that it can register images.
imageLoader->FrameCreated(this);
} else {
uint32_t contentIndex = 0;
const nsStyleContent* styleContent = StyleContent();
if (mKind == Kind::ContentPropertyAtIndex) {
MOZ_RELEASE_ASSERT(
aParent->GetContent()->IsGeneratedContentContainerForMarker() ||
aParent->GetContent()->IsGeneratedContentContainerForAfter() ||
aParent->GetContent()->IsGeneratedContentContainerForBefore());
MOZ_RELEASE_ASSERT(
aContent->IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage));
nsIFrame* nonAnonymousParent = aParent;
while (nonAnonymousParent->Style()->IsAnonBox()) {
nonAnonymousParent = nonAnonymousParent->GetParent();
}
MOZ_RELEASE_ASSERT(aParent->GetContent() ==
nonAnonymousParent->GetContent());
styleContent = nonAnonymousParent->StyleContent();
contentIndex = static_cast<GeneratedImageContent*>(aContent)->Index();
}
MOZ_RELEASE_ASSERT(contentIndex < styleContent->ContentCount());
MOZ_RELEASE_ASSERT(styleContent->ContentAt(contentIndex).IsImage());
const StyleImage& image = styleContent->ContentAt(contentIndex).AsImage();
MOZ_ASSERT(image.IsImageRequestType(),
const StyleImage* image = GetImageFromStyle();
MOZ_ASSERT(image->IsImageRequestType(),
"Content image should only parse url() type");
auto [finalImage, resolution] = image.FinalImageAndResolution();
Document* doc = PresContext()->Document();
if (imgRequestProxy* proxy = finalImage->GetImageRequest()) {
mContentURLRequestResolution = resolution;
if (imgRequestProxy* proxy = image->GetImageRequest()) {
proxy->Clone(mListener, doc, getter_AddRefs(mContentURLRequest));
SetupForContentURLRequest();
}
@ -457,34 +467,27 @@ void nsImageFrame::SetupForContentURLRequest() {
}
static void ScaleIntrinsicSizeForDensity(IntrinsicSize& aSize,
double aDensity) {
if (aDensity == 1.0) {
return;
}
const ImageResolution& aResolution) {
if (aSize.width) {
aSize.width = Some(NSToCoordRound(double(*aSize.width) / aDensity));
aResolution.ApplyXTo(aSize.width.ref());
}
if (aSize.height) {
aSize.height = Some(NSToCoordRound(double(*aSize.height) / aDensity));
aResolution.ApplyYTo(aSize.height.ref());
}
}
static void ScaleIntrinsicSizeForDensity(nsIContent& aContent,
static void ScaleIntrinsicSizeForDensity(imgIContainer* aImage,
nsIContent& aContent,
IntrinsicSize& aSize) {
auto* image = HTMLImageElement::FromNode(aContent);
if (!image) {
return;
if (auto* image = HTMLImageElement::FromNode(aContent)) {
if (auto* selector = image->GetResponsiveImageSelector()) {
float density = selector->GetSelectedImageDensity();
MOZ_ASSERT(density >= 0.0);
ScaleIntrinsicSizeForDensity(aSize, ImageResolution{density, density});
return;
}
}
ResponsiveImageSelector* selector = image->GetResponsiveImageSelector();
if (!selector) {
return;
}
double density = selector->GetSelectedImageDensity();
MOZ_ASSERT(density >= 0.0);
ScaleIntrinsicSizeForDensity(aSize, density);
ScaleIntrinsicSizeForDensity(aSize, aImage->GetResolution());
}
static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage,
@ -502,10 +505,10 @@ static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage,
intrinsicSize.width = size.width == -1 ? Nothing() : Some(size.width);
intrinsicSize.height = size.height == -1 ? Nothing() : Some(size.height);
if (aKind == nsImageFrame::Kind::ImageElement) {
ScaleIntrinsicSizeForDensity(*aFrame.GetContent(), intrinsicSize);
ScaleIntrinsicSizeForDensity(aImage, *aFrame.GetContent(), intrinsicSize);
} else {
ScaleIntrinsicSizeForDensity(intrinsicSize,
aFrame.GetContentURLRequestResolution());
aFrame.GetImageFromStyle()->GetResolution());
}
return intrinsicSize;
}
@ -1486,7 +1489,7 @@ ImgDrawResult nsImageFrame::DisplayAltFeedback(gfxContext& aRenderingContext,
nsRect dest(flushRight ? inner.XMost() - size : inner.x, inner.y, size,
size);
result = nsLayoutUtils::DrawSingleImage(
aRenderingContext, PresContext(), imgCon, /* aResolution = */ 1.0f,
aRenderingContext, PresContext(), imgCon,
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
/* no SVGImageContext */ Nothing(), aFlags);
}
@ -2090,10 +2093,8 @@ ImgDrawResult nsImageFrame::PaintImage(gfxContext& aRenderingContext,
Maybe<SVGImageContext> 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, /* aResolution = */ 1.0f,
aRenderingContext, PresContext(), aImage,
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
svgContext, flags, &anchorPoint);

View File

@ -103,11 +103,7 @@ 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;
}
const mozilla::StyleImage* GetImageFromStyle() const;
#ifdef ACCESSIBILITY
mozilla::a11y::AccType AccessibleType() override;
@ -359,7 +355,6 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
// An image request created for content: url(..).
RefPtr<imgRequestProxy> mContentURLRequest;
float mContentURLRequestResolution = 1.0f;
nsCOMPtr<imgIContainer> mImage;
nsCOMPtr<imgIContainer> mPrevImage;

View File

@ -50,6 +50,9 @@ nsSize CSSSizeOrRatio::ComputeConcreteSize() const {
nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage,
uint32_t aFlags)
: mForFrame(aForFrame),
mImage(&aImage->FinalImage()),
mImageResolution(aImage->GetResolution()),
mType(mImage->tag),
mImageContainer(nullptr),
mGradientData(nullptr),
mPaintServerFrame(nullptr),
@ -57,12 +60,7 @@ nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage,
mSize(0, 0),
mFlags(aFlags),
mExtendMode(ExtendMode::CLAMP),
mMaskOp(StyleMaskMode::MatchSource) {
auto pair = aImage->FinalImageAndResolution();
mImage = pair.first;
mType = mImage->tag;
mImageResolution = pair.second;
}
mMaskOp(StyleMaskMode::MatchSource) {}
bool nsImageRenderer::PrepareImage() {
if (mImage->IsNone()) {
@ -940,9 +938,8 @@ ImgDrawResult nsImageRenderer::DrawBorderImageComponent(
if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) {
ImgDrawResult result = nsLayoutUtils::DrawSingleImage(
aRenderingContext, aPresContext, subImage, mImageResolution,
samplingFilter, aFill, aDirtyRect,
/* no SVGImageContext */ Nothing(), drawFlags);
aRenderingContext, aPresContext, subImage, samplingFilter, aFill,
aDirtyRect, /* no SVGImageContext */ Nothing(), drawFlags);
if (!mImage->IsComplete()) {
result &= ImgDrawResult::SUCCESS_NOT_COMPLETE;
@ -1010,9 +1007,8 @@ 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, mImageResolution,
SamplingFilter::POINT, dest, dest, Nothing(), drawFlags, nullptr,
nullptr);
aRenderingContext, aPresContext, mImageContainer, SamplingFilter::POINT,
dest, dest, Nothing(), drawFlags);
}
if (mImage->IsGradient()) {

View File

@ -299,7 +299,7 @@ class nsImageRenderer {
nsIFrame* mForFrame;
const mozilla::StyleImage* mImage;
float mImageResolution;
ImageResolution mImageResolution;
mozilla::StyleImage::Tag mType;
nsCOMPtr<imgIContainer> mImageContainer;
const mozilla::StyleGradient* mGradientData;

View File

@ -30,6 +30,7 @@
# include "nsCSSPropertyID.h"
# include "nsCompatibility.h"
# include "nsIURI.h"
# include "mozilla/image/Resolution.h"
# include <atomic>
struct RawServoAnimationValueTable;

View File

@ -942,20 +942,16 @@ inline bool RestyleHint::DefinitelyRecascadesAllSubtree() const {
}
template <>
inline std::pair<const StyleImage*, float> StyleImage::FinalImageAndResolution()
const {
if (!IsImageSet()) {
return {this, 1.0f};
}
auto& set = AsImageSet();
auto& selectedItem = set->items.AsSpan()[set->selected_index];
return {selectedItem.image.FinalImageAndResolution().first,
selectedItem.resolution._0};
}
ImageResolution StyleImage::GetResolution() const;
template <>
inline const StyleImage& StyleImage::FinalImage() const {
return *FinalImageAndResolution().first;
if (!IsImageSet()) {
return *this;
}
auto& set = AsImageSet();
auto& selectedItem = set->items.AsSpan()[set->selected_index];
return selectedItem.image.FinalImage();
}
template <>

View File

@ -1629,10 +1629,27 @@ void StyleImage::ResolveImage(Document& aDoc, const StyleImage* aOld) {
const_cast<StyleComputedImageUrl*>(url)->ResolveImage(aDoc, old);
}
template <>
ImageResolution StyleImage::GetResolution() const {
if (IsImageSet()) {
auto& set = AsImageSet();
float r = set->items.AsSpan()[set->selected_index].resolution._0;
if (MOZ_LIKELY(r != 0.0f)) {
return ImageResolution(r, r);
}
} else if (imgRequestProxy* request = GetImageRequest()) {
RefPtr<imgIContainer> image;
request->GetImage(getter_AddRefs(image));
if (image) {
return image->GetResolution();
}
}
return {};
}
template <>
Maybe<CSSIntSize> StyleImage::GetIntrinsicSize() const {
auto [finalImage, resolution] = FinalImageAndResolution();
imgRequestProxy* request = finalImage->GetImageRequest();
imgRequestProxy* request = GetImageRequest();
if (!request) {
return Nothing();
}
@ -1647,10 +1664,7 @@ Maybe<CSSIntSize> StyleImage::GetIntrinsicSize() const {
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);
}
GetResolution().ApplyTo(w, h);
return Some(CSSIntSize{w, h});
}
@ -1909,9 +1923,9 @@ static bool SizeDependsOnPositioningAreaSize(const StyleBackgroundSize& aSize,
bool 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);
nsLayoutUtils::ComputeSizeForDrawing(imgContainer, ImageResolution(),
imageSize, imageRatio, hasWidth,
hasHeight);
// If the image has a fixed width and height, rendering never depends on
// the frame size.

View File

@ -241,17 +241,21 @@ bool SVGImageFrame::GetIntrinsicImageDimensions(
return false;
}
ImageResolution resolution = mImageContainer->GetResolution();
int32_t width, height;
if (NS_FAILED(mImageContainer->GetWidth(&width))) {
aSize.width = -1;
} else {
aSize.width = width;
resolution.ApplyXTo(aSize.width);
}
if (NS_FAILED(mImageContainer->GetHeight(&height))) {
aSize.height = -1;
} else {
aSize.height = height;
resolution.ApplyYTo(aSize.height);
}
Maybe<AspectRatio> asp = mImageContainer->GetIntrinsicRatio();
@ -272,6 +276,7 @@ bool SVGImageFrame::TransformContextForPainting(gfxContext* aGfxContext,
nativeWidth == 0 || nativeHeight == 0) {
return false;
}
mImageContainer->GetResolution().ApplyTo(nativeWidth, nativeHeight);
imageTransform = GetRasterImageTransform(nativeWidth, nativeHeight) *
ToMatrix(aTransform);
@ -405,7 +410,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, /* aResolution = */ 1.0f,
aContext, PresContext(), mImageContainer,
nsLayoutUtils::GetSamplingFilterForFrame(this), destRect,
aDirtyRect ? dirtyRect : destRect, context, flags);
} else { // mImageContainer->GetType() == TYPE_RASTER
@ -674,6 +679,7 @@ nsIFrame* SVGImageFrame::GetFrameForPoint(const gfxPoint& aPoint) {
nativeWidth == 0 || nativeHeight == 0) {
return nullptr;
}
mImageContainer->GetResolution().ApplyTo(nativeWidth, nativeHeight);
Matrix viewBoxTM = SVGContentUtils::GetViewBoxTransform(
rect.width, rect.height, 0, 0, nativeWidth, nativeHeight,
element->mPreserveAspectRatio);

View File

@ -240,7 +240,6 @@ void nsImageBoxFrame::UpdateImage() {
mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
mUseSrcAttr = !src.IsEmpty();
if (mUseSrcAttr) {
mImageResolution = 1.0f;
nsContentPolicyType contentPolicyType;
nsCOMPtr<nsIPrincipal> triggeringPrincipal;
uint64_t requestContextID = 0;
@ -270,9 +269,7 @@ void nsImageBoxFrame::UpdateImage() {
}
}
} else if (auto* styleImage = GetImageFromStyle()) {
auto [finalImage, resolution] = styleImage->FinalImageAndResolution();
mImageResolution = resolution;
if (auto* styleRequest = finalImage->GetImageRequest()) {
if (auto* styleRequest = styleImage->GetImageRequest()) {
styleRequest->SyncClone(mListener, mContent->GetComposedDoc(),
getter_AddRefs(mImageRequest));
}
@ -394,7 +391,7 @@ ImgDrawResult nsImageBoxFrame::PaintImage(gfxContext& aRenderingContext,
Maybe<SVGImageContext> svgContext;
SVGImageContext::MaybeStoreContextPaint(svgContext, this, imgCon);
return nsLayoutUtils::DrawSingleImage(
aRenderingContext, PresContext(), imgCon, mImageResolution,
aRenderingContext, PresContext(), imgCon,
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, dirty, svgContext,
aFlags, anchorPoint.ptrOr(nullptr), hasSubRect ? &mSubRect : nullptr);
}
@ -610,11 +607,11 @@ bool nsImageBoxFrame::CanOptimizeToImageLayer() {
}
const mozilla::StyleImage* nsImageBoxFrame::GetImageFromStyle(
const ComputedStyle& aStyle) {
const ComputedStyle& aStyle) const {
const nsStyleDisplay* disp = aStyle.StyleDisplay();
if (disp->HasAppearance()) {
nsPresContext* pc = PresContext();
if (pc->Theme()->ThemeSupportsWidget(pc, this,
if (pc->Theme()->ThemeSupportsWidget(pc, const_cast<nsImageBoxFrame*>(this),
disp->EffectiveAppearance())) {
return nullptr;
}
@ -626,6 +623,21 @@ const mozilla::StyleImage* nsImageBoxFrame::GetImageFromStyle(
return &image;
}
ImageResolution nsImageBoxFrame::GetImageResolution() const {
if (auto* image = GetImageFromStyle()) {
return image->GetResolution();
}
if (!mImageRequest) {
return {};
}
nsCOMPtr<imgIContainer> image;
mImageRequest->GetImage(getter_AddRefs(image));
if (!image) {
return {};
}
return image->GetResolution();
}
/* virtual */
void nsImageBoxFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
nsLeafBoxFrame::DidSetComputedStyle(aOldStyle);
@ -803,13 +815,10 @@ void nsImageBoxFrame::OnSizeAvailable(imgIRequest* aRequest,
aImage->GetWidth(&w);
aImage->GetHeight(&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));
GetImageResolution().ApplyTo(mIntrinsicSize.width, mIntrinsicSize.height);
if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
NS_FRAME_IS_DIRTY);

View File

@ -78,11 +78,13 @@ class nsImageBoxFrame final : public nsLeafBoxFrame {
*
* TODO(emilio): Maybe support list-style-image: linear-gradient() etc?
*/
const mozilla::StyleImage* GetImageFromStyle(const ComputedStyle&);
const mozilla::StyleImage* GetImageFromStyle() {
const mozilla::StyleImage* GetImageFromStyle(const ComputedStyle&) const;
const mozilla::StyleImage* GetImageFromStyle() const {
return GetImageFromStyle(*Style());
}
mozilla::ImageResolution GetImageResolution() const;
/**
* Update mUseSrcAttr from appropriate content attributes or from
* style, throw away the current image, and load the appropriate
@ -141,7 +143,6 @@ class nsImageBoxFrame final : public nsLeafBoxFrame {
nsSize mImageSize;
RefPtr<imgRequestProxy> mImageRequest;
float mImageResolution = 1.0f;
nsCOMPtr<imgINotificationObserver> mListener;
int32_t mLoadFlags;

View File

@ -791,12 +791,11 @@ 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.
// Returns the intrinsic resolution of the image.
//
// The resolution is in dppx, and should be used to impact the intrinsic size
// of the image.
std::pair<const StyleGenericImage*, float> FinalImageAndResolution() const;
// The resolution is in dppx, and should be used to impact the intrinsic
// size of the image.
ImageResolution GetResolution() const;
// Returns the intrinsic size of the image, if there's one, accounting for
// resolution as needed.

View File

@ -0,0 +1 @@
prefs: [image.exif-density-correction.enabled:true]

View File

@ -1,4 +0,0 @@
[density-corrected-image-in-canvas.html]
[Density corrected size: canvas]
expected: FAIL

View File

@ -1,2 +0,0 @@
[density-corrected-image-svg-aspect-ratio.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[density-corrected-image-svg.html]
expected: FAIL

View File

@ -1,4 +0,0 @@
[density-corrected-natural-size.html]
[density-corrected-natural-size]
expected: FAIL

View File

@ -1,2 +0,0 @@
[density-corrected-size-bg.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[density-corrected-size-img.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[density-corrected-size-pseudo-elements.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[density-corrected-various-elements.html]
expected: FAIL

View File

@ -925,7 +925,7 @@ void PuppetWidget::SetCursor(const Cursor& aCursor) {
IntSize customCursorSize;
int32_t stride = 0;
auto format = SurfaceFormat::B8G8R8A8;
float resolution = aCursor.mResolution;
ImageResolution resolution = aCursor.mResolution;
if (aCursor.IsCustom()) {
int32_t width = 0, height = 0;
aCursor.mContainer->GetWidth(&width);
@ -936,9 +936,8 @@ void PuppetWidget::SetCursor(const Cursor& aCursor) {
if (width && height &&
aCursor.mContainer->GetType() == imgIContainer::TYPE_VECTOR) {
// For vector images, scale to device pixels.
resolution *= GetDefaultScale().scale;
width = std::ceil(width * resolution);
height = std::ceil(height * resolution);
resolution.ScaleBy(GetDefaultScale().scale);
resolution.ApplyInverseTo(width, height);
surface = aCursor.mContainer->GetFrameAtSize(
{width, height},
imgIContainer::FRAME_CURRENT, flags);
@ -964,8 +963,9 @@ void PuppetWidget::SetCursor(const Cursor& aCursor) {
length);
if (!mBrowserChild->SendSetCursor(
aCursor.mDefaultCursor, hasCustomCursor, cursorData,
customCursorSize.width, customCursorSize.height, resolution,
stride, format, aCursor.mHotspotX, aCursor.mHotspotY, force)) {
customCursorSize.width, customCursorSize.height,
resolution.mX, resolution.mY, stride, format,
aCursor.mHotspotX, aCursor.mHotspotY, force)) {
return;
}
mCursor = aCursor;

View File

@ -2351,8 +2351,9 @@ static GdkCursor* GetCursorForImage(const nsIWidget::Cursor& aCursor,
// factor and then tell gtk to scale it down. We ensure to scale at least to
// the GDK scale factor, so that cursors aren't downsized in HiDPI on wayland,
// see bug 1707533.
int32_t gtkScale =
std::max(aWidgetScaleFactor, int32_t(std::ceil(aCursor.mResolution)));
int32_t gtkScale = std::max(
aWidgetScaleFactor, int32_t(std::ceil(std::max(aCursor.mResolution.mX,
aCursor.mResolution.mY))));
// Reject cursors greater than 128 pixels in some direction, to prevent
// spoofing.

View File

@ -529,10 +529,7 @@ nsIntSize nsIWidget::CustomCursorSize(const Cursor& aCursor) {
int32_t height = 0;
aCursor.mContainer->GetWidth(&width);
aCursor.mContainer->GetHeight(&height);
if (aCursor.mResolution != 0.0f && aCursor.mResolution != 1.0f) {
width = std::round(float(width) / aCursor.mResolution);
height = std::round(float(height) / aCursor.mResolution);
}
aCursor.mResolution.ApplyTo(width, height);
return {width, height};
}

View File

@ -23,6 +23,7 @@
#include "mozilla/layers/LayersTypes.h"
#include "mozilla/layers/ScrollableLayerGuid.h"
#include "mozilla/layers/ZoomConstraints.h"
#include "mozilla/image/Resolution.h"
#include "mozilla/widget/IMEData.h"
#include "nsCOMPtr.h"
#include "nsColor.h"
@ -992,7 +993,7 @@ class nsIWidget : public nsISupports {
nsCOMPtr<imgIContainer> mContainer;
uint32_t mHotspotX = 0;
uint32_t mHotspotY = 0;
float mResolution = 1.0f;
mozilla::ImageResolution mResolution;
bool IsCustom() const { return !!mContainer; }
@ -1003,7 +1004,7 @@ class nsIWidget : public nsISupports {
mResolution == aOther.mResolution;
}
bool operator!=(const Cursor& aOther) { return !(*this == aOther); }
bool operator!=(const Cursor& aOther) const { return !(*this == aOther); }
};
/**