BasicShapeStyleValue.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /*
  2. * Copyright (c) 2024, MacDue <macdue@dueutil.tech>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "BasicShapeStyleValue.h"
  7. #include <LibGfx/Path.h>
  8. namespace Web::CSS {
  9. static Gfx::Path path_from_resolved_rect(float top, float right, float bottom, float left)
  10. {
  11. Gfx::Path path;
  12. path.move_to(Gfx::FloatPoint { left, top });
  13. path.line_to(Gfx::FloatPoint { right, top });
  14. path.line_to(Gfx::FloatPoint { right, bottom });
  15. path.line_to(Gfx::FloatPoint { left, bottom });
  16. path.close();
  17. return path;
  18. }
  19. Gfx::Path Inset::to_path(CSSPixelRect reference_box, Layout::Node const& node) const
  20. {
  21. // FIXME: A pair of insets in either dimension that add up to more than the used dimension
  22. // (such as left and right insets of 75% apiece) use the CSS Backgrounds 3 § 4.5 Overlapping Curves rules
  23. // to proportionally reduce the inset effect to 100%.
  24. auto top = inset_box.top().to_px(node, reference_box.height()).to_float();
  25. auto right = reference_box.width().to_float() - inset_box.right().to_px(node, reference_box.width()).to_float();
  26. auto bottom = reference_box.height().to_float() - inset_box.bottom().to_px(node, reference_box.height()).to_float();
  27. auto left = inset_box.left().to_px(node, reference_box.width()).to_float();
  28. return path_from_resolved_rect(top, right, bottom, left);
  29. }
  30. String Inset::to_string() const
  31. {
  32. return MUST(String::formatted("inset({} {} {} {})", inset_box.top(), inset_box.right(), inset_box.bottom(), inset_box.left()));
  33. }
  34. Gfx::Path Xywh::to_path(CSSPixelRect reference_box, Layout::Node const& node) const
  35. {
  36. auto top = y.to_px(node, reference_box.height()).to_float();
  37. auto bottom = top + max(0.0f, height.to_px(node, reference_box.height()).to_float());
  38. auto left = x.to_px(node, reference_box.width()).to_float();
  39. auto right = left + max(0.0f, width.to_px(node, reference_box.width()).to_float());
  40. return path_from_resolved_rect(top, right, bottom, left);
  41. }
  42. String Xywh::to_string() const
  43. {
  44. return MUST(String::formatted("xywh({} {} {} {})", x, y, width, height));
  45. }
  46. Gfx::Path Rect::to_path(CSSPixelRect reference_box, Layout::Node const& node) const
  47. {
  48. // An auto value makes the edge of the box coincide with the corresponding edge of the reference box:
  49. // it’s equivalent to 0% as the first (top) or fourth (left) value, and equivalent to 100% as the second (right) or third (bottom) value.
  50. auto top = box.top().is_auto() ? 0 : box.top().to_px(node, reference_box.height()).to_float();
  51. auto right = box.right().is_auto() ? reference_box.width().to_float() : box.right().to_px(node, reference_box.width()).to_float();
  52. auto bottom = box.bottom().is_auto() ? reference_box.height().to_float() : box.bottom().to_px(node, reference_box.height()).to_float();
  53. auto left = box.left().is_auto() ? 0 : box.left().to_px(node, reference_box.width()).to_float();
  54. // The second (right) and third (bottom) values are floored by the fourth (left) and second (top) values, respectively.
  55. return path_from_resolved_rect(top, max(right, left), max(bottom, top), left);
  56. }
  57. String Rect::to_string() const
  58. {
  59. return MUST(String::formatted("rect({} {} {} {})", box.top(), box.right(), box.bottom(), box.left()));
  60. }
  61. static String radius_to_string(ShapeRadius radius)
  62. {
  63. return radius.visit(
  64. [](LengthPercentage const& length_percentage) { return length_percentage.to_string(); },
  65. [](FitSide const& side) {
  66. switch (side) {
  67. case FitSide::ClosestSide:
  68. return "closest-side"_string;
  69. case FitSide::FarthestSide:
  70. return "farthest-side"_string;
  71. }
  72. VERIFY_NOT_REACHED();
  73. });
  74. }
  75. Gfx::Path Circle::to_path(CSSPixelRect reference_box, Layout::Node const& node) const
  76. {
  77. // Translating the reference box because PositionStyleValues are resolved to an absolute position.
  78. auto center = position->resolved(node, reference_box.translated(-reference_box.x(), -reference_box.y()));
  79. float radius_px = radius.visit(
  80. [&](LengthPercentage const& length_percentage) {
  81. auto radius_ref = sqrt(pow(reference_box.width().to_float(), 2) + pow(reference_box.height().to_float(), 2)) / AK::Sqrt2<float>;
  82. return max(0.0f, length_percentage.to_px(node, CSSPixels(radius_ref)).to_float());
  83. },
  84. [&](FitSide const& side) {
  85. switch (side) {
  86. case FitSide::ClosestSide:
  87. float closest;
  88. closest = min(abs(center.x()), abs(center.y())).to_float();
  89. closest = min(closest, abs(reference_box.width() - center.x()).to_float());
  90. closest = min(closest, abs(reference_box.height() - center.y()).to_float());
  91. return closest;
  92. case FitSide::FarthestSide:
  93. float farthest;
  94. farthest = max(abs(center.x()), abs(center.y())).to_float();
  95. farthest = max(farthest, abs(reference_box.width() - center.x()).to_float());
  96. farthest = max(farthest, abs(reference_box.height() - center.y()).to_float());
  97. return farthest;
  98. }
  99. VERIFY_NOT_REACHED();
  100. });
  101. Gfx::Path path;
  102. path.move_to(Gfx::FloatPoint { center.x().to_float(), center.y().to_float() + radius_px });
  103. path.arc_to(Gfx::FloatPoint { center.x().to_float(), center.y().to_float() - radius_px }, radius_px, true, true);
  104. path.arc_to(Gfx::FloatPoint { center.x().to_float(), center.y().to_float() + radius_px }, radius_px, true, true);
  105. return path;
  106. }
  107. String Circle::to_string() const
  108. {
  109. return MUST(String::formatted("circle({} at {})", radius_to_string(radius), position->to_string()));
  110. }
  111. Gfx::Path Ellipse::to_path(CSSPixelRect reference_box, Layout::Node const& node) const
  112. {
  113. // Translating the reference box because PositionStyleValues are resolved to an absolute position.
  114. auto center = position->resolved(node, reference_box.translated(-reference_box.x(), -reference_box.y()));
  115. float radius_x_px = radius_x.visit(
  116. [&](LengthPercentage const& length_percentage) {
  117. return max(0.0f, length_percentage.to_px(node, reference_box.width()).to_float());
  118. },
  119. [&](FitSide const& side) {
  120. switch (side) {
  121. case FitSide::ClosestSide:
  122. return min(abs(center.x()), abs(reference_box.width() - center.x())).to_float();
  123. case FitSide::FarthestSide:
  124. return max(abs(center.x()), abs(reference_box.width() - center.x())).to_float();
  125. }
  126. VERIFY_NOT_REACHED();
  127. });
  128. float radius_y_px = radius_y.visit(
  129. [&](LengthPercentage const& length_percentage) {
  130. return max(0.0f, length_percentage.to_px(node, reference_box.height()).to_float());
  131. },
  132. [&](FitSide const& side) {
  133. switch (side) {
  134. case FitSide::ClosestSide:
  135. return min(abs(center.y()), abs(reference_box.height() - center.y())).to_float();
  136. case FitSide::FarthestSide:
  137. return max(abs(center.y()), abs(reference_box.height() - center.y())).to_float();
  138. }
  139. VERIFY_NOT_REACHED();
  140. });
  141. Gfx::Path path;
  142. path.move_to(Gfx::FloatPoint { center.x().to_float(), center.y().to_float() + radius_y_px });
  143. path.elliptical_arc_to(Gfx::FloatPoint { center.x().to_float(), center.y().to_float() - radius_y_px }, Gfx::FloatSize { radius_x_px, radius_y_px }, 0, true, true);
  144. path.elliptical_arc_to(Gfx::FloatPoint { center.x().to_float(), center.y().to_float() + radius_y_px }, Gfx::FloatSize { radius_x_px, radius_y_px }, 0, true, true);
  145. return path;
  146. }
  147. String Ellipse::to_string() const
  148. {
  149. return MUST(String::formatted("ellipse({} {} at {})", radius_to_string(radius_x), radius_to_string(radius_y), position->to_string()));
  150. }
  151. Gfx::Path Polygon::to_path(CSSPixelRect reference_box, Layout::Node const& node) const
  152. {
  153. Gfx::Path path;
  154. bool first = true;
  155. for (auto const& point : points) {
  156. Gfx::FloatPoint resolved_point {
  157. point.x.to_px(node, reference_box.width()).to_float(),
  158. point.y.to_px(node, reference_box.height()).to_float()
  159. };
  160. if (first)
  161. path.move_to(resolved_point);
  162. else
  163. path.line_to(resolved_point);
  164. first = false;
  165. }
  166. path.close();
  167. return path;
  168. }
  169. String Polygon::to_string() const
  170. {
  171. StringBuilder builder;
  172. builder.append("polygon("sv);
  173. bool first = true;
  174. for (auto const& point : points) {
  175. if (!first)
  176. builder.append(',');
  177. builder.appendff("{} {}", point.x, point.y);
  178. first = false;
  179. }
  180. builder.append(')');
  181. return MUST(builder.to_string());
  182. }
  183. BasicShapeStyleValue::~BasicShapeStyleValue() = default;
  184. Gfx::Path BasicShapeStyleValue::to_path(CSSPixelRect reference_box, Layout::Node const& node) const
  185. {
  186. return m_basic_shape.visit([&](auto const& shape) {
  187. return shape.to_path(reference_box, node);
  188. });
  189. }
  190. String BasicShapeStyleValue::to_string() const
  191. {
  192. return m_basic_shape.visit([](auto const& shape) {
  193. return shape.to_string();
  194. });
  195. }
  196. }