1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 14:37:46 +00:00

LibWeb: Support preserveAspectRatio=none for SVGs

This is very easy now all transforms are computed during layout.
This commit is contained in:
MacDue 2024-03-09 12:31:00 +01:00 committed by Andreas Kling
parent 190a8f948e
commit 05f42efc06
2 changed files with 18 additions and 10 deletions

View file

@ -98,7 +98,7 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline
TextNode <#text> TextNode <#text>
SVGSVGBox <svg> at (249,201) content-size 160x60 [SVG] children: inline SVGSVGBox <svg> at (249,201) content-size 160x60 [SVG] children: inline
TextNode <#text> TextNode <#text>
SVGGeometryBox <circle> at (299,201) content-size 60x60 children: not-inline SVGGeometryBox <circle> at (249,201) content-size 160x60 children: not-inline
TextNode <#text> TextNode <#text>
TextNode <#text> TextNode <#text>
@ -141,4 +141,4 @@ ViewportPaintable (Viewport<#document>) [0,0 800x600]
SVGPathPaintable (SVGGeometryBox<circle>) [114,136 125x125] SVGPathPaintable (SVGGeometryBox<circle>) [114,136 125x125]
TextPaintable (TextNode<#text>) TextPaintable (TextNode<#text>)
SVGSVGPaintable (SVGSVGBox<svg>) [248,200 162x62] SVGSVGPaintable (SVGSVGBox<svg>) [248,200 162x62]
SVGPathPaintable (SVGGeometryBox<circle>) [299,201 60x60] SVGPathPaintable (SVGGeometryBox<circle>) [249,201 160x60]

View file

@ -46,7 +46,8 @@ CSSPixels SVGFormattingContext::automatic_content_height() const
struct ViewBoxTransform { struct ViewBoxTransform {
CSSPixelPoint offset; CSSPixelPoint offset;
double scale_factor; double scale_factor_x;
double scale_factor_y;
}; };
// https://svgwg.org/svg2-draft/coords.html#PreserveAspectRatioAttribute // https://svgwg.org/svg2-draft/coords.html#PreserveAspectRatioAttribute
@ -55,20 +56,27 @@ static ViewBoxTransform scale_and_align_viewbox_content(SVG::PreserveAspectRatio
{ {
ViewBoxTransform viewbox_transform {}; ViewBoxTransform viewbox_transform {};
if (preserve_aspect_ratio.align == SVG::PreserveAspectRatio::Align::None) {
viewbox_transform.scale_factor_x = viewbox_scale.width();
viewbox_transform.scale_factor_y = viewbox_scale.height();
viewbox_transform.offset = {};
return viewbox_transform;
}
switch (preserve_aspect_ratio.meet_or_slice) { switch (preserve_aspect_ratio.meet_or_slice) {
case SVG::PreserveAspectRatio::MeetOrSlice::Meet: case SVG::PreserveAspectRatio::MeetOrSlice::Meet:
// meet (the default) - Scale the graphic such that: // meet (the default) - Scale the graphic such that:
// - aspect ratio is preserved // - aspect ratio is preserved
// - the entire viewBox is visible within the SVG viewport // - the entire viewBox is visible within the SVG viewport
// - the viewBox is scaled up as much as possible, while still meeting the other criteria // - the viewBox is scaled up as much as possible, while still meeting the other criteria
viewbox_transform.scale_factor = min(viewbox_scale.width(), viewbox_scale.height()); viewbox_transform.scale_factor_x = viewbox_transform.scale_factor_y = min(viewbox_scale.width(), viewbox_scale.height());
break; break;
case SVG::PreserveAspectRatio::MeetOrSlice::Slice: case SVG::PreserveAspectRatio::MeetOrSlice::Slice:
// slice - Scale the graphic such that: // slice - Scale the graphic such that:
// aspect ratio is preserved // aspect ratio is preserved
// the entire SVG viewport is covered by the viewBox // the entire SVG viewport is covered by the viewBox
// the viewBox is scaled down as much as possible, while still meeting the other criteria // the viewBox is scaled down as much as possible, while still meeting the other criteria
viewbox_transform.scale_factor = max(viewbox_scale.width(), viewbox_scale.height()); viewbox_transform.scale_factor_x = viewbox_transform.scale_factor_y = max(viewbox_scale.width(), viewbox_scale.height());
break; break;
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
@ -93,13 +101,13 @@ static ViewBoxTransform scale_and_align_viewbox_content(SVG::PreserveAspectRatio
case SVG::PreserveAspectRatio::Align::xMidYMid: case SVG::PreserveAspectRatio::Align::xMidYMid:
case SVG::PreserveAspectRatio::Align::xMidYMax: case SVG::PreserveAspectRatio::Align::xMidYMax:
// Align the midpoint X value of the element's viewBox with the midpoint X value of the SVG viewport. // Align the midpoint X value of the element's viewBox with the midpoint X value of the SVG viewport.
viewbox_transform.offset.translate_by((svg_box_state.content_width() - CSSPixels::nearest_value_for(view_box.width * viewbox_transform.scale_factor)) / 2, 0); viewbox_transform.offset.translate_by((svg_box_state.content_width() - CSSPixels::nearest_value_for(view_box.width * viewbox_transform.scale_factor_x)) / 2, 0);
break; break;
case SVG::PreserveAspectRatio::Align::xMaxYMin: case SVG::PreserveAspectRatio::Align::xMaxYMin:
case SVG::PreserveAspectRatio::Align::xMaxYMid: case SVG::PreserveAspectRatio::Align::xMaxYMid:
case SVG::PreserveAspectRatio::Align::xMaxYMax: case SVG::PreserveAspectRatio::Align::xMaxYMax:
// Align the <min-x>+<width> of the element's viewBox with the maximum X value of the SVG viewport. // Align the <min-x>+<width> of the element's viewBox with the maximum X value of the SVG viewport.
viewbox_transform.offset.translate_by((svg_box_state.content_width() - CSSPixels::nearest_value_for(view_box.width * viewbox_transform.scale_factor)), 0); viewbox_transform.offset.translate_by((svg_box_state.content_width() - CSSPixels::nearest_value_for(view_box.width * viewbox_transform.scale_factor_x)), 0);
break; break;
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
@ -124,13 +132,13 @@ static ViewBoxTransform scale_and_align_viewbox_content(SVG::PreserveAspectRatio
case SVG::PreserveAspectRatio::Align::xMidYMid: case SVG::PreserveAspectRatio::Align::xMidYMid:
case SVG::PreserveAspectRatio::Align::xMaxYMid: case SVG::PreserveAspectRatio::Align::xMaxYMid:
// Align the midpoint Y value of the element's viewBox with the midpoint Y value of the SVG viewport. // Align the midpoint Y value of the element's viewBox with the midpoint Y value of the SVG viewport.
viewbox_transform.offset.translate_by(0, (svg_box_state.content_height() - CSSPixels::nearest_value_for(view_box.height * viewbox_transform.scale_factor)) / 2); viewbox_transform.offset.translate_by(0, (svg_box_state.content_height() - CSSPixels::nearest_value_for(view_box.height * viewbox_transform.scale_factor_y)) / 2);
break; break;
case SVG::PreserveAspectRatio::Align::xMinYMax: case SVG::PreserveAspectRatio::Align::xMinYMax:
case SVG::PreserveAspectRatio::Align::xMidYMax: case SVG::PreserveAspectRatio::Align::xMidYMax:
case SVG::PreserveAspectRatio::Align::xMaxYMax: case SVG::PreserveAspectRatio::Align::xMaxYMax:
// Align the <min-y>+<height> of the element's viewBox with the maximum Y value of the SVG viewport. // Align the <min-y>+<height> of the element's viewBox with the maximum Y value of the SVG viewport.
viewbox_transform.offset.translate_by(0, (svg_box_state.content_height() - CSSPixels::nearest_value_for(view_box.height * viewbox_transform.scale_factor))); viewbox_transform.offset.translate_by(0, (svg_box_state.content_height() - CSSPixels::nearest_value_for(view_box.height * viewbox_transform.scale_factor_y)));
break; break;
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
@ -232,7 +240,7 @@ void SVGFormattingContext::run(Box const& box, LayoutMode layout_mode, Available
CSSPixelPoint offset = viewbox_offset_and_scale.offset; CSSPixelPoint offset = viewbox_offset_and_scale.offset;
return Gfx::AffineTransform { m_parent_viewbox_transform }.multiply(Gfx::AffineTransform {} return Gfx::AffineTransform { m_parent_viewbox_transform }.multiply(Gfx::AffineTransform {}
.translate(offset.to_type<float>()) .translate(offset.to_type<float>())
.scale(viewbox_offset_and_scale.scale_factor, viewbox_offset_and_scale.scale_factor) .scale(viewbox_offset_and_scale.scale_factor_x, viewbox_offset_and_scale.scale_factor_y)
.translate({ -viewbox->min_x, -viewbox->min_y })); .translate({ -viewbox->min_x, -viewbox->min_y }));
}(); }();