| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2023, MacDue <macdue@dueutil.tech> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <LibWeb/Bindings/Intrinsics.h>
 | 
					
						
							| 
									
										
										
										
											2024-04-27 12:09:58 +12:00
										 |  |  | #include <LibWeb/Bindings/SVGGradientElementPrototype.h>
 | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  | #include <LibWeb/DOM/Document.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-13 15:22:46 +03:00
										 |  |  | #include <LibWeb/Painting/PaintStyle.h>
 | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  | #include <LibWeb/SVG/AttributeNames.h>
 | 
					
						
							|  |  |  | #include <LibWeb/SVG/SVGGradientElement.h>
 | 
					
						
							|  |  |  | #include <LibWeb/SVG/SVGGraphicsElement.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Web::SVG { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SVGGradientElement::SVGGradientElement(DOM::Document& document, DOM::QualifiedName qualified_name) | 
					
						
							|  |  |  |     : SVGElement(document, move(qualified_name)) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-09 20:18:41 +01:00
										 |  |  | void SVGGradientElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value) | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-07-09 20:18:41 +01:00
										 |  |  |     SVGElement::attribute_changed(name, old_value, value); | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  |     if (name == AttributeNames::gradientUnits) { | 
					
						
							| 
									
										
										
										
											2023-11-19 18:10:36 +13:00
										 |  |  |         m_gradient_units = AttributeParser::parse_units(value.value_or(String {})); | 
					
						
							| 
									
										
										
										
											2023-08-20 15:45:02 +01:00
										 |  |  |     } else if (name == AttributeNames::spreadMethod) { | 
					
						
							| 
									
										
										
										
											2023-11-19 18:10:36 +13:00
										 |  |  |         m_spread_method = AttributeParser::parse_spread_method(value.value_or(String {})); | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  |     } else if (name == AttributeNames::gradientTransform) { | 
					
						
							| 
									
										
										
										
											2023-11-19 18:10:36 +13:00
										 |  |  |         if (auto transform_list = AttributeParser::parse_transform(value.value_or(String {})); transform_list.has_value()) { | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  |             m_gradient_transform = transform_from_transform_list(*transform_list); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             m_gradient_transform = {}; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | GradientUnits SVGGradientElement::gradient_units() const | 
					
						
							| 
									
										
										
										
											2024-03-10 12:18:09 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     HashTable<SVGGradientElement const*> seen_gradients; | 
					
						
							|  |  |  |     return gradient_units_impl(seen_gradients); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | GradientUnits SVGGradientElement::gradient_units_impl(HashTable<SVGGradientElement const*>& seen_gradients) const | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     if (m_gradient_units.has_value()) | 
					
						
							|  |  |  |         return *m_gradient_units; | 
					
						
							| 
									
										
										
										
											2024-03-10 12:18:09 +01:00
										 |  |  |     if (auto gradient = linked_gradient(seen_gradients)) | 
					
						
							|  |  |  |         return gradient->gradient_units_impl(seen_gradients); | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  |     return GradientUnits::ObjectBoundingBox; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-20 15:45:02 +01:00
										 |  |  | SpreadMethod SVGGradientElement::spread_method() const | 
					
						
							| 
									
										
										
										
											2024-03-10 12:18:09 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     HashTable<SVGGradientElement const*> seen_gradients; | 
					
						
							|  |  |  |     return spread_method_impl(seen_gradients); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SpreadMethod SVGGradientElement::spread_method_impl(HashTable<SVGGradientElement const*>& seen_gradients) const | 
					
						
							| 
									
										
										
										
											2023-08-20 15:45:02 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     if (m_spread_method.has_value()) | 
					
						
							|  |  |  |         return *m_spread_method; | 
					
						
							| 
									
										
										
										
											2024-03-10 12:18:09 +01:00
										 |  |  |     if (auto gradient = linked_gradient(seen_gradients)) | 
					
						
							|  |  |  |         return gradient->spread_method_impl(seen_gradients); | 
					
						
							| 
									
										
										
										
											2023-08-20 15:45:02 +01:00
										 |  |  |     return SpreadMethod::Pad; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  | Optional<Gfx::AffineTransform> SVGGradientElement::gradient_transform() const | 
					
						
							| 
									
										
										
										
											2024-03-10 12:18:09 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     HashTable<SVGGradientElement const*> seen_gradients; | 
					
						
							|  |  |  |     return gradient_transform_impl(seen_gradients); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Optional<Gfx::AffineTransform> SVGGradientElement::gradient_transform_impl(HashTable<SVGGradientElement const*>& seen_gradients) const | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     if (m_gradient_transform.has_value()) | 
					
						
							|  |  |  |         return m_gradient_transform; | 
					
						
							| 
									
										
										
										
											2024-03-10 12:18:09 +01:00
										 |  |  |     if (auto gradient = linked_gradient(seen_gradients)) | 
					
						
							|  |  |  |         return gradient->gradient_transform_impl(seen_gradients); | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-02 23:05:47 +01:00
										 |  |  | // The gradient transform, appropriately scaled and combined with the paint transform.
 | 
					
						
							|  |  |  | Gfx::AffineTransform SVGGradientElement::gradient_paint_transform(SVGPaintContext const& paint_context) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto transform = gradient_transform().value_or(Gfx::AffineTransform {}); | 
					
						
							|  |  |  |     if (gradient_units() == GradientUnits::ObjectBoundingBox) { | 
					
						
							|  |  |  |         // Adjust transform to take place in the coordinate system defined by the bounding box:
 | 
					
						
							|  |  |  |         return Gfx::AffineTransform { paint_context.transform } | 
					
						
							|  |  |  |             .translate(paint_context.path_bounding_box.location()) | 
					
						
							|  |  |  |             .scale(paint_context.path_bounding_box.width(), paint_context.path_bounding_box.height()) | 
					
						
							|  |  |  |             .multiply(transform); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return Gfx::AffineTransform { paint_context.transform }.multiply(transform); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-13 15:22:46 +03:00
										 |  |  | void SVGGradientElement::add_color_stops(Painting::SVGGradientPaintStyle& paint_style) const | 
					
						
							| 
									
										
										
										
											2023-05-02 23:05:47 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     for_each_color_stop([&](auto& stop) { | 
					
						
							|  |  |  |         // https://svgwg.org/svg2-draft/pservers.html#StopNotes
 | 
					
						
							|  |  |  |         // Gradient offset values less than 0 (or less than 0%) are rounded up to 0%.
 | 
					
						
							|  |  |  |         // Gradient offset values greater than 1 (or greater than 100%) are rounded down to 100%.
 | 
					
						
							|  |  |  |         float stop_offset = AK::clamp(stop.stop_offset().value(), 0.0f, 1.0f); | 
					
						
							|  |  |  |         // FIXME: Each gradient offset value is required to be equal to or greater than the previous gradient
 | 
					
						
							|  |  |  |         // stop's offset value. If a given gradient stop's offset value is not equal to or greater than all
 | 
					
						
							|  |  |  |         // previous offset values, then the offset value is adjusted to be equal to the largest of all previous
 | 
					
						
							|  |  |  |         // offset values.
 | 
					
						
							| 
									
										
										
										
											2024-06-13 15:22:46 +03:00
										 |  |  |         paint_style.add_color_stop(stop_offset, stop.stop_color().with_opacity(stop.stop_opacity())); | 
					
						
							| 
									
										
										
										
											2023-05-02 23:05:47 +01:00
										 |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-10 12:18:09 +01:00
										 |  |  | JS::GCPtr<SVGGradientElement const> SVGGradientElement::linked_gradient(HashTable<SVGGradientElement const*>& seen_gradients) const | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     // FIXME: This entire function is an ad-hoc hack!
 | 
					
						
							|  |  |  |     // It can only resolve #<ids> in the same document.
 | 
					
						
							| 
									
										
										
										
											2023-07-30 13:50:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-16 19:04:45 +01:00
										 |  |  |     auto link = has_attribute(AttributeNames::href) ? get_attribute(AttributeNames::href) : get_attribute("xlink:href"_fly_string); | 
					
						
							|  |  |  |     if (auto href = link; href.has_value() && !link->is_empty()) { | 
					
						
							|  |  |  |         auto url = document().parse_url(*href); | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  |         auto id = url.fragment(); | 
					
						
							| 
									
										
										
										
											2023-08-12 16:52:42 +12:00
										 |  |  |         if (!id.has_value() || id->is_empty()) | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  |             return {}; | 
					
						
							| 
									
										
										
										
											2023-09-11 18:23:48 +12:00
										 |  |  |         auto element = document().get_element_by_id(id.value()); | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  |         if (!element) | 
					
						
							|  |  |  |             return {}; | 
					
						
							| 
									
										
										
										
											2024-03-10 12:18:09 +01:00
										 |  |  |         if (element == this) | 
					
						
							|  |  |  |             return {}; | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  |         if (!is<SVGGradientElement>(*element)) | 
					
						
							|  |  |  |             return {}; | 
					
						
							| 
									
										
										
										
											2024-03-10 12:18:09 +01:00
										 |  |  |         if (seen_gradients.set(&verify_cast<SVGGradientElement>(*element)) != AK::HashSetResult::InsertedNewEntry) | 
					
						
							|  |  |  |             return {}; | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  |         return &verify_cast<SVGGradientElement>(*element); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-07 08:41:28 +02:00
										 |  |  | void SVGGradientElement::initialize(JS::Realm& realm) | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-08-07 08:41:28 +02:00
										 |  |  |     Base::initialize(realm); | 
					
						
							| 
									
										
										
										
											2024-03-16 13:13:08 +01:00
										 |  |  |     WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGGradientElement); | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-16 13:59:10 +01:00
										 |  |  | void SVGGradientElement::visit_edges(Cell::Visitor& visitor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Base::visit_edges(visitor); | 
					
						
							|  |  |  |     SVGURIReferenceMixin::visit_edges(visitor); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-21 18:51:00 +01:00
										 |  |  | } |