diff options
author | Andreas Kling <kling@serenityos.org> | 2022-03-10 14:02:25 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-03-11 00:21:49 +0100 |
commit | 02b316fd5cc2039a30cab33edee4595900cb35aa (patch) | |
tree | 22c7ac3c6c93d97fd7548a2b1286516260545a05 /Userland/Libraries/LibWeb/Painting/SVGGeometryPaintable.cpp | |
parent | f6497b64ac52674e99564d83c4fad6229d06b1ef (diff) | |
download | serenity-02b316fd5cc2039a30cab33edee4595900cb35aa.zip |
LibWeb: Let Paintable perform the painting
This patch adds a bunch of Paintable subclasses, each corresponding to
the Layout::Node subclasses that had a paint() override. All painting
logic is moved from layout nodes into their corresponding paintables.
Paintables are now created by asking a Layout::Box to produce one:
static NonnullOwnPtr<Paintable> Layout::Box::create_paintable()
Note that inline nodes still have their painting logic. Since they
are not boxes, and all paintables have a corresponding box, we'll need
to come up with some other solution for them.
Diffstat (limited to 'Userland/Libraries/LibWeb/Painting/SVGGeometryPaintable.cpp')
-rw-r--r-- | Userland/Libraries/LibWeb/Painting/SVGGeometryPaintable.cpp | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/Painting/SVGGeometryPaintable.cpp b/Userland/Libraries/LibWeb/Painting/SVGGeometryPaintable.cpp new file mode 100644 index 0000000000..f238b9a8a8 --- /dev/null +++ b/Userland/Libraries/LibWeb/Painting/SVGGeometryPaintable.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibGfx/AntiAliasingPainter.h> +#include <LibWeb/Layout/ImageBox.h> +#include <LibWeb/Painting/SVGGeometryPaintable.h> +#include <LibWeb/SVG/SVGSVGElement.h> + +namespace Web::Painting { + +NonnullOwnPtr<SVGGeometryPaintable> SVGGeometryPaintable::create(Layout::SVGGeometryBox const& layout_box) +{ + return adopt_own(*new SVGGeometryPaintable(layout_box)); +} + +SVGGeometryPaintable::SVGGeometryPaintable(Layout::SVGGeometryBox const& layout_box) + : SVGGraphicsPaintable(layout_box) +{ +} + +Layout::SVGGeometryBox const& SVGGeometryPaintable::layout_box() const +{ + return static_cast<Layout::SVGGeometryBox const&>(m_layout_box); +} + +void SVGGeometryPaintable::paint(PaintContext& context, PaintPhase phase) const +{ + if (!is_visible()) + return; + + SVGGraphicsPaintable::paint(context, phase); + + if (phase != PaintPhase::Foreground) + return; + + auto& geometry_element = layout_box().dom_node(); + + Gfx::AntiAliasingPainter painter { context.painter() }; + auto& svg_context = context.svg_context(); + + auto offset = svg_context.svg_element_position(); + painter.translate(offset); + + auto const* svg_element = geometry_element.first_ancestor_of_type<SVG::SVGSVGElement>(); + auto maybe_view_box = svg_element->view_box(); + + context.painter().add_clip_rect(enclosing_int_rect(absolute_rect())); + + Gfx::Path path = const_cast<SVG::SVGGeometryElement&>(geometry_element).get_path(); + + if (maybe_view_box.has_value()) { + Gfx::Path new_path; + auto scaling = layout_box().viewbox_scaling(); + auto origin = layout_box().viewbox_origin(); + + auto transform_point = [&scaling, &origin](Gfx::FloatPoint const& point) -> Gfx::FloatPoint { + auto new_point = point; + new_point.translate_by({ -origin.x(), -origin.y() }); + new_point.scale_by(scaling); + return new_point; + }; + + for (auto& segment : path.segments()) { + switch (segment.type()) { + case Gfx::Segment::Type::Invalid: + break; + case Gfx::Segment::Type::MoveTo: + new_path.move_to(transform_point(segment.point())); + break; + case Gfx::Segment::Type::LineTo: + new_path.line_to(transform_point(segment.point())); + break; + case Gfx::Segment::Type::QuadraticBezierCurveTo: { + auto& quadratic_bezier_segment = static_cast<Gfx::QuadraticBezierCurveSegment const&>(segment); + new_path.quadratic_bezier_curve_to(transform_point(quadratic_bezier_segment.through()), transform_point(quadratic_bezier_segment.point())); + break; + } + case Gfx::Segment::Type::CubicBezierCurveTo: { + auto& cubic_bezier_segment = static_cast<Gfx::CubicBezierCurveSegment const&>(segment); + new_path.cubic_bezier_curve_to(transform_point(cubic_bezier_segment.through_0()), transform_point(cubic_bezier_segment.through_1()), transform_point(cubic_bezier_segment.point())); + break; + } + case Gfx::Segment::Type::EllipticalArcTo: { + auto& elliptical_arc_segment = static_cast<Gfx::EllipticalArcSegment const&>(segment); + new_path.elliptical_arc_to(transform_point(elliptical_arc_segment.point()), elliptical_arc_segment.radii().scaled(scaling, scaling), elliptical_arc_segment.x_axis_rotation(), false, false); + break; + } + } + } + + path = new_path; + } + + if (auto fill_color = geometry_element.fill_color().value_or(svg_context.fill_color()); fill_color.alpha() > 0) { + // We need to fill the path before applying the stroke, however the filled + // path must be closed, whereas the stroke path may not necessary be closed. + // Copy the path and close it for filling, but use the previous path for stroke + auto closed_path = path; + closed_path.close(); + + // Fills are computed as though all paths are closed (https://svgwg.org/svg2-draft/painting.html#FillProperties) + painter.fill_path( + closed_path, + fill_color, + Gfx::Painter::WindingRule::EvenOdd); + } + + if (auto stroke_color = geometry_element.stroke_color().value_or(svg_context.stroke_color()); stroke_color.alpha() > 0) { + painter.stroke_path( + path, + stroke_color, + geometry_element.stroke_width().value_or(svg_context.stroke_width())); + } + + painter.translate(-offset); + context.painter().clear_clip_rect(); +} + +} |