| 
									
										
										
										
											2024-05-25 23:06:47 +01:00
										 |  |  |  | /*
 | 
					
						
							|  |  |  |  |  * Copyright (c) 2024, MacDue <macdue@dueutil.tech> | 
					
						
							|  |  |  |  |  * | 
					
						
							|  |  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #include "BasicShapeStyleValue.h"
 | 
					
						
							| 
									
										
										
										
											2024-08-20 17:27:08 +02:00
										 |  |  |  | #include <LibGfx/Path.h>
 | 
					
						
							| 
									
										
										
										
											2024-05-25 23:06:47 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | namespace Web::CSS { | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-27 11:58:52 +11:00
										 |  |  |  | static Gfx::Path path_from_resolved_rect(float top, float right, float bottom, float left) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     Gfx::Path path; | 
					
						
							|  |  |  |  |     path.move_to(Gfx::FloatPoint { left, top }); | 
					
						
							|  |  |  |  |     path.line_to(Gfx::FloatPoint { right, top }); | 
					
						
							|  |  |  |  |     path.line_to(Gfx::FloatPoint { right, bottom }); | 
					
						
							|  |  |  |  |     path.line_to(Gfx::FloatPoint { left, bottom }); | 
					
						
							|  |  |  |  |     path.close(); | 
					
						
							|  |  |  |  |     return path; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Gfx::Path Inset::to_path(CSSPixelRect reference_box, Layout::Node const& node) const | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     // FIXME: A pair of insets in either dimension that add up to more than the used dimension
 | 
					
						
							|  |  |  |  |     // (such as left and right insets of 75% apiece) use the CSS Backgrounds 3 § 4.5 Overlapping Curves rules
 | 
					
						
							|  |  |  |  |     // to proportionally reduce the inset effect to 100%.
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto top = inset_box.top().to_px(node, reference_box.height()).to_float(); | 
					
						
							|  |  |  |  |     auto right = reference_box.width().to_float() - inset_box.right().to_px(node, reference_box.width()).to_float(); | 
					
						
							|  |  |  |  |     auto bottom = reference_box.height().to_float() - inset_box.bottom().to_px(node, reference_box.height()).to_float(); | 
					
						
							|  |  |  |  |     auto left = inset_box.left().to_px(node, reference_box.width()).to_float(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return path_from_resolved_rect(top, right, bottom, left); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-29 22:44:14 +11:00
										 |  |  |  | String Inset::to_string(CSSStyleValue::SerializationMode) const | 
					
						
							| 
									
										
										
										
											2024-10-27 11:58:52 +11:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     return MUST(String::formatted("inset({} {} {} {})", inset_box.top(), inset_box.right(), inset_box.bottom(), inset_box.left())); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Gfx::Path Xywh::to_path(CSSPixelRect reference_box, Layout::Node const& node) const | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     auto top = y.to_px(node, reference_box.height()).to_float(); | 
					
						
							|  |  |  |  |     auto bottom = top + max(0.0f, height.to_px(node, reference_box.height()).to_float()); | 
					
						
							|  |  |  |  |     auto left = x.to_px(node, reference_box.width()).to_float(); | 
					
						
							|  |  |  |  |     auto right = left + max(0.0f, width.to_px(node, reference_box.width()).to_float()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return path_from_resolved_rect(top, right, bottom, left); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-29 22:44:14 +11:00
										 |  |  |  | String Xywh::to_string(CSSStyleValue::SerializationMode) const | 
					
						
							| 
									
										
										
										
											2024-10-27 11:58:52 +11:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     return MUST(String::formatted("xywh({} {} {} {})", x, y, width, height)); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Gfx::Path Rect::to_path(CSSPixelRect reference_box, Layout::Node const& node) const | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     // An auto value makes the edge of the box coincide with the corresponding edge of the reference box:
 | 
					
						
							|  |  |  |  |     // it’s equivalent to 0% as the first (top) or fourth (left) value, and equivalent to 100% as the second (right) or third (bottom) value.
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto top = box.top().is_auto() ? 0 : box.top().to_px(node, reference_box.height()).to_float(); | 
					
						
							|  |  |  |  |     auto right = box.right().is_auto() ? reference_box.width().to_float() : box.right().to_px(node, reference_box.width()).to_float(); | 
					
						
							|  |  |  |  |     auto bottom = box.bottom().is_auto() ? reference_box.height().to_float() : box.bottom().to_px(node, reference_box.height()).to_float(); | 
					
						
							|  |  |  |  |     auto left = box.left().is_auto() ? 0 : box.left().to_px(node, reference_box.width()).to_float(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // The second (right) and third (bottom) values are floored by the fourth (left) and second (top) values, respectively.
 | 
					
						
							|  |  |  |  |     return path_from_resolved_rect(top, max(right, left), max(bottom, top), left); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-29 22:44:14 +11:00
										 |  |  |  | String Rect::to_string(CSSStyleValue::SerializationMode) const | 
					
						
							| 
									
										
										
										
											2024-10-27 11:58:52 +11:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     return MUST(String::formatted("rect({} {} {} {})", box.top(), box.right(), box.bottom(), box.left())); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static String radius_to_string(ShapeRadius radius) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     return radius.visit( | 
					
						
							|  |  |  |  |         [](LengthPercentage const& length_percentage) { return length_percentage.to_string(); }, | 
					
						
							|  |  |  |  |         [](FitSide const& side) { | 
					
						
							|  |  |  |  |             switch (side) { | 
					
						
							|  |  |  |  |             case FitSide::ClosestSide: | 
					
						
							|  |  |  |  |                 return "closest-side"_string; | 
					
						
							|  |  |  |  |             case FitSide::FarthestSide: | 
					
						
							|  |  |  |  |                 return "farthest-side"_string; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |  |         }); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Gfx::Path Circle::to_path(CSSPixelRect reference_box, Layout::Node const& node) const | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     // Translating the reference box because PositionStyleValues are resolved to an absolute position.
 | 
					
						
							|  |  |  |  |     auto center = position->resolved(node, reference_box.translated(-reference_box.x(), -reference_box.y())); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     float radius_px = radius.visit( | 
					
						
							|  |  |  |  |         [&](LengthPercentage const& length_percentage) { | 
					
						
							|  |  |  |  |             auto radius_ref = sqrt(pow(reference_box.width().to_float(), 2) + pow(reference_box.height().to_float(), 2)) / AK::Sqrt2<float>; | 
					
						
							|  |  |  |  |             return max(0.0f, length_percentage.to_px(node, CSSPixels(radius_ref)).to_float()); | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |         [&](FitSide const& side) { | 
					
						
							|  |  |  |  |             switch (side) { | 
					
						
							|  |  |  |  |             case FitSide::ClosestSide: | 
					
						
							|  |  |  |  |                 float closest; | 
					
						
							|  |  |  |  |                 closest = min(abs(center.x()), abs(center.y())).to_float(); | 
					
						
							|  |  |  |  |                 closest = min(closest, abs(reference_box.width() - center.x()).to_float()); | 
					
						
							|  |  |  |  |                 closest = min(closest, abs(reference_box.height() - center.y()).to_float()); | 
					
						
							|  |  |  |  |                 return closest; | 
					
						
							|  |  |  |  |             case FitSide::FarthestSide: | 
					
						
							|  |  |  |  |                 float farthest; | 
					
						
							|  |  |  |  |                 farthest = max(abs(center.x()), abs(center.y())).to_float(); | 
					
						
							|  |  |  |  |                 farthest = max(farthest, abs(reference_box.width() - center.x()).to_float()); | 
					
						
							|  |  |  |  |                 farthest = max(farthest, abs(reference_box.height() - center.y()).to_float()); | 
					
						
							|  |  |  |  |                 return farthest; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |  |         }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     Gfx::Path path; | 
					
						
							|  |  |  |  |     path.move_to(Gfx::FloatPoint { center.x().to_float(), center.y().to_float() + radius_px }); | 
					
						
							|  |  |  |  |     path.arc_to(Gfx::FloatPoint { center.x().to_float(), center.y().to_float() - radius_px }, radius_px, true, true); | 
					
						
							|  |  |  |  |     path.arc_to(Gfx::FloatPoint { center.x().to_float(), center.y().to_float() + radius_px }, radius_px, true, true); | 
					
						
							|  |  |  |  |     return path; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-29 22:44:14 +11:00
										 |  |  |  | String Circle::to_string(CSSStyleValue::SerializationMode mode) const | 
					
						
							| 
									
										
										
										
											2024-10-27 11:58:52 +11:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-29 22:44:14 +11:00
										 |  |  |  |     return MUST(String::formatted("circle({} at {})", radius_to_string(radius), position->to_string(mode))); | 
					
						
							| 
									
										
										
										
											2024-10-27 11:58:52 +11:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Gfx::Path Ellipse::to_path(CSSPixelRect reference_box, Layout::Node const& node) const | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     // Translating the reference box because PositionStyleValues are resolved to an absolute position.
 | 
					
						
							|  |  |  |  |     auto center = position->resolved(node, reference_box.translated(-reference_box.x(), -reference_box.y())); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     float radius_x_px = radius_x.visit( | 
					
						
							|  |  |  |  |         [&](LengthPercentage const& length_percentage) { | 
					
						
							|  |  |  |  |             return max(0.0f, length_percentage.to_px(node, reference_box.width()).to_float()); | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |         [&](FitSide const& side) { | 
					
						
							|  |  |  |  |             switch (side) { | 
					
						
							|  |  |  |  |             case FitSide::ClosestSide: | 
					
						
							|  |  |  |  |                 return min(abs(center.x()), abs(reference_box.width() - center.x())).to_float(); | 
					
						
							|  |  |  |  |             case FitSide::FarthestSide: | 
					
						
							|  |  |  |  |                 return max(abs(center.x()), abs(reference_box.width() - center.x())).to_float(); | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |  |         }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     float radius_y_px = radius_y.visit( | 
					
						
							|  |  |  |  |         [&](LengthPercentage const& length_percentage) { | 
					
						
							|  |  |  |  |             return max(0.0f, length_percentage.to_px(node, reference_box.height()).to_float()); | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |         [&](FitSide const& side) { | 
					
						
							|  |  |  |  |             switch (side) { | 
					
						
							|  |  |  |  |             case FitSide::ClosestSide: | 
					
						
							|  |  |  |  |                 return min(abs(center.y()), abs(reference_box.height() - center.y())).to_float(); | 
					
						
							|  |  |  |  |             case FitSide::FarthestSide: | 
					
						
							|  |  |  |  |                 return max(abs(center.y()), abs(reference_box.height() - center.y())).to_float(); | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |  |         }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     Gfx::Path path; | 
					
						
							|  |  |  |  |     path.move_to(Gfx::FloatPoint { center.x().to_float(), center.y().to_float() + radius_y_px }); | 
					
						
							|  |  |  |  |     path.elliptical_arc_to(Gfx::FloatPoint { center.x().to_float(), center.y().to_float() - radius_y_px }, Gfx::FloatSize { radius_x_px, radius_y_px }, 0, true, true); | 
					
						
							|  |  |  |  |     path.elliptical_arc_to(Gfx::FloatPoint { center.x().to_float(), center.y().to_float() + radius_y_px }, Gfx::FloatSize { radius_x_px, radius_y_px }, 0, true, true); | 
					
						
							|  |  |  |  |     return path; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-29 22:44:14 +11:00
										 |  |  |  | String Ellipse::to_string(CSSStyleValue::SerializationMode mode) const | 
					
						
							| 
									
										
										
										
											2024-10-27 11:58:52 +11:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-29 22:44:14 +11:00
										 |  |  |  |     return MUST(String::formatted("ellipse({} {} at {})", radius_to_string(radius_x), radius_to_string(radius_y), position->to_string(mode))); | 
					
						
							| 
									
										
										
										
											2024-10-27 11:58:52 +11:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-20 17:27:08 +02:00
										 |  |  |  | Gfx::Path Polygon::to_path(CSSPixelRect reference_box, Layout::Node const& node) const | 
					
						
							| 
									
										
										
										
											2024-05-25 23:06:47 +01:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-08-20 17:27:08 +02:00
										 |  |  |  |     Gfx::Path path; | 
					
						
							| 
									
										
										
										
											2024-10-28 13:51:23 +11:00
										 |  |  |  |     path.set_fill_type(fill_rule); | 
					
						
							| 
									
										
										
										
											2024-05-25 23:06:47 +01:00
										 |  |  |  |     bool first = true; | 
					
						
							|  |  |  |  |     for (auto const& point : points) { | 
					
						
							|  |  |  |  |         Gfx::FloatPoint resolved_point { | 
					
						
							| 
									
										
										
										
											2024-10-27 11:58:52 +11:00
										 |  |  |  |             point.x.to_px(node, reference_box.width()).to_float(), | 
					
						
							|  |  |  |  |             point.y.to_px(node, reference_box.height()).to_float() | 
					
						
							| 
									
										
										
										
											2024-05-25 23:06:47 +01:00
										 |  |  |  |         }; | 
					
						
							|  |  |  |  |         if (first) | 
					
						
							|  |  |  |  |             path.move_to(resolved_point); | 
					
						
							|  |  |  |  |         else | 
					
						
							|  |  |  |  |             path.line_to(resolved_point); | 
					
						
							|  |  |  |  |         first = false; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     path.close(); | 
					
						
							|  |  |  |  |     return path; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-29 22:44:14 +11:00
										 |  |  |  | String Polygon::to_string(CSSStyleValue::SerializationMode) const | 
					
						
							| 
									
										
										
										
											2024-05-25 23:06:47 +01:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     StringBuilder builder; | 
					
						
							|  |  |  |  |     builder.append("polygon("sv); | 
					
						
							| 
									
										
										
										
											2024-10-28 13:51:23 +11:00
										 |  |  |  |     switch (fill_rule) { | 
					
						
							|  |  |  |  |     case Gfx::WindingRule::Nonzero: | 
					
						
							|  |  |  |  |         builder.append("nonzero"sv); | 
					
						
							|  |  |  |  |         break; | 
					
						
							|  |  |  |  |     case Gfx::WindingRule::EvenOdd: | 
					
						
							|  |  |  |  |         builder.append("evenodd"sv); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-05-25 23:06:47 +01:00
										 |  |  |  |     for (auto const& point : points) { | 
					
						
							| 
									
										
										
										
											2024-10-28 13:51:23 +11:00
										 |  |  |  |         builder.appendff(", {} {}", point.x, point.y); | 
					
						
							| 
									
										
										
										
											2024-05-25 23:06:47 +01:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |     builder.append(')'); | 
					
						
							|  |  |  |  |     return MUST(builder.to_string()); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | BasicShapeStyleValue::~BasicShapeStyleValue() = default; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-20 17:27:08 +02:00
										 |  |  |  | Gfx::Path BasicShapeStyleValue::to_path(CSSPixelRect reference_box, Layout::Node const& node) const | 
					
						
							| 
									
										
										
										
											2024-05-25 23:06:47 +01:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     return m_basic_shape.visit([&](auto const& shape) { | 
					
						
							|  |  |  |  |         return shape.to_path(reference_box, node); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-29 22:44:14 +11:00
										 |  |  |  | String BasicShapeStyleValue::to_string(SerializationMode mode) const | 
					
						
							| 
									
										
										
										
											2024-05-25 23:06:47 +01:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-29 22:44:14 +11:00
										 |  |  |  |     return m_basic_shape.visit([mode](auto const& shape) { | 
					
						
							|  |  |  |  |         return shape.to_string(mode); | 
					
						
							| 
									
										
										
										
											2024-05-25 23:06:47 +01:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | } |