SVGGeometryPaintable.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /*
  2. * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGfx/AntiAliasingPainter.h>
  7. #include <LibWeb/Layout/ImageBox.h>
  8. #include <LibWeb/Painting/SVGGeometryPaintable.h>
  9. #include <LibWeb/SVG/SVGSVGElement.h>
  10. namespace Web::Painting {
  11. NonnullRefPtr<SVGGeometryPaintable> SVGGeometryPaintable::create(Layout::SVGGeometryBox const& layout_box)
  12. {
  13. return adopt_ref(*new SVGGeometryPaintable(layout_box));
  14. }
  15. SVGGeometryPaintable::SVGGeometryPaintable(Layout::SVGGeometryBox const& layout_box)
  16. : SVGGraphicsPaintable(layout_box)
  17. {
  18. }
  19. Layout::SVGGeometryBox const& SVGGeometryPaintable::layout_box() const
  20. {
  21. return static_cast<Layout::SVGGeometryBox const&>(layout_node());
  22. }
  23. void SVGGeometryPaintable::paint(PaintContext& context, PaintPhase phase) const
  24. {
  25. if (!is_visible())
  26. return;
  27. SVGGraphicsPaintable::paint(context, phase);
  28. if (phase != PaintPhase::Foreground)
  29. return;
  30. auto& geometry_element = layout_box().dom_node();
  31. Gfx::AntiAliasingPainter painter { context.painter() };
  32. auto& svg_context = context.svg_context();
  33. auto offset = svg_context.svg_element_position();
  34. painter.translate(offset);
  35. auto const* svg_element = geometry_element.first_ancestor_of_type<SVG::SVGSVGElement>();
  36. auto maybe_view_box = svg_element->view_box();
  37. context.painter().add_clip_rect(enclosing_int_rect(absolute_rect()));
  38. Gfx::Path path = const_cast<SVG::SVGGeometryElement&>(geometry_element).get_path();
  39. if (maybe_view_box.has_value()) {
  40. Gfx::Path new_path;
  41. auto scaling = layout_box().viewbox_scaling();
  42. auto origin = layout_box().viewbox_origin();
  43. auto transform_point = [&scaling, &origin](Gfx::FloatPoint const& point) -> Gfx::FloatPoint {
  44. auto new_point = point;
  45. new_point.translate_by({ -origin.x(), -origin.y() });
  46. new_point.scale_by(scaling);
  47. return new_point;
  48. };
  49. for (auto& segment : path.segments()) {
  50. switch (segment.type()) {
  51. case Gfx::Segment::Type::Invalid:
  52. break;
  53. case Gfx::Segment::Type::MoveTo:
  54. new_path.move_to(transform_point(segment.point()));
  55. break;
  56. case Gfx::Segment::Type::LineTo:
  57. new_path.line_to(transform_point(segment.point()));
  58. break;
  59. case Gfx::Segment::Type::QuadraticBezierCurveTo: {
  60. auto& quadratic_bezier_segment = static_cast<Gfx::QuadraticBezierCurveSegment const&>(segment);
  61. new_path.quadratic_bezier_curve_to(transform_point(quadratic_bezier_segment.through()), transform_point(quadratic_bezier_segment.point()));
  62. break;
  63. }
  64. case Gfx::Segment::Type::CubicBezierCurveTo: {
  65. auto& cubic_bezier_segment = static_cast<Gfx::CubicBezierCurveSegment const&>(segment);
  66. 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()));
  67. break;
  68. }
  69. case Gfx::Segment::Type::EllipticalArcTo: {
  70. auto& elliptical_arc_segment = static_cast<Gfx::EllipticalArcSegment const&>(segment);
  71. new_path.elliptical_arc_to(transform_point(elliptical_arc_segment.point()), elliptical_arc_segment.radii().scaled(scaling, scaling), elliptical_arc_segment.x_axis_rotation(), elliptical_arc_segment.large_arc(), elliptical_arc_segment.sweep());
  72. break;
  73. }
  74. }
  75. }
  76. path = new_path;
  77. }
  78. if (auto fill_color = geometry_element.fill_color().value_or(svg_context.fill_color()); fill_color.alpha() > 0) {
  79. // We need to fill the path before applying the stroke, however the filled
  80. // path must be closed, whereas the stroke path may not necessary be closed.
  81. // Copy the path and close it for filling, but use the previous path for stroke
  82. auto closed_path = path;
  83. closed_path.close();
  84. // Fills are computed as though all paths are closed (https://svgwg.org/svg2-draft/painting.html#FillProperties)
  85. painter.fill_path(
  86. closed_path,
  87. fill_color,
  88. Gfx::Painter::WindingRule::EvenOdd);
  89. }
  90. if (auto stroke_color = geometry_element.stroke_color().value_or(svg_context.stroke_color()); stroke_color.alpha() > 0) {
  91. painter.stroke_path(
  92. path,
  93. stroke_color,
  94. geometry_element.stroke_width().value_or(svg_context.stroke_width()));
  95. }
  96. painter.translate(-offset);
  97. context.painter().clear_clip_rect();
  98. }
  99. }