SVGRadialGradientElement.cpp 8.5 KB

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