DeprecatedPath.h 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/Optional.h>
  8. #include <AK/Vector.h>
  9. #include <LibGfx/Forward.h>
  10. #include <LibGfx/Line.h>
  11. #include <LibGfx/Point.h>
  12. #include <LibGfx/Rect.h>
  13. namespace Gfx {
  14. class DeprecatedPath;
  15. class DeprecatedPathSegment {
  16. public:
  17. enum Command : u8 {
  18. MoveTo,
  19. LineTo,
  20. QuadraticBezierCurveTo,
  21. CubicBezierCurveTo,
  22. };
  23. ALWAYS_INLINE Command command() const { return m_command; }
  24. ALWAYS_INLINE FloatPoint point() const { return m_points.last(); }
  25. ALWAYS_INLINE FloatPoint through() const
  26. {
  27. VERIFY(m_command == Command::QuadraticBezierCurveTo);
  28. return m_points[0];
  29. }
  30. ALWAYS_INLINE FloatPoint through_0() const
  31. {
  32. VERIFY(m_command == Command::CubicBezierCurveTo);
  33. return m_points[0];
  34. }
  35. ALWAYS_INLINE FloatPoint through_1() const
  36. {
  37. VERIFY(m_command == Command::CubicBezierCurveTo);
  38. return m_points[1];
  39. }
  40. ALWAYS_INLINE ReadonlySpan<FloatPoint> points() const { return m_points; }
  41. static constexpr int points_per_command(Command command)
  42. {
  43. switch (command) {
  44. case Command::MoveTo:
  45. case Command::LineTo:
  46. return 1; // Single point.
  47. case Command::QuadraticBezierCurveTo:
  48. return 2; // Control point + point.
  49. case Command::CubicBezierCurveTo:
  50. return 3; // Two control points + point.
  51. }
  52. VERIFY_NOT_REACHED();
  53. }
  54. DeprecatedPathSegment(Command command, ReadonlySpan<FloatPoint> points)
  55. : m_command(command)
  56. , m_points(points) {};
  57. private:
  58. Command m_command;
  59. ReadonlySpan<FloatPoint> m_points;
  60. };
  61. class PathSegmentIterator {
  62. public:
  63. int operator<=>(PathSegmentIterator other) const
  64. {
  65. if (m_command_index > other.m_command_index)
  66. return 1;
  67. if (m_command_index < other.m_command_index)
  68. return -1;
  69. return 0;
  70. }
  71. bool operator==(PathSegmentIterator other) const { return m_command_index == other.m_command_index; }
  72. bool operator!=(PathSegmentIterator other) const { return m_command_index != other.m_command_index; }
  73. PathSegmentIterator operator++()
  74. {
  75. if (m_command_index < m_commands.size())
  76. m_point_index += DeprecatedPathSegment::points_per_command(m_commands[m_command_index++]);
  77. return *this;
  78. }
  79. PathSegmentIterator operator++(int)
  80. {
  81. PathSegmentIterator old(*this);
  82. ++*this;
  83. return old;
  84. }
  85. PathSegmentIterator operator--()
  86. {
  87. if (m_command_index > 0)
  88. m_point_index -= DeprecatedPathSegment::points_per_command(m_commands[--m_command_index]);
  89. return *this;
  90. }
  91. PathSegmentIterator operator--(int)
  92. {
  93. PathSegmentIterator old(*this);
  94. --*this;
  95. return old;
  96. }
  97. DeprecatedPathSegment operator*() const
  98. {
  99. auto command = m_commands[m_command_index];
  100. return DeprecatedPathSegment { command, m_points.span().slice(m_point_index, DeprecatedPathSegment::points_per_command(command)) };
  101. }
  102. PathSegmentIterator& operator=(PathSegmentIterator const& other)
  103. {
  104. m_point_index = other.m_point_index;
  105. m_command_index = other.m_command_index;
  106. return *this;
  107. }
  108. PathSegmentIterator(PathSegmentIterator const&) = default;
  109. friend DeprecatedPath;
  110. private:
  111. PathSegmentIterator(Vector<FloatPoint> const& points, Vector<DeprecatedPathSegment::Command> const& commands, size_t point_index = 0, size_t command_index = 0)
  112. : m_points(points)
  113. , m_commands(commands)
  114. , m_point_index(point_index)
  115. , m_command_index(command_index)
  116. {
  117. }
  118. // Note: Store reference to vectors from Gfx::DeprecatedPath so appending segments does not invalidate iterators.
  119. Vector<FloatPoint> const& m_points;
  120. Vector<DeprecatedPathSegment::Command> const& m_commands;
  121. size_t m_point_index { 0 };
  122. size_t m_command_index { 0 };
  123. };
  124. class DeprecatedPath {
  125. public:
  126. DeprecatedPath() = default;
  127. void move_to(FloatPoint point)
  128. {
  129. append_segment<DeprecatedPathSegment::MoveTo>(point);
  130. }
  131. void line_to(FloatPoint point)
  132. {
  133. append_segment<DeprecatedPathSegment::LineTo>(point);
  134. invalidate_split_lines();
  135. }
  136. void quadratic_bezier_curve_to(FloatPoint through, FloatPoint point)
  137. {
  138. append_segment<DeprecatedPathSegment::QuadraticBezierCurveTo>(through, point);
  139. invalidate_split_lines();
  140. }
  141. void cubic_bezier_curve_to(FloatPoint c1, FloatPoint c2, FloatPoint p2)
  142. {
  143. append_segment<DeprecatedPathSegment::CubicBezierCurveTo>(c1, c2, p2);
  144. invalidate_split_lines();
  145. }
  146. void elliptical_arc_to(FloatPoint point, FloatSize radii, float x_axis_rotation, bool large_arc, bool sweep);
  147. void arc_to(FloatPoint point, float radius, bool large_arc, bool sweep)
  148. {
  149. elliptical_arc_to(point, { radius, radius }, 0, large_arc, sweep);
  150. }
  151. FloatPoint last_point()
  152. {
  153. if (!m_points.is_empty())
  154. return m_points.last();
  155. return {};
  156. }
  157. void close();
  158. void close_all_subpaths();
  159. DeprecatedPath stroke_to_fill(float thickness) const;
  160. DeprecatedPath copy_transformed(AffineTransform const&) const;
  161. ReadonlySpan<FloatLine> split_lines() const
  162. {
  163. if (!m_split_lines.has_value()) {
  164. const_cast<DeprecatedPath*>(this)->segmentize_path();
  165. VERIFY(m_split_lines.has_value());
  166. }
  167. return m_split_lines->lines;
  168. }
  169. Gfx::FloatRect const& bounding_box() const
  170. {
  171. (void)split_lines();
  172. return m_split_lines->bounding_box;
  173. }
  174. ByteString to_byte_string() const;
  175. PathSegmentIterator begin() const
  176. {
  177. return PathSegmentIterator(m_points, m_commands);
  178. }
  179. PathSegmentIterator end() const
  180. {
  181. return PathSegmentIterator(m_points, m_commands, m_points.size(), m_commands.size());
  182. }
  183. bool is_empty() const
  184. {
  185. return m_commands.is_empty();
  186. }
  187. void clear()
  188. {
  189. *this = DeprecatedPath {};
  190. }
  191. private:
  192. void approximate_elliptical_arc_with_cubic_beziers(FloatPoint center, FloatSize radii, float x_axis_rotation, float theta, float theta_delta);
  193. void invalidate_split_lines()
  194. {
  195. m_split_lines.clear();
  196. }
  197. void segmentize_path();
  198. template<DeprecatedPathSegment::Command command, typename... Args>
  199. void append_segment(Args&&... args)
  200. {
  201. constexpr auto point_count = sizeof...(Args);
  202. static_assert(point_count == DeprecatedPathSegment::points_per_command(command));
  203. FloatPoint points[] { args... };
  204. // Note: This should maintain the invariant that `m_points.last()` is always the last point in the path.
  205. m_points.append(points, point_count);
  206. m_commands.append(command);
  207. }
  208. Vector<FloatPoint> m_points {};
  209. Vector<DeprecatedPathSegment::Command> m_commands {};
  210. struct SplitLines {
  211. Vector<FloatLine> lines;
  212. Gfx::FloatRect bounding_box;
  213. };
  214. Optional<SplitLines> m_split_lines {};
  215. };
  216. }