mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibWeb: Unify objectBoundingBox and userSpaceOnUse coord transformations
There's a fairly complicated interaction between an SVG gradient's paint transformation and the gradient coordinate transformation required to correctly draw gradient fills. This was especially noticeable when scaling down an SVG, resulting in broken gradient coordinates and graphical glitches. This changes the objectBoundingBox units to immediately map to the bounding box's coordinate system, so we can unify the gradient paint transformation logic and make it a lot simpler. We only need to undo the bounding box offset and apply the paint transformation to fix a lot of gradient fill bugs.
This commit is contained in:
parent
beb1d60714
commit
4dbae64dce
Notes:
github-actions[bot]
2025-10-27 23:43:24 +00:00
Author: https://github.com/gmta
Commit: 4dbae64dce
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6592
14 changed files with 200 additions and 21 deletions
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
|
||||
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
|
@ -98,16 +99,9 @@ Optional<Gfx::AffineTransform> SVGGradientElement::gradient_transform_impl(HashT
|
|||
// The gradient transform, appropriately scaled and combined with the paint transform.
|
||||
Gfx::AffineTransform SVGGradientElement::gradient_paint_transform(SVGPaintContext const& paint_context) const
|
||||
{
|
||||
Gfx::AffineTransform gradient_paint_transform = paint_context.paint_transform;
|
||||
auto const& bounding_box = paint_context.path_bounding_box;
|
||||
|
||||
if (gradient_units() == SVGUnits::ObjectBoundingBox) {
|
||||
// Scale points from 0..1 to bounding box coordinates:
|
||||
gradient_paint_transform.scale(bounding_box.width(), bounding_box.height());
|
||||
} else {
|
||||
// Translate points from viewport to bounding box coordinates:
|
||||
gradient_paint_transform.translate(paint_context.viewport.location() - bounding_box.location());
|
||||
}
|
||||
auto gradient_paint_transform = Gfx::AffineTransform {};
|
||||
gradient_paint_transform.set_translation(-paint_context.paint_transform.map(paint_context.path_bounding_box).location())
|
||||
.multiply(paint_context.paint_transform);
|
||||
|
||||
if (auto transform = gradient_transform(); transform.has_value())
|
||||
gradient_paint_transform.multiply(transform.value());
|
||||
|
|
|
|||
|
|
@ -114,7 +114,6 @@ NumberPercentage SVGLinearGradientElement::end_y_impl(HashTable<SVGGradientEleme
|
|||
|
||||
Optional<Painting::PaintStyle> SVGLinearGradientElement::to_gfx_paint_style(SVGPaintContext const& paint_context) const
|
||||
{
|
||||
// FIXME: Resolve percentages properly
|
||||
Gfx::FloatPoint start_point {};
|
||||
Gfx::FloatPoint end_point {};
|
||||
|
||||
|
|
@ -125,8 +124,15 @@ Optional<Painting::PaintStyle> SVGLinearGradientElement::to_gfx_paint_style(SVGP
|
|||
// box units) and then applying the transform specified by attribute ‘gradientTransform’. Percentages represent
|
||||
// values relative to the bounding box for the object.
|
||||
// Note: For gradientUnits="objectBoundingBox" both "100%" and "1" are treated the same.
|
||||
start_point = { start_x().value(), start_y().value() };
|
||||
end_point = { end_x().value(), end_y().value() };
|
||||
auto const& bounding_box = paint_context.path_bounding_box;
|
||||
start_point = {
|
||||
bounding_box.location().x() + start_x().value() * bounding_box.width(),
|
||||
bounding_box.location().y() + start_y().value() * bounding_box.height(),
|
||||
};
|
||||
end_point = {
|
||||
bounding_box.location().x() + end_x().value() * bounding_box.width(),
|
||||
bounding_box.location().y() + end_y().value() * bounding_box.height(),
|
||||
};
|
||||
} else {
|
||||
// GradientUnits::UserSpaceOnUse
|
||||
// If gradientUnits="userSpaceOnUse", ‘x1’, ‘y1’, ‘x2’, and ‘y2’ represent values in the coordinate system
|
||||
|
|
|
|||
|
|
@ -169,15 +169,23 @@ Optional<Painting::PaintStyle> SVGRadialGradientElement::to_gfx_paint_style(SVGP
|
|||
Gfx::FloatPoint end_center;
|
||||
float end_radius = 0.0f;
|
||||
|
||||
// FIXME: Where in the spec does it say what axis the radius is relative to?
|
||||
if (units == GradientUnits::ObjectBoundingBox) {
|
||||
// If gradientUnits="objectBoundingBox", the user coordinate system for attributes ‘cx’, ‘cy’, ‘r’, ‘fx’, ‘fy’, and ‘fr’
|
||||
// is established using the bounding box of the element to which the gradient is applied (see Object bounding box units)
|
||||
// and then applying the transform specified by attribute ‘gradientTransform’. Percentages represent values relative
|
||||
// to the bounding box for the object.
|
||||
start_center = Gfx::FloatPoint { start_circle_x().value(), start_circle_y().value() };
|
||||
start_radius = start_circle_radius().value();
|
||||
end_center = Gfx::FloatPoint { end_circle_x().value(), end_circle_y().value() };
|
||||
end_radius = end_circle_radius().value();
|
||||
auto const& bounding_box = paint_context.path_bounding_box;
|
||||
start_center = {
|
||||
bounding_box.location().x() + start_circle_x().value() * bounding_box.width(),
|
||||
bounding_box.location().y() + start_circle_y().value() * bounding_box.height(),
|
||||
};
|
||||
start_radius = start_circle_radius().value() * bounding_box.width();
|
||||
end_center = {
|
||||
bounding_box.location().x() + end_circle_x().value() * bounding_box.width(),
|
||||
bounding_box.location().y() + end_circle_y().value() * bounding_box.height(),
|
||||
};
|
||||
end_radius = end_circle_radius().value() * bounding_box.width();
|
||||
} else {
|
||||
// GradientUnits::UserSpaceOnUse
|
||||
// If gradientUnits="userSpaceOnUse", ‘cx’, ‘cy’, ‘r’, ‘fx’, ‘fy’, and ‘fr’ represent values in the coordinate system
|
||||
|
|
@ -191,7 +199,6 @@ Optional<Painting::PaintStyle> SVGRadialGradientElement::to_gfx_paint_style(SVGP
|
|||
start_circle_x().resolve_relative_to(paint_context.viewport.width()),
|
||||
start_circle_y().resolve_relative_to(paint_context.viewport.height()),
|
||||
};
|
||||
// FIXME: Where in the spec does it say what axis the radius is relative to?
|
||||
start_radius = start_circle_radius().resolve_relative_to(paint_context.viewport.width());
|
||||
end_center = Gfx::FloatPoint {
|
||||
end_circle_x().resolve_relative_to(paint_context.viewport.width()),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue