LibWeb: Remove SVGContext

The SVGContext is a leftover from when SVG properties were more ad-hoc.
All properties are now (for better or worse) treated as CSS properties
(or handled elsewhere). This makes the SVGContext's fill/stroke
inheritance handling unnecessary.
This commit is contained in:
MacDue 2023-07-01 21:24:04 +01:00 committed by Jelle Raaijmakers
parent 23a7ccf607
commit 7d26383426
Notes: sideshowbarker 2024-07-16 23:08:48 +09:00
11 changed files with 18 additions and 150 deletions

View file

@ -17,24 +17,6 @@ PaintContext::PaintContext(Gfx::Painter& painter, Palette const& palette, double
{
}
SVGContext& PaintContext::svg_context()
{
// FIXME: This is a total hack to avoid crashing on content that has SVG elements embedded directly in HTML without an <svg> element.
if (!m_svg_context.has_value())
m_svg_context = SVGContext { {} };
return m_svg_context.value();
}
void PaintContext::set_svg_context(SVGContext context)
{
m_svg_context = move(context);
}
void PaintContext::clear_svg_context()
{
m_svg_context.clear();
}
CSSPixelRect PaintContext::css_viewport_rect() const
{
return {

View file

@ -12,7 +12,6 @@
#include <LibGfx/Palette.h>
#include <LibGfx/Rect.h>
#include <LibWeb/PixelUnits.h>
#include <LibWeb/SVG/SVGContext.h>
namespace Web {
@ -23,11 +22,6 @@ public:
Gfx::Painter& painter() const { return m_painter; }
Palette const& palette() const { return m_palette; }
bool has_svg_context() const { return m_svg_context.has_value(); }
SVGContext& svg_context();
void set_svg_context(SVGContext);
void clear_svg_context();
bool should_show_line_box_borders() const { return m_should_show_line_box_borders; }
void set_should_show_line_box_borders(bool value) { m_should_show_line_box_borders = value; }
@ -60,7 +54,6 @@ public:
clone.m_device_viewport_rect = m_device_viewport_rect;
clone.m_should_show_line_box_borders = m_should_show_line_box_borders;
clone.m_focus = m_focus;
clone.m_svg_context = m_svg_context;
return clone;
}
@ -69,7 +62,6 @@ public:
private:
Gfx::Painter& m_painter;
Palette m_palette;
Optional<SVGContext> m_svg_context;
double m_device_pixels_per_css_pixel { 0 };
DevicePixelRect m_device_viewport_rect;
bool m_should_show_line_box_borders { false };

View file

@ -66,15 +66,16 @@ void SVGGeometryPaintable::paint(PaintContext& context, PaintPhase phase) const
auto& geometry_element = layout_box().dom_node();
auto const* svg_element = geometry_element.shadow_including_first_ancestor_of_type<SVG::SVGSVGElement>();
auto svg_element_rect = svg_element->paintable_box()->absolute_rect();
Gfx::AntiAliasingPainter painter { context.painter() };
auto& svg_context = context.svg_context();
// FIXME: This should not be trucated to an int.
Gfx::PainterStateSaver save_painter { context.painter() };
auto offset = context.floored_device_point(svg_context.svg_element_position()).to_type<int>().to_type<float>();
auto offset = context.floored_device_point(svg_element_rect.location()).to_type<int>().to_type<float>();
painter.translate(offset);
auto const* svg_element = geometry_element.shadow_including_first_ancestor_of_type<SVG::SVGSVGElement>();
auto maybe_view_box = svg_element->view_box();
auto transform = layout_box().layout_transform();
@ -102,7 +103,7 @@ void SVGGeometryPaintable::paint(PaintContext& context, PaintPhase phase) const
auto svg_viewport = [&] {
if (maybe_view_box.has_value())
return Gfx::FloatRect { maybe_view_box->min_x, maybe_view_box->min_y, maybe_view_box->width, maybe_view_box->height };
return Gfx::FloatRect { { 0, 0 }, svg_context.svg_element_size().to_type<double>().to_type<float>() };
return Gfx::FloatRect { { 0, 0 }, svg_element_rect.size().to_type<double>().to_type<float>() };
}();
SVG::SVGPaintContext paint_context {
@ -111,26 +112,25 @@ void SVGGeometryPaintable::paint(PaintContext& context, PaintPhase phase) const
.transform = paint_transform
};
auto fill_opacity = geometry_element.fill_opacity().value_or(svg_context.fill_opacity());
auto winding_rule = to_gfx_winding_rule(geometry_element.fill_rule().value_or(svg_context.fill_rule()));
auto fill_opacity = geometry_element.fill_opacity().value_or(1);
auto winding_rule = to_gfx_winding_rule(geometry_element.fill_rule().value_or(SVG::FillRule::Nonzero));
if (auto paint_style = geometry_element.fill_paint_style(paint_context); paint_style.has_value()) {
painter.fill_path(
closed_path(),
*paint_style,
fill_opacity,
winding_rule);
} else if (auto fill_color = geometry_element.fill_color().value_or(svg_context.fill_color()).with_opacity(fill_opacity); fill_color.alpha() > 0) {
} else if (auto fill_color = geometry_element.fill_color(); fill_color.has_value()) {
painter.fill_path(
closed_path(),
fill_color,
fill_color->with_opacity(fill_opacity),
winding_rule);
}
auto stroke_opacity = geometry_element.stroke_opacity().value_or(svg_context.stroke_opacity());
auto stroke_opacity = geometry_element.stroke_opacity().value_or(1);
// Note: This is assuming .x_scale() == .y_scale() (which it does currently).
float stroke_thickness = geometry_element.stroke_width().value_or(svg_context.stroke_width()) * viewbox_scale;
float stroke_thickness = geometry_element.stroke_width().value_or(1) * viewbox_scale;
if (auto paint_style = geometry_element.stroke_paint_style(paint_context); paint_style.has_value()) {
painter.stroke_path(
@ -138,10 +138,10 @@ void SVGGeometryPaintable::paint(PaintContext& context, PaintPhase phase) const
*paint_style,
stroke_thickness,
stroke_opacity);
} else if (auto stroke_color = geometry_element.stroke_color().value_or(svg_context.stroke_color()).with_opacity(stroke_opacity); stroke_color.alpha() > 0) {
} else if (auto stroke_color = geometry_element.stroke_color(); stroke_color.has_value()) {
painter.stroke_path(
path,
stroke_color,
stroke_color->with_opacity(stroke_opacity),
stroke_thickness);
}
}

View file

@ -24,26 +24,4 @@ Layout::SVGGraphicsBox const& SVGGraphicsPaintable::layout_box() const
return static_cast<Layout::SVGGraphicsBox const&>(layout_node());
}
void SVGGraphicsPaintable::before_children_paint(PaintContext& context, PaintPhase phase) const
{
SVGPaintable::before_children_paint(context, phase);
if (phase != PaintPhase::Foreground)
return;
auto& graphics_element = layout_box().dom_node();
if (auto fill_rule = graphics_element.fill_rule(); fill_rule.has_value())
context.svg_context().set_fill_rule(*fill_rule);
if (auto fill_color = graphics_element.fill_color(); fill_color.has_value())
context.svg_context().set_fill_color(*fill_color);
if (auto stroke_color = graphics_element.stroke_color(); stroke_color.has_value())
context.svg_context().set_stroke_color(*stroke_color);
if (auto stroke_width = graphics_element.stroke_width(); stroke_width.has_value())
context.svg_context().set_stroke_width(*stroke_width);
if (auto fill_opacity = graphics_element.fill_opacity(); fill_opacity.has_value())
context.svg_context().set_fill_opacity(*fill_opacity);
if (auto stroke_opacity = graphics_element.stroke_opacity(); stroke_opacity.has_value())
context.svg_context().set_stroke_opacity(*stroke_opacity);
}
}

View file

@ -17,8 +17,6 @@ class SVGGraphicsPaintable : public SVGPaintable {
public:
static JS::NonnullGCPtr<SVGGraphicsPaintable> create(Layout::SVGGraphicsBox const&);
virtual void before_children_paint(PaintContext&, PaintPhase) const override;
Layout::SVGGraphicsBox const& layout_box() const;
protected:

View file

@ -20,22 +20,6 @@ Layout::SVGBox const& SVGPaintable::layout_box() const
return static_cast<Layout::SVGBox const&>(layout_node());
}
void SVGPaintable::before_children_paint(PaintContext& context, PaintPhase phase) const
{
PaintableBox::before_children_paint(context, phase);
if (phase != PaintPhase::Foreground)
return;
context.svg_context().save();
}
void SVGPaintable::after_children_paint(PaintContext& context, PaintPhase phase) const
{
PaintableBox::after_children_paint(context, phase);
if (phase != PaintPhase::Foreground)
return;
context.svg_context().restore();
}
CSSPixelRect SVGPaintable::compute_absolute_rect() const
{
if (auto* svg_svg_box = layout_box().first_ancestor_of_type<Layout::SVGSVGBox>()) {

View file

@ -15,9 +15,6 @@ class SVGPaintable : public PaintableBox {
JS_CELL(SVGPaintable, PaintableBox);
public:
virtual void before_children_paint(PaintContext&, PaintPhase) const override;
virtual void after_children_paint(PaintContext&, PaintPhase) const override;
Layout::SVGBox const& layout_box() const;
protected:

View file

@ -30,9 +30,6 @@ void SVGSVGPaintable::before_children_paint(PaintContext& context, PaintPhase ph
if (phase != PaintPhase::Foreground)
return;
if (!context.has_svg_context())
context.set_svg_context(SVGContext(absolute_rect()));
context.painter().save();
context.painter().add_clip_rect(context.enclosing_device_rect(absolute_rect()).to_type<int>());
}
@ -44,7 +41,6 @@ void SVGSVGPaintable::after_children_paint(PaintContext& context, PaintPhase pha
return;
context.painter().restore();
context.clear_svg_context();
}
}

View file

@ -38,9 +38,12 @@ void SVGTextPaintable::paint(PaintContext& context, PaintPhase phase) const
auto& painter = context.painter();
auto& text_element = layout_box().dom_node();
auto const* svg_element = text_element.shadow_including_first_ancestor_of_type<SVG::SVGSVGElement>();
auto svg_element_rect = svg_element->paintable_box()->absolute_rect();
Gfx::PainterStateSaver save_painter { painter };
auto& svg_context = context.svg_context();
auto svg_context_offset = context.floored_device_point(svg_context.svg_element_position()).to_type<int>();
auto svg_context_offset = context.floored_device_point(svg_element_rect.location()).to_type<int>();
painter.translate(svg_context_offset);
auto const& dom_node = layout_box().dom_node();

View file

@ -1,61 +0,0 @@
/*
* Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Vector.h>
#include <LibGfx/Color.h>
#include <LibGfx/Rect.h>
#include <LibWeb/SVG/AttributeParser.h>
namespace Web {
class SVGContext {
public:
SVGContext(CSSPixelRect svg_element_bounds)
: m_svg_element_bounds(svg_element_bounds)
{
m_states.append(State());
}
SVG::FillRule fill_rule() const { return state().fill_rule; }
Gfx::Color fill_color() const { return state().fill_color; }
Gfx::Color stroke_color() const { return state().stroke_color; }
float stroke_width() const { return state().stroke_width; }
float fill_opacity() const { return state().fill_opacity; }
float stroke_opacity() const { return state().stroke_opacity; }
void set_fill_rule(SVG::FillRule fill_rule) { state().fill_rule = fill_rule; }
void set_fill_color(Gfx::Color color) { state().fill_color = color; }
void set_stroke_color(Gfx::Color color) { state().stroke_color = color; }
void set_stroke_width(float width) { state().stroke_width = width; }
void set_fill_opacity(float opacity) { state().fill_opacity = opacity; }
void set_stroke_opacity(float opacity) { state().stroke_opacity = opacity; }
CSSPixelPoint svg_element_position() const { return m_svg_element_bounds.top_left(); }
CSSPixelSize svg_element_size() const { return m_svg_element_bounds.size(); }
void save() { m_states.append(m_states.last()); }
void restore() { m_states.take_last(); }
private:
struct State {
SVG::FillRule fill_rule { SVG::FillRule::Nonzero };
Gfx::Color fill_color { Gfx::Color::Transparent };
Gfx::Color stroke_color { Gfx::Color::Transparent };
float stroke_width { 1.0f };
float fill_opacity { 1.0f };
float stroke_opacity { 1.0f };
};
State const& state() const { return m_states.last(); }
State& state() { return m_states.last(); }
CSSPixelRect m_svg_element_bounds;
Vector<State> m_states;
};
}

View file

@ -108,7 +108,6 @@ Gfx::AffineTransform transform_from_transform_list(ReadonlySpan<Transform> trans
Gfx::AffineTransform SVGGraphicsElement::get_transform() const
{
// FIXME: It would be nice to do this using the SVGContext, however, then layout/hit testing knows nothing about the transform.
Gfx::AffineTransform transform = m_transform;
for (auto* svg_ancestor = shadow_including_first_ancestor_of_type<SVGGraphicsElement>(); svg_ancestor; svg_ancestor = svg_ancestor->shadow_including_first_ancestor_of_type<SVGGraphicsElement>()) {
transform = Gfx::AffineTransform { svg_ancestor->m_transform }.multiply(transform);