From e025bcc4f95e37cc8019bcbcb8557d2893a79953 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Fri, 26 Jan 2024 15:56:25 +0000 Subject: [PATCH] LibWeb: Make use of transform-box when calculating transforms We don't currently calculate the fill- or stroke-boxes of SVG elements, so for now we use the content- and border-boxes respectively, as those are the closest equivalents. The test will need updating when we do support them. Also, the test is a screenshot because of rendering differences when applying transforms: a 20px box does not get painted the same as a 10px box scaled up 2x. Otherwise that would be the more ideal form of test. --- Tests/LibWeb/Ref/css-transform-box.html | 36 ++++++++++ .../Ref/reference/css-transform-box-ref.html | 15 ++++ .../images/css-transform-box-ref.png | Bin 0 -> 8362 bytes .../Libraries/LibWeb/Layout/LayoutState.cpp | 68 +++++++++++++++++- 4 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 Tests/LibWeb/Ref/css-transform-box.html create mode 100644 Tests/LibWeb/Ref/reference/css-transform-box-ref.html create mode 100644 Tests/LibWeb/Ref/reference/images/css-transform-box-ref.png diff --git a/Tests/LibWeb/Ref/css-transform-box.html b/Tests/LibWeb/Ref/css-transform-box.html new file mode 100644 index 00000000000..9ca3dbcf383 --- /dev/null +++ b/Tests/LibWeb/Ref/css-transform-box.html @@ -0,0 +1,36 @@ + + + +
Hi
+
Hi
+ + + + + + + + + + + diff --git a/Tests/LibWeb/Ref/reference/css-transform-box-ref.html b/Tests/LibWeb/Ref/reference/css-transform-box-ref.html new file mode 100644 index 00000000000..57ae32963bc --- /dev/null +++ b/Tests/LibWeb/Ref/reference/css-transform-box-ref.html @@ -0,0 +1,15 @@ + + + diff --git a/Tests/LibWeb/Ref/reference/images/css-transform-box-ref.png b/Tests/LibWeb/Ref/reference/images/css-transform-box-ref.png new file mode 100644 index 0000000000000000000000000000000000000000..58fe5260b51e5c5ecec0ee6031ee94955134dae9 GIT binary patch literal 8362 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 literal 0 HcmV?d00001 diff --git a/Userland/Libraries/LibWeb/Layout/LayoutState.cpp b/Userland/Libraries/LibWeb/Layout/LayoutState.cpp index c2b19b2c34f..886524f7944 100644 --- a/Userland/Libraries/LibWeb/Layout/LayoutState.cpp +++ b/Userland/Libraries/LibWeb/Layout/LayoutState.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2022-2023, Andreas Kling + * Copyright (c) 2024, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -12,6 +13,7 @@ #include #include #include +#include #include namespace Web::Layout { @@ -369,8 +371,70 @@ void LayoutState::resolve_layout_dependent_properties() } auto const& transform_origin = paintable_box.computed_values().transform_origin(); - // FIXME: respect transform-box property - auto const& reference_box = paintable_box.absolute_border_box_rect(); + // https://www.w3.org/TR/css-transforms-1/#transform-box + auto transform_box = paintable_box.computed_values().transform_box(); + // For SVG elements without associated CSS layout box, the used value for content-box is fill-box and for + // border-box is stroke-box. + // FIXME: This currently detects any SVG element except the one. Is that correct? + // And is it correct to use `else` below? + if (is(paintable_box)) { + switch (transform_box) { + case CSS::TransformBox::ContentBox: + transform_box = CSS::TransformBox::FillBox; + break; + case CSS::TransformBox::BorderBox: + transform_box = CSS::TransformBox::StrokeBox; + break; + default: + break; + } + } + // For elements with associated CSS layout box, the used value for fill-box is content-box and for + // stroke-box and view-box is border-box. + else { + switch (transform_box) { + case CSS::TransformBox::FillBox: + transform_box = CSS::TransformBox::ContentBox; + break; + case CSS::TransformBox::StrokeBox: + case CSS::TransformBox::ViewBox: + transform_box = CSS::TransformBox::BorderBox; + break; + default: + break; + } + } + + CSSPixelRect reference_box = [&]() { + switch (transform_box) { + case CSS::TransformBox::ContentBox: + // Uses the content box as reference box. + // FIXME: The reference box of a table is the border box of its table wrapper box, not its table box. + return paintable_box.absolute_rect(); + case CSS::TransformBox::BorderBox: + // Uses the border box as reference box. + // FIXME: The reference box of a table is the border box of its table wrapper box, not its table box. + return paintable_box.absolute_border_box_rect(); + case CSS::TransformBox::FillBox: + // Uses the object bounding box as reference box. + // FIXME: For now we're using the content rect as an approximation. + return paintable_box.absolute_rect(); + case CSS::TransformBox::StrokeBox: + // Uses the stroke bounding box as reference box. + // FIXME: For now we're using the border rect as an approximation. + return paintable_box.absolute_border_box_rect(); + case CSS::TransformBox::ViewBox: + // Uses the nearest SVG viewport as reference box. + // FIXME: If a viewBox attribute is specified for the SVG viewport creating element: + // - The reference box is positioned at the origin of the coordinate system established by the viewBox attribute. + // - The dimension of the reference box is set to the width and height values of the viewBox attribute. + auto* svg_paintable = paintable_box.first_ancestor_of_type(); + if (!svg_paintable) + return paintable_box.absolute_border_box_rect(); + return svg_paintable->absolute_rect(); + } + VERIFY_NOT_REACHED(); + }(); auto x = reference_box.left() + transform_origin.x.to_px(node, reference_box.width()); auto y = reference_box.top() + transform_origin.y.to_px(node, reference_box.height()); paintable_box.set_transform_origin({ x, y });