SVGGeometryPaintable.cpp 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. /*
  2. * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2023, MacDue <macdue@dueutil.tech>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibGfx/AntiAliasingPainter.h>
  8. #include <LibWeb/Layout/ImageBox.h>
  9. #include <LibWeb/Painting/SVGGeometryPaintable.h>
  10. #include <LibWeb/SVG/SVGSVGElement.h>
  11. namespace Web::Painting {
  12. JS::NonnullGCPtr<SVGGeometryPaintable> SVGGeometryPaintable::create(Layout::SVGGeometryBox const& layout_box)
  13. {
  14. return layout_box.heap().allocate_without_realm<SVGGeometryPaintable>(layout_box);
  15. }
  16. SVGGeometryPaintable::SVGGeometryPaintable(Layout::SVGGeometryBox const& layout_box)
  17. : SVGGraphicsPaintable(layout_box)
  18. {
  19. }
  20. Layout::SVGGeometryBox const& SVGGeometryPaintable::layout_box() const
  21. {
  22. return static_cast<Layout::SVGGeometryBox const&>(layout_node());
  23. }
  24. void SVGGeometryPaintable::paint(PaintContext& context, PaintPhase phase) const
  25. {
  26. if (!is_visible())
  27. return;
  28. SVGGraphicsPaintable::paint(context, phase);
  29. if (phase != PaintPhase::Foreground)
  30. return;
  31. auto& geometry_element = layout_box().dom_node();
  32. Gfx::AntiAliasingPainter painter { context.painter() };
  33. auto& svg_context = context.svg_context();
  34. // FIXME: This should not be trucated to an int.
  35. auto offset = context.floored_device_point(svg_context.svg_element_position()).to_type<int>().to_type<float>();
  36. painter.translate(offset);
  37. auto const* svg_element = geometry_element.first_ancestor_of_type<SVG::SVGSVGElement>();
  38. auto maybe_view_box = svg_element->view_box();
  39. context.painter().add_clip_rect(context.enclosing_device_rect(absolute_rect()).to_type<int>());
  40. auto css_scale = context.device_pixels_per_css_pixel();
  41. Gfx::Path path = const_cast<SVG::SVGGeometryElement&>(geometry_element).get_path().copy_transformed(Gfx::AffineTransform {}.scale(css_scale, css_scale).multiply(layout_box().layout_transform()));
  42. if (auto fill_color = geometry_element.fill_color().value_or(svg_context.fill_color()); fill_color.alpha() > 0) {
  43. // We need to fill the path before applying the stroke, however the filled
  44. // path must be closed, whereas the stroke path may not necessary be closed.
  45. // Copy the path and close it for filling, but use the previous path for stroke
  46. auto closed_path = path;
  47. closed_path.close();
  48. // Fills are computed as though all paths are closed (https://svgwg.org/svg2-draft/painting.html#FillProperties)
  49. painter.fill_path(
  50. closed_path,
  51. fill_color,
  52. Gfx::Painter::WindingRule::EvenOdd);
  53. }
  54. if (auto stroke_color = geometry_element.stroke_color().value_or(svg_context.stroke_color()); stroke_color.alpha() > 0) {
  55. painter.stroke_path(
  56. path,
  57. stroke_color,
  58. geometry_element.stroke_width().value_or(svg_context.stroke_width()) * context.device_pixels_per_css_pixel());
  59. }
  60. painter.translate(-offset);
  61. context.painter().clear_clip_rect();
  62. }
  63. }