SVGRadialGradientElement.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. * Copyright (c) 2023, MacDue <macdue@dueutil.tech>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/Bindings/Intrinsics.h>
  7. #include <LibWeb/SVG/AttributeNames.h>
  8. #include <LibWeb/SVG/SVGRadialGradientElement.h>
  9. namespace Web::SVG {
  10. JS_DEFINE_ALLOCATOR(SVGRadialGradientElement);
  11. SVGRadialGradientElement::SVGRadialGradientElement(DOM::Document& document, DOM::QualifiedName qualified_name)
  12. : SVGGradientElement(document, qualified_name)
  13. {
  14. }
  15. void SVGRadialGradientElement::initialize(JS::Realm& realm)
  16. {
  17. Base::initialize(realm);
  18. WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGRadialGradientElement);
  19. }
  20. void SVGRadialGradientElement::attribute_changed(FlyString const& name, Optional<String> const& value)
  21. {
  22. SVGGradientElement::attribute_changed(name, value);
  23. // FIXME: These are <length> or <coordinate> in the spec, but all examples seem to allow percentages
  24. // and unitless values.
  25. if (name == SVG::AttributeNames::cx) {
  26. m_cx = AttributeParser::parse_number_percentage(value.value_or(String {}));
  27. m_paint_style = nullptr;
  28. } else if (name == SVG::AttributeNames::cy) {
  29. m_cy = AttributeParser::parse_number_percentage(value.value_or(String {}));
  30. m_paint_style = nullptr;
  31. } else if (name == SVG::AttributeNames::fx) {
  32. m_fx = AttributeParser::parse_number_percentage(value.value_or(String {}));
  33. m_paint_style = nullptr;
  34. } else if (name == SVG::AttributeNames::fy) {
  35. m_fy = AttributeParser::parse_number_percentage(value.value_or(String {}));
  36. m_paint_style = nullptr;
  37. } else if (name == SVG::AttributeNames::fr) {
  38. m_fr = AttributeParser::parse_number_percentage(value.value_or(String {}));
  39. m_paint_style = nullptr;
  40. } else if (name == SVG::AttributeNames::r) {
  41. m_r = AttributeParser::parse_number_percentage(value.value_or(String {}));
  42. m_paint_style = nullptr;
  43. }
  44. }
  45. // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFXAttribute
  46. NumberPercentage SVGRadialGradientElement::start_circle_x() const
  47. {
  48. HashTable<SVGGradientElement const*> seen_gradients;
  49. return start_circle_x_impl(seen_gradients);
  50. }
  51. NumberPercentage SVGRadialGradientElement::start_circle_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
  52. {
  53. if (m_fx.has_value())
  54. return *m_fx;
  55. // If the element references an element that specifies a value for 'fx', then the value of 'fx' is
  56. // inherited from the referenced element.
  57. if (auto gradient = linked_radial_gradient(seen_gradients))
  58. return gradient->start_circle_x_impl(seen_gradients);
  59. // If attribute ‘fx’ is not specified, ‘fx’ will coincide with the presentational value of ‘cx’ for
  60. // the element whether the value for 'cx' was inherited or not.
  61. return end_circle_x();
  62. }
  63. // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFYAttribute
  64. NumberPercentage SVGRadialGradientElement::start_circle_y() const
  65. {
  66. HashTable<SVGGradientElement const*> seen_gradients;
  67. return start_circle_y_impl(seen_gradients);
  68. }
  69. NumberPercentage SVGRadialGradientElement::start_circle_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
  70. {
  71. if (m_fy.has_value())
  72. return *m_fy;
  73. // If the element references an element that specifies a value for 'fy', then the value of 'fy' is
  74. // inherited from the referenced element.
  75. if (auto gradient = linked_radial_gradient(seen_gradients))
  76. return gradient->start_circle_y_impl(seen_gradients);
  77. // If attribute ‘fy’ is not specified, ‘fy’ will coincide with the presentational value of ‘cy’ for
  78. // the element whether the value for 'cy' was inherited or not.
  79. return end_circle_y();
  80. }
  81. // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFRAttribute
  82. NumberPercentage SVGRadialGradientElement::start_circle_radius() const
  83. {
  84. HashTable<SVGGradientElement const*> seen_gradients;
  85. return start_circle_radius_impl(seen_gradients);
  86. }
  87. NumberPercentage SVGRadialGradientElement::start_circle_radius_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
  88. {
  89. // Note: A negative value is an error.
  90. if (m_fr.has_value() && m_fr->value() >= 0)
  91. return *m_fr;
  92. // if the element references an element that specifies a value for 'fr', then the value of
  93. // 'fr' is inherited from the referenced element.
  94. if (auto gradient = linked_radial_gradient(seen_gradients))
  95. return gradient->start_circle_radius_impl(seen_gradients);
  96. // If the attribute is not specified, the effect is as if a value of '0%' were specified.
  97. return NumberPercentage::create_percentage(0);
  98. }
  99. // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementCXAttribute
  100. NumberPercentage SVGRadialGradientElement::end_circle_x() const
  101. {
  102. HashTable<SVGGradientElement const*> seen_gradients;
  103. return end_circle_x_impl(seen_gradients);
  104. }
  105. NumberPercentage SVGRadialGradientElement::end_circle_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
  106. {
  107. if (m_cx.has_value())
  108. return *m_cx;
  109. if (auto gradient = linked_radial_gradient(seen_gradients))
  110. return gradient->end_circle_x_impl(seen_gradients);
  111. return NumberPercentage::create_percentage(50);
  112. }
  113. // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementCYAttribute
  114. NumberPercentage SVGRadialGradientElement::end_circle_y() const
  115. {
  116. HashTable<SVGGradientElement const*> seen_gradients;
  117. return end_circle_y_impl(seen_gradients);
  118. }
  119. NumberPercentage SVGRadialGradientElement::end_circle_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
  120. {
  121. if (m_cy.has_value())
  122. return *m_cy;
  123. if (auto gradient = linked_radial_gradient(seen_gradients))
  124. return gradient->end_circle_y_impl(seen_gradients);
  125. return NumberPercentage::create_percentage(50);
  126. }
  127. // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementRAttribute
  128. NumberPercentage SVGRadialGradientElement::end_circle_radius() const
  129. {
  130. HashTable<SVGGradientElement const*> seen_gradients;
  131. return end_circle_radius_impl(seen_gradients);
  132. }
  133. NumberPercentage SVGRadialGradientElement::end_circle_radius_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
  134. {
  135. // Note: A negative value is an error.
  136. if (m_r.has_value() && m_r->value() >= 0)
  137. return *m_r;
  138. if (auto gradient = linked_radial_gradient(seen_gradients))
  139. return gradient->end_circle_radius_impl(seen_gradients);
  140. return NumberPercentage::create_percentage(50);
  141. }
  142. Optional<Gfx::PaintStyle const&> SVGRadialGradientElement::to_gfx_paint_style(SVGPaintContext const& paint_context) const
  143. {
  144. auto units = gradient_units();
  145. Gfx::FloatPoint start_center;
  146. float start_radius = 0.0f;
  147. Gfx::FloatPoint end_center;
  148. float end_radius = 0.0f;
  149. if (units == GradientUnits::ObjectBoundingBox) {
  150. // If gradientUnits="objectBoundingBox", the user coordinate system for attributes ‘cx’, ‘cy’, ‘r’, ‘fx’, ‘fy’, and ‘fr’
  151. // is established using the bounding box of the element to which the gradient is applied (see Object bounding box units)
  152. // and then applying the transform specified by attribute ‘gradientTransform’. Percentages represent values relative
  153. // to the bounding box for the object.
  154. start_center = Gfx::FloatPoint { start_circle_x().value(), start_circle_y().value() };
  155. start_radius = start_circle_radius().value();
  156. end_center = Gfx::FloatPoint { end_circle_x().value(), end_circle_y().value() };
  157. end_radius = end_circle_radius().value();
  158. } else {
  159. // GradientUnits::UserSpaceOnUse
  160. // If gradientUnits="userSpaceOnUse", ‘cx’, ‘cy’, ‘r’, ‘fx’, ‘fy’, and ‘fr’ represent values in the coordinate system
  161. // that results from taking the current user coordinate system in place at the time when the gradient element is
  162. // referenced (i.e., the user coordinate system for the element referencing the gradient element via a fill or stroke property)
  163. // and then applying the transform specified by attribute ‘gradientTransform’.
  164. // Percentages represent values relative to the current SVG viewport.
  165. // Note: The start/end centers will be in relative units here.
  166. // They will be resolved at paint time using the gradient paint transform.
  167. start_center = Gfx::FloatPoint {
  168. start_circle_x().resolve_relative_to(paint_context.viewport.width()),
  169. start_circle_y().resolve_relative_to(paint_context.viewport.height()),
  170. };
  171. // FIXME: Where in the spec does it say what axis the radius is relative to?
  172. start_radius = start_circle_radius().resolve_relative_to(paint_context.viewport.width());
  173. end_center = Gfx::FloatPoint {
  174. end_circle_x().resolve_relative_to(paint_context.viewport.width()),
  175. end_circle_y().resolve_relative_to(paint_context.viewport.height()),
  176. };
  177. end_radius = end_circle_radius().resolve_relative_to(paint_context.viewport.width());
  178. }
  179. if (!m_paint_style) {
  180. m_paint_style = Gfx::SVGRadialGradientPaintStyle::create(start_center, start_radius, end_center, end_radius)
  181. .release_value_but_fixme_should_propagate_errors();
  182. // FIXME: Update stops in DOM changes:
  183. add_color_stops(*m_paint_style);
  184. } else {
  185. m_paint_style->set_start_center(start_center);
  186. m_paint_style->set_start_radius(start_radius);
  187. m_paint_style->set_end_center(end_center);
  188. m_paint_style->set_end_radius(end_radius);
  189. }
  190. m_paint_style->set_gradient_transform(gradient_paint_transform(paint_context));
  191. m_paint_style->set_spread_method(to_gfx_spread_method(spread_method()));
  192. return *m_paint_style;
  193. }
  194. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::cx() const
  195. {
  196. // FIXME: Implement this properly.
  197. return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
  198. }
  199. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::cy() const
  200. {
  201. // FIXME: Implement this properly.
  202. return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
  203. }
  204. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::fx() const
  205. {
  206. // FIXME: Implement this properly.
  207. return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
  208. }
  209. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::fy() const
  210. {
  211. // FIXME: Implement this properly.
  212. return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
  213. }
  214. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::fr() const
  215. {
  216. // FIXME: Implement this properly.
  217. return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
  218. }
  219. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::r() const
  220. {
  221. // FIXME: Implement this properly.
  222. return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
  223. }
  224. }