SVGRadialGradientElement.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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. set_prototype(&Bindings::ensure_web_prototype<Bindings::SVGRadialGradientElementPrototype>(realm, "SVGRadialGradientElement"_fly_string));
  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. if (m_fx.has_value())
  49. return *m_fx;
  50. // If the element references an element that specifies a value for 'fx', then the value of 'fx' is
  51. // inherited from the referenced element.
  52. if (auto gradient = linked_radial_gradient())
  53. return gradient->start_circle_x();
  54. // If attribute ‘fx’ is not specified, ‘fx’ will coincide with the presentational value of ‘cx’ for
  55. // the element whether the value for 'cx' was inherited or not.
  56. return end_circle_x();
  57. }
  58. // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFYAttribute
  59. NumberPercentage SVGRadialGradientElement::start_circle_y() const
  60. {
  61. if (m_fy.has_value())
  62. return *m_fy;
  63. // If the element references an element that specifies a value for 'fy', then the value of 'fy' is
  64. // inherited from the referenced element.
  65. if (auto gradient = linked_radial_gradient())
  66. return gradient->start_circle_y();
  67. // If attribute ‘fy’ is not specified, ‘fy’ will coincide with the presentational value of ‘cy’ for
  68. // the element whether the value for 'cy' was inherited or not.
  69. return end_circle_y();
  70. }
  71. // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFRAttribute
  72. NumberPercentage SVGRadialGradientElement::start_circle_radius() const
  73. {
  74. // Note: A negative value is an error.
  75. if (m_fr.has_value() && m_fr->value() >= 0)
  76. return *m_fr;
  77. // if the element references an element that specifies a value for 'fr', then the value of
  78. // 'fr' is inherited from the referenced element.
  79. if (auto gradient = linked_radial_gradient())
  80. return gradient->start_circle_radius();
  81. // If the attribute is not specified, the effect is as if a value of '0%' were specified.
  82. return NumberPercentage::create_percentage(0);
  83. }
  84. // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementCXAttribute
  85. NumberPercentage SVGRadialGradientElement::end_circle_x() const
  86. {
  87. if (m_cx.has_value())
  88. return *m_cx;
  89. if (auto gradient = linked_radial_gradient())
  90. return gradient->end_circle_x();
  91. return NumberPercentage::create_percentage(50);
  92. }
  93. // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementCYAttribute
  94. NumberPercentage SVGRadialGradientElement::end_circle_y() const
  95. {
  96. if (m_cy.has_value())
  97. return *m_cy;
  98. if (auto gradient = linked_radial_gradient())
  99. return gradient->end_circle_y();
  100. return NumberPercentage::create_percentage(50);
  101. }
  102. // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementRAttribute
  103. NumberPercentage SVGRadialGradientElement::end_circle_radius() const
  104. {
  105. // Note: A negative value is an error.
  106. if (m_r.has_value() && m_r->value() >= 0)
  107. return *m_r;
  108. if (auto gradient = linked_radial_gradient())
  109. return gradient->end_circle_radius();
  110. return NumberPercentage::create_percentage(50);
  111. }
  112. Optional<Gfx::PaintStyle const&> SVGRadialGradientElement::to_gfx_paint_style(SVGPaintContext const& paint_context) const
  113. {
  114. auto units = gradient_units();
  115. Gfx::FloatPoint start_center;
  116. float start_radius = 0.0f;
  117. Gfx::FloatPoint end_center;
  118. float end_radius = 0.0f;
  119. if (units == GradientUnits::ObjectBoundingBox) {
  120. // If gradientUnits="objectBoundingBox", the user coordinate system for attributes ‘cx’, ‘cy’, ‘r’, ‘fx’, ‘fy’, and ‘fr’
  121. // is established using the bounding box of the element to which the gradient is applied (see Object bounding box units)
  122. // and then applying the transform specified by attribute ‘gradientTransform’. Percentages represent values relative
  123. // to the bounding box for the object.
  124. start_center = Gfx::FloatPoint { start_circle_x().value(), start_circle_y().value() };
  125. start_radius = start_circle_radius().value();
  126. end_center = Gfx::FloatPoint { end_circle_x().value(), end_circle_y().value() };
  127. end_radius = end_circle_radius().value();
  128. } else {
  129. // GradientUnits::UserSpaceOnUse
  130. // If gradientUnits="userSpaceOnUse", ‘cx’, ‘cy’, ‘r’, ‘fx’, ‘fy’, and ‘fr’ represent values in the coordinate system
  131. // that results from taking the current user coordinate system in place at the time when the gradient element is
  132. // referenced (i.e., the user coordinate system for the element referencing the gradient element via a fill or stroke property)
  133. // and then applying the transform specified by attribute ‘gradientTransform’.
  134. // Percentages represent values relative to the current SVG viewport.
  135. // Note: The start/end centers will be in relative units here.
  136. // They will be resolved at paint time using the gradient paint transform.
  137. start_center = Gfx::FloatPoint {
  138. start_circle_x().resolve_relative_to(paint_context.viewport.width()),
  139. start_circle_y().resolve_relative_to(paint_context.viewport.height()),
  140. };
  141. // FIXME: Where in the spec does it say what axis the radius is relative to?
  142. start_radius = start_circle_radius().resolve_relative_to(paint_context.viewport.width());
  143. end_center = Gfx::FloatPoint {
  144. end_circle_x().resolve_relative_to(paint_context.viewport.width()),
  145. end_circle_y().resolve_relative_to(paint_context.viewport.height()),
  146. };
  147. end_radius = end_circle_radius().resolve_relative_to(paint_context.viewport.width());
  148. }
  149. if (!m_paint_style) {
  150. m_paint_style = Gfx::SVGRadialGradientPaintStyle::create(start_center, start_radius, end_center, end_radius)
  151. .release_value_but_fixme_should_propagate_errors();
  152. // FIXME: Update stops in DOM changes:
  153. add_color_stops(*m_paint_style);
  154. } else {
  155. m_paint_style->set_start_center(start_center);
  156. m_paint_style->set_start_radius(start_radius);
  157. m_paint_style->set_end_center(end_center);
  158. m_paint_style->set_end_radius(end_radius);
  159. }
  160. m_paint_style->set_gradient_transform(gradient_paint_transform(paint_context));
  161. m_paint_style->set_spread_method(to_gfx_spread_method(spread_method()));
  162. return *m_paint_style;
  163. }
  164. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::cx() const
  165. {
  166. TODO();
  167. }
  168. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::cy() const
  169. {
  170. TODO();
  171. }
  172. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::fx() const
  173. {
  174. TODO();
  175. }
  176. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::fy() const
  177. {
  178. TODO();
  179. }
  180. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::fr() const
  181. {
  182. TODO();
  183. }
  184. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::r() const
  185. {
  186. TODO();
  187. }
  188. }