SVGRadialGradientElement.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. JS::ThrowCompletionOr<void> SVGRadialGradientElement::initialize(JS::Realm& realm)
  15. {
  16. MUST_OR_THROW_OOM(Base::initialize(realm));
  17. set_prototype(&Bindings::ensure_web_prototype<Bindings::SVGRadialGradientElementPrototype>(realm, "SVGRadialGradientElement"));
  18. return {};
  19. }
  20. void SVGRadialGradientElement::parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value)
  21. {
  22. SVGGradientElement::parse_attribute(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);
  27. m_paint_style = nullptr;
  28. } else if (name == SVG::AttributeNames::cy) {
  29. m_cy = AttributeParser::parse_number_percentage(value);
  30. m_paint_style = nullptr;
  31. } else if (name == SVG::AttributeNames::fx) {
  32. m_fx = AttributeParser::parse_number_percentage(value);
  33. m_paint_style = nullptr;
  34. } else if (name == SVG::AttributeNames::fy) {
  35. m_fy = AttributeParser::parse_number_percentage(value);
  36. m_paint_style = nullptr;
  37. } else if (name == SVG::AttributeNames::fr) {
  38. m_fr = AttributeParser::parse_number_percentage(value);
  39. m_paint_style = nullptr;
  40. } else if (name == SVG::AttributeNames::r) {
  41. m_r = AttributeParser::parse_number_percentage(value);
  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 href = radial_gradient_xlink_href())
  53. return href->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 href = radial_gradient_xlink_href())
  66. return href->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 href = radial_gradient_xlink_href())
  80. return href->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 href = radial_gradient_xlink_href())
  90. return href->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 href = radial_gradient_xlink_href())
  99. return href->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 href = radial_gradient_xlink_href())
  109. return href->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 {
  125. start_circle_x().value(), start_circle_y().value()
  126. };
  127. start_radius = start_circle_radius().value();
  128. end_center = paint_context.path_bounding_box.location() + Gfx::FloatPoint { end_circle_x().value(), end_circle_y().value() };
  129. end_radius = end_circle_radius().value();
  130. } else {
  131. // GradientUnits::UserSpaceOnUse
  132. // If gradientUnits="userSpaceOnUse", ‘cx’, ‘cy’, ‘r’, ‘fx’, ‘fy’, and ‘fr’ represent values in the coordinate system
  133. // that results from taking the current user coordinate system in place at the time when the gradient element is
  134. // referenced (i.e., the user coordinate system for the element referencing the gradient element via a fill or stroke property)
  135. // and then applying the transform specified by attribute ‘gradientTransform’.
  136. // Percentages represent values relative to the current SVG viewport.
  137. // Note: The start/end centers will be in relative units here.
  138. // They will be resolved at paint time using the gradient paint transform.
  139. start_center = Gfx::FloatPoint {
  140. start_circle_x().resolve_relative_to(paint_context.viewport.width()),
  141. start_circle_y().resolve_relative_to(paint_context.viewport.height()),
  142. };
  143. // FIXME: Where in the spec does it say what axis the radius is relative to?
  144. start_radius = start_circle_radius().resolve_relative_to(paint_context.viewport.width());
  145. end_center = Gfx::FloatPoint {
  146. end_circle_x().resolve_relative_to(paint_context.viewport.width()),
  147. end_circle_y().resolve_relative_to(paint_context.viewport.height()),
  148. };
  149. end_radius = end_circle_radius().resolve_relative_to(paint_context.viewport.width());
  150. }
  151. if (!m_paint_style) {
  152. m_paint_style = Gfx::SVGRadialGradientPaintStyle::create(start_center, start_radius, end_center, end_radius)
  153. .release_value_but_fixme_should_propagate_errors();
  154. // FIXME: Update stops in DOM changes:
  155. add_color_stops(*m_paint_style);
  156. } else {
  157. m_paint_style->set_start_center(start_center);
  158. m_paint_style->set_start_radius(start_radius);
  159. m_paint_style->set_end_center(end_center);
  160. m_paint_style->set_end_radius(end_radius);
  161. }
  162. m_paint_style->set_gradient_transform(gradient_paint_transform(paint_context));
  163. return *m_paint_style;
  164. }
  165. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::cx() const
  166. {
  167. TODO();
  168. }
  169. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::cy() const
  170. {
  171. TODO();
  172. }
  173. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::fx() const
  174. {
  175. TODO();
  176. }
  177. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::fy() const
  178. {
  179. TODO();
  180. }
  181. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::fr() const
  182. {
  183. TODO();
  184. }
  185. JS::NonnullGCPtr<SVGAnimatedLength> SVGRadialGradientElement::r() const
  186. {
  187. TODO();
  188. }
  189. }