mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 05:32:44 +00:00 
			
		
		
		
	LibWeb: Support CSS property background-clip: text
				
					
				
			From https://drafts.csswg.org/css-backgrounds-4/#background-clip "The background is painted within (clipped to) the intersection of the border box and the geometry of the text in the element and its in-flow and floated descendants" This change implements it in the following way: 1. Traverse the descendants of the element, collecting the Gfx::Path of glyphs into a vector. 2. The vector of collected paths is saved in the background painting command. 3. The painting commands executor uses the list of glyphs to paint a mask for background clipping. Co-authored-by: Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
This commit is contained in:
		
							parent
							
								
									4a3680cafc
								
							
						
					
					
						commit
						9165faca5e
					
				
					 27 changed files with 353 additions and 105 deletions
				
			
		
							
								
								
									
										84
									
								
								Tests/LibWeb/Ref/css-background-clip-text.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								Tests/LibWeb/Ref/css-background-clip-text.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,84 @@ | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | <head> | ||||||
|  |   <meta charset="UTF-8"> | ||||||
|  |   <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|  |   <link rel="match" href="reference/css-background-clip-text-ref.html" /> | ||||||
|  |   <title>Document</title> | ||||||
|  | 
 | ||||||
|  |   <style> | ||||||
|  |     p, .container { | ||||||
|  |       border: 0.8em darkviolet; | ||||||
|  |       border-style: dotted double; | ||||||
|  |       margin: 1em 0; | ||||||
|  |       padding: 1.4em; | ||||||
|  |       font: 900 1.2em sans-serif; | ||||||
|  |       text-decoration: underline; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .linear-gradient { | ||||||
|  |       background: linear-gradient(60deg, red, yellow, red, yellow, red); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .radial-gradient { | ||||||
|  |       background: radial-gradient(circle, red, yellow, red, yellow, red); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .conic-gradient { | ||||||
|  |       background: conic-gradient(red, yellow, red, yellow, red); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .image-background { | ||||||
|  |       background: url('./assets/car.png'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .border-box { | ||||||
|  |       background-clip: border-box; | ||||||
|  |     } | ||||||
|  |     .padding-box { | ||||||
|  |       background-clip: padding-box; | ||||||
|  |     } | ||||||
|  |     .content-box { | ||||||
|  |       background-clip: content-box; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .text { | ||||||
|  |       background-clip: text; | ||||||
|  |       color: rgb(0 0 0 / 20%); | ||||||
|  |     } | ||||||
|  |     .new-background { | ||||||
|  |       background: rgb(255 255 0 / 30%); | ||||||
|  |     } | ||||||
|  |   </style> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  |   <!-- Hack to make the test runner wait for the image to load --> | ||||||
|  |   <img src="./assets/car.png" /> | ||||||
|  |    | ||||||
|  |   <p class="border-box linear-gradient">The background extends behind the border.</p> | ||||||
|  |   <p class="padding-box radial-gradient"> | ||||||
|  |     The background extends to the inside edge of the border. | ||||||
|  |   </p> | ||||||
|  |   <p class="content-box conic-gradient"> | ||||||
|  |     The background extends only to the edge of the content box. | ||||||
|  |   </p> | ||||||
|  |   <div class="text container linear-gradient"> | ||||||
|  |     The background is clipped to the foreground text. | ||||||
|  |     <span>Some other text in a sub-element</span> | ||||||
|  |   </div> | ||||||
|  |   <div class="text container radial-gradient"> | ||||||
|  |     The background is clipped to the foreground text. | ||||||
|  |     <span>Some other text in a sub-element</span> | ||||||
|  |   </div> | ||||||
|  |   <div class="text container conic-gradient"> | ||||||
|  |     The background is clipped to the foreground text. | ||||||
|  |     <span>Some other text in a sub-element</span> | ||||||
|  |   </div> | ||||||
|  |   <div class="text container image-background"> | ||||||
|  |     Testing text. | ||||||
|  |     <div> | ||||||
|  |       <div class="new-background" style="color: rgb(0 0 0 / 20%);">The is nested text that should still be clipped to the background</div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										15
									
								
								Tests/LibWeb/Ref/reference/css-background-clip-text-ref.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Tests/LibWeb/Ref/reference/css-background-clip-text-ref.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | <style> | ||||||
|  |   * { | ||||||
|  |     margin: 0; | ||||||
|  |   } | ||||||
|  |   body { | ||||||
|  |     background-color: white; | ||||||
|  |   } | ||||||
|  | </style> | ||||||
|  | <!-- To rebase: | ||||||
|  |   1. Open background-clip-text.html in Ladybird | ||||||
|  |   2. Resize the window just above the width of the canvas | ||||||
|  |   3. Right click > "Take Full Screenshot" | ||||||
|  |   4. Update the image below: | ||||||
|  | --> | ||||||
|  | <img src="./images/css-background-clip-text.png"> | ||||||
							
								
								
									
										
											BIN
										
									
								
								Tests/LibWeb/Ref/reference/images/css-background-clip-text.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/LibWeb/Ref/reference/images/css-background-clip-text.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 203 KiB | 
|  | @ -36,7 +36,7 @@ public: | ||||||
|     virtual void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const {}; |     virtual void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const {}; | ||||||
| 
 | 
 | ||||||
|     virtual bool is_paintable() const = 0; |     virtual bool is_paintable() const = 0; | ||||||
|     virtual void paint(PaintContext& context, DevicePixelRect const& dest_rect, ImageRendering) const = 0; |     virtual void paint(PaintContext& context, DevicePixelRect const& dest_rect, ImageRendering, Vector<Gfx::Path> const& clip_paths = {}) const = 0; | ||||||
| 
 | 
 | ||||||
|     virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const { return {}; } |     virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const { return {}; } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -42,10 +42,10 @@ void ConicGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModelM | ||||||
|     m_resolved->position = m_properties.position->resolved(node, CSSPixelRect { { 0, 0 }, size }); |     m_resolved->position = m_properties.position->resolved(node, CSSPixelRect { { 0, 0 }, size }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ConicGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const | void ConicGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering, Vector<Gfx::Path> const& clip_paths) const | ||||||
| { | { | ||||||
|     VERIFY(m_resolved.has_value()); |     VERIFY(m_resolved.has_value()); | ||||||
|     Painting::paint_conic_gradient(context, dest_rect, m_resolved->data, context.rounded_device_point(m_resolved->position)); |     Painting::paint_conic_gradient(context, dest_rect, m_resolved->data, context.rounded_device_point(m_resolved->position), clip_paths); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ConicGradientStyleValue::equals(StyleValue const& other) const | bool ConicGradientStyleValue::equals(StyleValue const& other) const | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual String to_string() const override; |     virtual String to_string() const override; | ||||||
| 
 | 
 | ||||||
|     void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering) const override; |     void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering, Vector<Gfx::Path> const& clip_paths = {}) const override; | ||||||
| 
 | 
 | ||||||
|     virtual bool equals(StyleValue const& other) const override; |     virtual bool equals(StyleValue const& other) const override; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -131,11 +131,11 @@ Optional<CSSPixelFraction> ImageStyleValue::natural_aspect_ratio() const | ||||||
|     return {}; |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const | void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, Vector<Gfx::Path> const& clip_paths) const | ||||||
| { | { | ||||||
|     if (auto const* b = bitmap(m_current_frame_index, dest_rect.size().to_type<int>()); b != nullptr) { |     if (auto const* b = bitmap(m_current_frame_index, dest_rect.size().to_type<int>()); b != nullptr) { | ||||||
|         auto scaling_mode = to_gfx_scaling_mode(image_rendering, b->rect(), dest_rect.to_type<int>()); |         auto scaling_mode = to_gfx_scaling_mode(image_rendering, b->rect(), dest_rect.to_type<int>()); | ||||||
|         context.recording_painter().draw_scaled_immutable_bitmap(dest_rect.to_type<int>(), *b, b->rect(), scaling_mode); |         context.recording_painter().draw_scaled_immutable_bitmap(dest_rect.to_type<int>(), *b, b->rect(), scaling_mode, clip_paths); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -45,7 +45,7 @@ public: | ||||||
|     Optional<CSSPixelFraction> natural_aspect_ratio() const override; |     Optional<CSSPixelFraction> natural_aspect_ratio() const override; | ||||||
| 
 | 
 | ||||||
|     virtual bool is_paintable() const override; |     virtual bool is_paintable() const override; | ||||||
|     void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const override; |     void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, Vector<Gfx::Path> const& clip_paths = {}) const override; | ||||||
| 
 | 
 | ||||||
|     virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const override; |     virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const override; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -109,10 +109,10 @@ void LinearGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModel | ||||||
|     m_resolved = ResolvedData { Painting::resolve_linear_gradient_data(node, size, *this), size }; |     m_resolved = ResolvedData { Painting::resolve_linear_gradient_data(node, size, *this), size }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void LinearGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const | void LinearGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering, Vector<Gfx::Path> const& clip_paths) const | ||||||
| { | { | ||||||
|     VERIFY(m_resolved.has_value()); |     VERIFY(m_resolved.has_value()); | ||||||
|     Painting::paint_linear_gradient(context, dest_rect, m_resolved->data); |     Painting::paint_linear_gradient(context, dest_rect, m_resolved->data, clip_paths); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -60,7 +60,7 @@ public: | ||||||
|     void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const override; |     void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const override; | ||||||
| 
 | 
 | ||||||
|     bool is_paintable() const override { return true; } |     bool is_paintable() const override { return true; } | ||||||
|     void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const override; |     void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, Vector<Gfx::Path> const& clip_paths = {}) const override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     LinearGradientStyleValue(GradientDirection direction, Vector<LinearColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating) |     LinearGradientStyleValue(GradientDirection direction, Vector<LinearColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating) | ||||||
|  |  | ||||||
|  | @ -207,12 +207,13 @@ bool RadialGradientStyleValue::equals(StyleValue const& other) const | ||||||
|     return m_properties == other_gradient.m_properties; |     return m_properties == other_gradient.m_properties; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RadialGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const | void RadialGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering, Vector<Gfx::Path> const& clip_paths) const | ||||||
| { | { | ||||||
|     VERIFY(m_resolved.has_value()); |     VERIFY(m_resolved.has_value()); | ||||||
|     Painting::paint_radial_gradient(context, dest_rect, m_resolved->data, |     Painting::paint_radial_gradient(context, dest_rect, m_resolved->data, | ||||||
|         context.rounded_device_point(m_resolved->center), |         context.rounded_device_point(m_resolved->center), | ||||||
|         context.rounded_device_size(m_resolved->gradient_size)); |         context.rounded_device_size(m_resolved->gradient_size), | ||||||
|  |         clip_paths); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -51,7 +51,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual String to_string() const override; |     virtual String to_string() const override; | ||||||
| 
 | 
 | ||||||
|     void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering) const override; |     void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering, Vector<Gfx::Path> const& clip_paths = {}) const override; | ||||||
| 
 | 
 | ||||||
|     virtual bool equals(StyleValue const& other) const override; |     virtual bool equals(StyleValue const& other) const override; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -61,7 +61,7 @@ static CSSPixelSize run_default_sizing_algorithm( | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // https://www.w3.org/TR/css-backgrounds-3/#backgrounds
 | // https://www.w3.org/TR/css-backgrounds-3/#backgrounds
 | ||||||
| void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMetrics const& layout_node, CSSPixelRect const& border_rect, Color background_color, CSS::ImageRendering image_rendering, Vector<CSS::BackgroundLayerData> const* background_layers, BorderRadiiData const& border_radii) | void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMetrics const& layout_node, CSSPixelRect const& border_rect, Color background_color, CSS::ImageRendering image_rendering, Vector<CSS::BackgroundLayerData> const* background_layers, BorderRadiiData const& border_radii, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     auto& painter = context.recording_painter(); |     auto& painter = context.recording_painter(); | ||||||
| 
 | 
 | ||||||
|  | @ -118,13 +118,14 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     context.recording_painter().fill_rect_with_rounded_corners( |     painter.fill_rect_with_rounded_corners( | ||||||
|         context.rounded_device_rect(color_box.rect).to_type<int>(), |         context.rounded_device_rect(color_box.rect).to_type<int>(), | ||||||
|         background_color, |         background_color, | ||||||
|         color_box.radii.top_left.as_corner(context), |         color_box.radii.top_left.as_corner(context), | ||||||
|         color_box.radii.top_right.as_corner(context), |         color_box.radii.top_right.as_corner(context), | ||||||
|         color_box.radii.bottom_right.as_corner(context), |         color_box.radii.bottom_right.as_corner(context), | ||||||
|         color_box.radii.bottom_left.as_corner(context)); |         color_box.radii.bottom_left.as_corner(context), | ||||||
|  |         clip_paths); | ||||||
| 
 | 
 | ||||||
|     if (!has_paintable_layers) |     if (!has_paintable_layers) | ||||||
|         return; |         return; | ||||||
|  | @ -388,10 +389,10 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet | ||||||
|                     fill_rect = fill_rect->united(image_device_rect); |                     fill_rect = fill_rect->united(image_device_rect); | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|             painter.fill_rect(fill_rect->to_type<int>(), color.value()); |             painter.fill_rect(fill_rect->to_type<int>(), color.value(), clip_paths); | ||||||
|         } else { |         } else { | ||||||
|             for_each_image_device_rect([&](auto const& image_device_rect) { |             for_each_image_device_rect([&](auto const& image_device_rect) { | ||||||
|                 image.paint(context, image_device_rect, image_rendering); |                 image.paint(context, image_device_rect, image_rendering, clip_paths); | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -12,6 +12,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace Web::Painting { | namespace Web::Painting { | ||||||
| 
 | 
 | ||||||
| void paint_background(PaintContext&, Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelRect const&, Color background_color, CSS::ImageRendering, Vector<CSS::BackgroundLayerData> const*, BorderRadiiData const&); | void paint_background(PaintContext&, Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelRect const&, Color background_color, CSS::ImageRendering, Vector<CSS::BackgroundLayerData> const*, BorderRadiiData const&, Vector<Gfx::Path> const& clip_paths = {}); | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -65,6 +65,7 @@ struct DrawText { | ||||||
| struct FillRect { | struct FillRect { | ||||||
|     Gfx::IntRect rect; |     Gfx::IntRect rect; | ||||||
|     Color color; |     Color color; | ||||||
|  |     Vector<Gfx::Path> clip_paths; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } |     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } | ||||||
|  | @ -85,6 +86,7 @@ struct DrawScaledImmutableBitmap { | ||||||
|     NonnullRefPtr<Gfx::ImmutableBitmap> bitmap; |     NonnullRefPtr<Gfx::ImmutableBitmap> bitmap; | ||||||
|     Gfx::IntRect src_rect; |     Gfx::IntRect src_rect; | ||||||
|     Gfx::Painter::ScalingMode scaling_mode; |     Gfx::Painter::ScalingMode scaling_mode; | ||||||
|  |     Vector<Gfx::Path> clip_paths; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; } | ||||||
|     void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } |     void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } | ||||||
|  | @ -128,6 +130,7 @@ struct PopStackingContext { }; | ||||||
| struct PaintLinearGradient { | struct PaintLinearGradient { | ||||||
|     Gfx::IntRect gradient_rect; |     Gfx::IntRect gradient_rect; | ||||||
|     LinearGradientData linear_gradient_data; |     LinearGradientData linear_gradient_data; | ||||||
|  |     Vector<Gfx::Path> clip_paths; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; } | ||||||
| 
 | 
 | ||||||
|  | @ -170,6 +173,7 @@ struct FillRectWithRoundedCorners { | ||||||
|     Gfx::AntiAliasingPainter::CornerRadius top_right_radius; |     Gfx::AntiAliasingPainter::CornerRadius top_right_radius; | ||||||
|     Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius; |     Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius; | ||||||
|     Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius; |     Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius; | ||||||
|  |     Vector<Gfx::Path> clip_paths; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } |     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } | ||||||
|  | @ -334,6 +338,7 @@ struct PaintRadialGradient { | ||||||
|     RadialGradientData radial_gradient_data; |     RadialGradientData radial_gradient_data; | ||||||
|     Gfx::IntPoint center; |     Gfx::IntPoint center; | ||||||
|     Gfx::IntSize size; |     Gfx::IntSize size; | ||||||
|  |     Vector<Gfx::Path> clip_paths; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
| 
 | 
 | ||||||
|  | @ -344,6 +349,7 @@ struct PaintConicGradient { | ||||||
|     Gfx::IntRect rect; |     Gfx::IntRect rect; | ||||||
|     ConicGradientData conic_gradient_data; |     ConicGradientData conic_gradient_data; | ||||||
|     Gfx::IntPoint position; |     Gfx::IntPoint position; | ||||||
|  |     Vector<Gfx::Path> clip_paths; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -55,10 +55,38 @@ CommandResult CommandExecutorCPU::draw_text(Gfx::IntRect const& rect, String con | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CommandResult CommandExecutorCPU::fill_rect(Gfx::IntRect const& rect, Color const& color) | template<typename Callback> | ||||||
|  | void apply_clip_paths_to_painter(Gfx::IntRect const& rect, Callback callback, Vector<Gfx::Path> const& clip_paths, Gfx::Painter& target_painter) | ||||||
| { | { | ||||||
|     auto& painter = this->painter(); |     // Setup a painter for a background canvas that we will paint to first.
 | ||||||
|     painter.fill_rect(rect, color); |     auto background_canvas = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, rect.size()).release_value_but_fixme_should_propagate_errors(); | ||||||
|  |     Gfx::Painter painter(*background_canvas); | ||||||
|  | 
 | ||||||
|  |     // Offset the painter to paint in the correct location.
 | ||||||
|  |     painter.translate(-rect.location()); | ||||||
|  | 
 | ||||||
|  |     // Paint the background canvas.
 | ||||||
|  |     callback(painter); | ||||||
|  | 
 | ||||||
|  |     // Apply the clip path to the target painter.
 | ||||||
|  |     Gfx::AntiAliasingPainter aa_painter(target_painter); | ||||||
|  |     for (auto const& clip_path : clip_paths) { | ||||||
|  |         auto fill_offset = clip_path.bounding_box().location().to_type<int>() - rect.location(); | ||||||
|  |         auto paint_style = Gfx::BitmapPaintStyle::create(*background_canvas, fill_offset).release_value_but_fixme_should_propagate_errors(); | ||||||
|  |         aa_painter.fill_path(clip_path, paint_style); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CommandResult CommandExecutorCPU::fill_rect(Gfx::IntRect const& rect, Color const& color, Vector<Gfx::Path> const& clip_paths) | ||||||
|  | { | ||||||
|  |     auto paint_op = [&](Gfx::Painter& painter) { | ||||||
|  |         painter.fill_rect(rect, color); | ||||||
|  |     }; | ||||||
|  |     if (clip_paths.is_empty()) { | ||||||
|  |         paint_op(painter()); | ||||||
|  |     } else { | ||||||
|  |         apply_clip_paths_to_painter(rect, paint_op, clip_paths, painter()); | ||||||
|  |     } | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -69,10 +97,16 @@ CommandResult CommandExecutorCPU::draw_scaled_bitmap(Gfx::IntRect const& dst_rec | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CommandResult CommandExecutorCPU::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& immutable_bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) | CommandResult CommandExecutorCPU::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& immutable_bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     auto& painter = this->painter(); |     auto paint_op = [&](Gfx::Painter& painter) { | ||||||
|     painter.draw_scaled_bitmap(dst_rect, immutable_bitmap.bitmap(), src_rect, 1, scaling_mode); |         painter.draw_scaled_bitmap(dst_rect, immutable_bitmap.bitmap(), src_rect, 1, scaling_mode); | ||||||
|  |     }; | ||||||
|  |     if (clip_paths.is_empty()) { | ||||||
|  |         paint_op(painter()); | ||||||
|  |     } else { | ||||||
|  |         apply_clip_paths_to_painter(dst_rect, paint_op, clip_paths, painter()); | ||||||
|  |     } | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -200,12 +234,18 @@ CommandResult CommandExecutorCPU::pop_stacking_context() | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CommandResult CommandExecutorCPU::paint_linear_gradient(Gfx::IntRect const& gradient_rect, Web::Painting::LinearGradientData const& linear_gradient_data) | CommandResult CommandExecutorCPU::paint_linear_gradient(Gfx::IntRect const& gradient_rect, Web::Painting::LinearGradientData const& linear_gradient_data, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     auto const& data = linear_gradient_data; |     auto paint_op = [&](Gfx::Painter& painter) { | ||||||
|     painter().fill_rect_with_linear_gradient( |         painter.fill_rect_with_linear_gradient( | ||||||
|         gradient_rect, data.color_stops.list, |             gradient_rect, linear_gradient_data.color_stops.list, | ||||||
|         data.gradient_angle, data.color_stops.repeat_length); |             linear_gradient_data.gradient_angle, linear_gradient_data.color_stops.repeat_length); | ||||||
|  |     }; | ||||||
|  |     if (clip_paths.is_empty()) { | ||||||
|  |         paint_op(painter()); | ||||||
|  |     } else { | ||||||
|  |         apply_clip_paths_to_painter(gradient_rect, paint_op, clip_paths, painter()); | ||||||
|  |     } | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -255,16 +295,23 @@ CommandResult CommandExecutorCPU::paint_text_shadow(int blur_radius, Gfx::IntRec | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CommandResult CommandExecutorCPU::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius) | CommandResult CommandExecutorCPU::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     Gfx::AntiAliasingPainter aa_painter(painter()); |     auto paint_op = [&](Gfx::Painter& painter) { | ||||||
|     aa_painter.fill_rect_with_rounded_corners( |         Gfx::AntiAliasingPainter aa_painter(painter); | ||||||
|         rect, |         aa_painter.fill_rect_with_rounded_corners( | ||||||
|         color, |             rect, | ||||||
|         top_left_radius, |             color, | ||||||
|         top_right_radius, |             top_left_radius, | ||||||
|         bottom_right_radius, |             top_right_radius, | ||||||
|         bottom_left_radius); |             bottom_right_radius, | ||||||
|  |             bottom_left_radius); | ||||||
|  |     }; | ||||||
|  |     if (clip_paths.is_empty()) { | ||||||
|  |         paint_op(painter()); | ||||||
|  |     } else { | ||||||
|  |         apply_clip_paths_to_painter(rect, paint_op, clip_paths, painter()); | ||||||
|  |     } | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -380,15 +427,29 @@ CommandResult CommandExecutorCPU::draw_rect(Gfx::IntRect const& rect, Color cons | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CommandResult CommandExecutorCPU::paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size) | CommandResult CommandExecutorCPU::paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     painter().fill_rect_with_radial_gradient(rect, radial_gradient_data.color_stops.list, center, size, radial_gradient_data.color_stops.repeat_length); |     auto paint_op = [&](Gfx::Painter& painter) { | ||||||
|  |         painter.fill_rect_with_radial_gradient(rect, radial_gradient_data.color_stops.list, center, size, radial_gradient_data.color_stops.repeat_length); | ||||||
|  |     }; | ||||||
|  |     if (clip_paths.is_empty()) { | ||||||
|  |         paint_op(painter()); | ||||||
|  |     } else { | ||||||
|  |         apply_clip_paths_to_painter(rect, paint_op, clip_paths, painter()); | ||||||
|  |     } | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CommandResult CommandExecutorCPU::paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position) | CommandResult CommandExecutorCPU::paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     painter().fill_rect_with_conic_gradient(rect, conic_gradient_data.color_stops.list, position, conic_gradient_data.start_angle, conic_gradient_data.color_stops.repeat_length); |     auto paint_op = [&](Gfx::Painter& painter) { | ||||||
|  |         painter.fill_rect_with_conic_gradient(rect, conic_gradient_data.color_stops.list, position, conic_gradient_data.start_angle, conic_gradient_data.color_stops.repeat_length); | ||||||
|  |     }; | ||||||
|  |     if (clip_paths.is_empty()) { | ||||||
|  |         paint_op(painter()); | ||||||
|  |     } else { | ||||||
|  |         apply_clip_paths_to_painter(rect, paint_op, clip_paths, painter()); | ||||||
|  |     } | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,18 +15,18 @@ class CommandExecutorCPU : public CommandExecutor { | ||||||
| public: | public: | ||||||
|     CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) override; |     CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) override; | ||||||
|     CommandResult draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) override; |     CommandResult draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) override; | ||||||
|     CommandResult fill_rect(Gfx::IntRect const& rect, Color const&) override; |     CommandResult fill_rect(Gfx::IntRect const& rect, Color const&, Vector<Gfx::Path> const& clip_paths) override; | ||||||
|     CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override; |     CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override; | ||||||
|     CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override; |     CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode, Vector<Gfx::Path> const& clip_paths = {}) override; | ||||||
|     CommandResult set_clip_rect(Gfx::IntRect const& rect) override; |     CommandResult set_clip_rect(Gfx::IntRect const& rect) override; | ||||||
|     CommandResult clear_clip_rect() override; |     CommandResult clear_clip_rect() override; | ||||||
|     CommandResult push_stacking_context(float opacity, bool is_fixed_position, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) override; |     CommandResult push_stacking_context(float opacity, bool is_fixed_position, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) override; | ||||||
|     CommandResult pop_stacking_context() override; |     CommandResult pop_stacking_context() override; | ||||||
|     CommandResult paint_linear_gradient(Gfx::IntRect const&, Web::Painting::LinearGradientData const&) override; |     CommandResult paint_linear_gradient(Gfx::IntRect const&, Web::Painting::LinearGradientData const&, Vector<Gfx::Path> const& clip_paths = {}) override; | ||||||
|     CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) override; |     CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) override; | ||||||
|     CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) override; |     CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) override; | ||||||
|     CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) override; |     CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) override; | ||||||
|     CommandResult fill_rect_with_rounded_corners(Gfx::IntRect const&, Color const&, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius) override; |     CommandResult fill_rect_with_rounded_corners(Gfx::IntRect const&, Color const&, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, Vector<Gfx::Path> const& clip_paths) override; | ||||||
|     CommandResult fill_path_using_color(Gfx::Path const&, Color const&, Gfx::Painter::WindingRule winding_rule, Gfx::FloatPoint const& aa_translation) override; |     CommandResult fill_path_using_color(Gfx::Path const&, Color const&, Gfx::Painter::WindingRule winding_rule, Gfx::FloatPoint const& aa_translation) override; | ||||||
|     CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) override; |     CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) override; | ||||||
|     CommandResult stroke_path_using_color(Gfx::Path const&, Color const& color, float thickness, Gfx::FloatPoint const& aa_translation) override; |     CommandResult stroke_path_using_color(Gfx::Path const&, Color const& color, float thickness, Gfx::FloatPoint const& aa_translation) override; | ||||||
|  | @ -38,8 +38,8 @@ public: | ||||||
|     CommandResult paint_frame(Gfx::IntRect const& rect, Palette const&, Gfx::FrameStyle) override; |     CommandResult paint_frame(Gfx::IntRect const& rect, Palette const&, Gfx::FrameStyle) override; | ||||||
|     CommandResult apply_backdrop_filter(Gfx::IntRect const& backdrop_region, Web::CSS::ResolvedBackdropFilter const& backdrop_filter) override; |     CommandResult apply_backdrop_filter(Gfx::IntRect const& backdrop_region, Web::CSS::ResolvedBackdropFilter const& backdrop_filter) override; | ||||||
|     CommandResult draw_rect(Gfx::IntRect const& rect, Color const&, bool rough) override; |     CommandResult draw_rect(Gfx::IntRect const& rect, Color const&, bool rough) override; | ||||||
|     CommandResult paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size) override; |     CommandResult paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size, Vector<Gfx::Path> const& clip_paths = {}) override; | ||||||
|     CommandResult paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position) override; |     CommandResult paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths = {}) override; | ||||||
|     CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const&, int amplitude, int thickness) override; |     CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const&, int amplitude, int thickness) override; | ||||||
|     CommandResult sample_under_corners(u32 id, CornerRadii const&, Gfx::IntRect const&, CornerClip) override; |     CommandResult sample_under_corners(u32 id, CornerRadii const&, Gfx::IntRect const&, CornerClip) override; | ||||||
|     CommandResult blit_corner_clipping(u32 id) override; |     CommandResult blit_corner_clipping(u32 id) override; | ||||||
|  |  | ||||||
|  | @ -53,8 +53,9 @@ CommandResult CommandExecutorGPU::draw_text(Gfx::IntRect const&, String const&, | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CommandResult CommandExecutorGPU::fill_rect(Gfx::IntRect const& rect, Color const& color) | CommandResult CommandExecutorGPU::fill_rect(Gfx::IntRect const& rect, Color const& color, Vector<Gfx::Path> const&) | ||||||
| { | { | ||||||
|  |     // FIXME: Support clip paths
 | ||||||
|     painter().fill_rect(rect, color); |     painter().fill_rect(rect, color); | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
|  | @ -80,8 +81,9 @@ CommandResult CommandExecutorGPU::draw_scaled_bitmap(Gfx::IntRect const& dst_rec | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CommandResult CommandExecutorGPU::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& immutable_bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) | CommandResult CommandExecutorGPU::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& immutable_bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode, Vector<Gfx::Path> const&) | ||||||
| { | { | ||||||
|  |     // TODO: Support clip paths
 | ||||||
|     painter().draw_scaled_immutable_bitmap(dst_rect, immutable_bitmap, src_rect, to_accelgfx_scaling_mode(scaling_mode)); |     painter().draw_scaled_immutable_bitmap(dst_rect, immutable_bitmap, src_rect, to_accelgfx_scaling_mode(scaling_mode)); | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
|  | @ -160,8 +162,9 @@ CommandResult CommandExecutorGPU::pop_stacking_context() | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CommandResult CommandExecutorGPU::paint_linear_gradient(Gfx::IntRect const& rect, Web::Painting::LinearGradientData const& data) | CommandResult CommandExecutorGPU::paint_linear_gradient(Gfx::IntRect const& rect, Web::Painting::LinearGradientData const& data, Vector<Gfx::Path> const&) | ||||||
| { | { | ||||||
|  |     // FIXME: Support clip paths
 | ||||||
|     painter().fill_rect_with_linear_gradient(rect, data.color_stops.list, data.gradient_angle, data.color_stops.repeat_length); |     painter().fill_rect_with_linear_gradient(rect, data.color_stops.list, data.gradient_angle, data.color_stops.repeat_length); | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
|  | @ -201,8 +204,9 @@ CommandResult CommandExecutorGPU::paint_text_shadow(int blur_radius, Gfx::IntRec | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CommandResult CommandExecutorGPU::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius) | CommandResult CommandExecutorGPU::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, Vector<Gfx::Path> const&) | ||||||
| { | { | ||||||
|  |     // FIXME: Support clip paths
 | ||||||
|     painter().fill_rect_with_rounded_corners( |     painter().fill_rect_with_rounded_corners( | ||||||
|         rect, color, |         rect, color, | ||||||
|         { static_cast<float>(top_left_radius.horizontal_radius), static_cast<float>(top_left_radius.vertical_radius) }, |         { static_cast<float>(top_left_radius.horizontal_radius), static_cast<float>(top_left_radius.vertical_radius) }, | ||||||
|  | @ -286,13 +290,13 @@ CommandResult CommandExecutorGPU::draw_rect(Gfx::IntRect const&, Color const&, b | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CommandResult CommandExecutorGPU::paint_radial_gradient(Gfx::IntRect const&, Web::Painting::RadialGradientData const&, Gfx::IntPoint const&, Gfx::IntSize const&) | CommandResult CommandExecutorGPU::paint_radial_gradient(Gfx::IntRect const&, Web::Painting::RadialGradientData const&, Gfx::IntPoint const&, Gfx::IntSize const&, Vector<Gfx::Path> const&) | ||||||
| { | { | ||||||
|     // FIXME
 |     // FIXME
 | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CommandResult CommandExecutorGPU::paint_conic_gradient(Gfx::IntRect const&, Web::Painting::ConicGradientData const&, Gfx::IntPoint const&) | CommandResult CommandExecutorGPU::paint_conic_gradient(Gfx::IntRect const&, Web::Painting::ConicGradientData const&, Gfx::IntPoint const&, Vector<Gfx::Path> const&) | ||||||
| { | { | ||||||
|     // FIXME
 |     // FIXME
 | ||||||
|     return CommandResult::Continue; |     return CommandResult::Continue; | ||||||
|  |  | ||||||
|  | @ -16,18 +16,18 @@ class CommandExecutorGPU : public CommandExecutor { | ||||||
| public: | public: | ||||||
|     CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) override; |     CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) override; | ||||||
|     CommandResult draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) override; |     CommandResult draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) override; | ||||||
|     CommandResult fill_rect(Gfx::IntRect const& rect, Color const&) override; |     CommandResult fill_rect(Gfx::IntRect const& rect, Color const&, Vector<Gfx::Path> const& clip_paths) override; | ||||||
|     CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override; |     CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override; | ||||||
|     CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override; |     CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode, Vector<Gfx::Path> const& clip_paths = {}) override; | ||||||
|     CommandResult set_clip_rect(Gfx::IntRect const& rect) override; |     CommandResult set_clip_rect(Gfx::IntRect const& rect) override; | ||||||
|     CommandResult clear_clip_rect() override; |     CommandResult clear_clip_rect() override; | ||||||
|     CommandResult push_stacking_context(float opacity, bool, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) override; |     CommandResult push_stacking_context(float opacity, bool, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) override; | ||||||
|     CommandResult pop_stacking_context() override; |     CommandResult pop_stacking_context() override; | ||||||
|     CommandResult paint_linear_gradient(Gfx::IntRect const&, Web::Painting::LinearGradientData const&) override; |     CommandResult paint_linear_gradient(Gfx::IntRect const&, Web::Painting::LinearGradientData const&, Vector<Gfx::Path> const& clip_paths = {}) override; | ||||||
|     CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) override; |     CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) override; | ||||||
|     CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) override; |     CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) override; | ||||||
|     CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) override; |     CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) override; | ||||||
|     CommandResult fill_rect_with_rounded_corners(Gfx::IntRect const&, Color const&, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius) override; |     CommandResult fill_rect_with_rounded_corners(Gfx::IntRect const&, Color const&, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, Vector<Gfx::Path> const& clip_paths) override; | ||||||
|     CommandResult fill_path_using_color(Gfx::Path const&, Color const&, Gfx::Painter::WindingRule winding_rule, Gfx::FloatPoint const& aa_translation) override; |     CommandResult fill_path_using_color(Gfx::Path const&, Color const&, Gfx::Painter::WindingRule winding_rule, Gfx::FloatPoint const& aa_translation) override; | ||||||
|     CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) override; |     CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) override; | ||||||
|     CommandResult stroke_path_using_color(Gfx::Path const&, Color const& color, float thickness, Gfx::FloatPoint const& aa_translation) override; |     CommandResult stroke_path_using_color(Gfx::Path const&, Color const& color, float thickness, Gfx::FloatPoint const& aa_translation) override; | ||||||
|  | @ -39,8 +39,8 @@ public: | ||||||
|     CommandResult paint_frame(Gfx::IntRect const& rect, Palette const&, Gfx::FrameStyle) override; |     CommandResult paint_frame(Gfx::IntRect const& rect, Palette const&, Gfx::FrameStyle) override; | ||||||
|     CommandResult apply_backdrop_filter(Gfx::IntRect const& backdrop_region, Web::CSS::ResolvedBackdropFilter const& backdrop_filter) override; |     CommandResult apply_backdrop_filter(Gfx::IntRect const& backdrop_region, Web::CSS::ResolvedBackdropFilter const& backdrop_filter) override; | ||||||
|     CommandResult draw_rect(Gfx::IntRect const& rect, Color const&, bool rough) override; |     CommandResult draw_rect(Gfx::IntRect const& rect, Color const&, bool rough) override; | ||||||
|     CommandResult paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size) override; |     CommandResult paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size, Vector<Gfx::Path> const& clip_paths = {}) override; | ||||||
|     CommandResult paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position) override; |     CommandResult paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths = {}) override; | ||||||
|     CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const&, int amplitude, int thickness) override; |     CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const&, int amplitude, int thickness) override; | ||||||
|     CommandResult sample_under_corners(u32 id, CornerRadii const&, Gfx::IntRect const&, CornerClip) override; |     CommandResult sample_under_corners(u32 id, CornerRadii const&, Gfx::IntRect const&, CornerClip) override; | ||||||
|     CommandResult blit_corner_clipping(u32) override; |     CommandResult blit_corner_clipping(u32) override; | ||||||
|  |  | ||||||
|  | @ -96,7 +96,7 @@ void CommandList::execute(CommandExecutor& executor) | ||||||
|                     command.elision, command.wrapping, command.font); |                     command.elision, command.wrapping, command.font); | ||||||
|             }, |             }, | ||||||
|             [&](FillRect const& command) { |             [&](FillRect const& command) { | ||||||
|                 return executor.fill_rect(command.rect, command.color); |                 return executor.fill_rect(command.rect, command.color, command.clip_paths); | ||||||
|             }, |             }, | ||||||
|             [&](DrawScaledBitmap const& command) { |             [&](DrawScaledBitmap const& command) { | ||||||
|                 return executor.draw_scaled_bitmap(command.dst_rect, command.bitmap, command.src_rect, |                 return executor.draw_scaled_bitmap(command.dst_rect, command.bitmap, command.src_rect, | ||||||
|  | @ -104,7 +104,7 @@ void CommandList::execute(CommandExecutor& executor) | ||||||
|             }, |             }, | ||||||
|             [&](DrawScaledImmutableBitmap const& command) { |             [&](DrawScaledImmutableBitmap const& command) { | ||||||
|                 return executor.draw_scaled_immutable_bitmap(command.dst_rect, command.bitmap, command.src_rect, |                 return executor.draw_scaled_immutable_bitmap(command.dst_rect, command.bitmap, command.src_rect, | ||||||
|                     command.scaling_mode); |                     command.scaling_mode, command.clip_paths); | ||||||
|             }, |             }, | ||||||
|             [&](SetClipRect const& command) { |             [&](SetClipRect const& command) { | ||||||
|                 return executor.set_clip_rect(command.rect); |                 return executor.set_clip_rect(command.rect); | ||||||
|  | @ -122,15 +122,15 @@ void CommandList::execute(CommandExecutor& executor) | ||||||
|                 return executor.pop_stacking_context(); |                 return executor.pop_stacking_context(); | ||||||
|             }, |             }, | ||||||
|             [&](PaintLinearGradient const& command) { |             [&](PaintLinearGradient const& command) { | ||||||
|                 return executor.paint_linear_gradient(command.gradient_rect, command.linear_gradient_data); |                 return executor.paint_linear_gradient(command.gradient_rect, command.linear_gradient_data, command.clip_paths); | ||||||
|             }, |             }, | ||||||
|             [&](PaintRadialGradient const& command) { |             [&](PaintRadialGradient const& command) { | ||||||
|                 return executor.paint_radial_gradient(command.rect, command.radial_gradient_data, |                 return executor.paint_radial_gradient(command.rect, command.radial_gradient_data, | ||||||
|                     command.center, command.size); |                     command.center, command.size, command.clip_paths); | ||||||
|             }, |             }, | ||||||
|             [&](PaintConicGradient const& command) { |             [&](PaintConicGradient const& command) { | ||||||
|                 return executor.paint_conic_gradient(command.rect, command.conic_gradient_data, |                 return executor.paint_conic_gradient(command.rect, command.conic_gradient_data, | ||||||
|                     command.position); |                     command.position, command.clip_paths); | ||||||
|             }, |             }, | ||||||
|             [&](PaintOuterBoxShadow const& command) { |             [&](PaintOuterBoxShadow const& command) { | ||||||
|                 return executor.paint_outer_box_shadow(command.outer_box_shadow_params); |                 return executor.paint_outer_box_shadow(command.outer_box_shadow_params); | ||||||
|  | @ -148,7 +148,8 @@ void CommandList::execute(CommandExecutor& executor) | ||||||
|                     command.top_left_radius, |                     command.top_left_radius, | ||||||
|                     command.top_right_radius, |                     command.top_right_radius, | ||||||
|                     command.bottom_left_radius, |                     command.bottom_left_radius, | ||||||
|                     command.bottom_right_radius); |                     command.bottom_right_radius, | ||||||
|  |                     command.clip_paths); | ||||||
|             }, |             }, | ||||||
|             [&](FillPathUsingColor const& command) { |             [&](FillPathUsingColor const& command) { | ||||||
|                 return executor.fill_path_using_color(command.path, command.color, command.winding_rule, |                 return executor.fill_path_using_color(command.path, command.color, command.winding_rule, | ||||||
|  |  | ||||||
|  | @ -49,16 +49,16 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) = 0; |     virtual CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) = 0; | ||||||
|     virtual CommandResult draw_text(Gfx::IntRect const&, String const&, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) = 0; |     virtual CommandResult draw_text(Gfx::IntRect const&, String const&, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) = 0; | ||||||
|     virtual CommandResult fill_rect(Gfx::IntRect const&, Color const&) = 0; |     virtual CommandResult fill_rect(Gfx::IntRect const&, Color const&, Vector<Gfx::Path> const& clip_paths) = 0; | ||||||
|     virtual CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) = 0; |     virtual CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) = 0; | ||||||
|     virtual CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) = 0; |     virtual CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode, Vector<Gfx::Path> const& clip_paths = {}) = 0; | ||||||
|     virtual CommandResult set_clip_rect(Gfx::IntRect const& rect) = 0; |     virtual CommandResult set_clip_rect(Gfx::IntRect const& rect) = 0; | ||||||
|     virtual CommandResult clear_clip_rect() = 0; |     virtual CommandResult clear_clip_rect() = 0; | ||||||
|     virtual CommandResult push_stacking_context(float opacity, bool is_fixed_position, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) = 0; |     virtual CommandResult push_stacking_context(float opacity, bool is_fixed_position, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) = 0; | ||||||
|     virtual CommandResult pop_stacking_context() = 0; |     virtual CommandResult pop_stacking_context() = 0; | ||||||
|     virtual CommandResult paint_linear_gradient(Gfx::IntRect const&, LinearGradientData const&) = 0; |     virtual CommandResult paint_linear_gradient(Gfx::IntRect const&, LinearGradientData const&, Vector<Gfx::Path> const& clip_paths = {}) = 0; | ||||||
|     virtual CommandResult paint_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const&, Gfx::IntPoint const& center, Gfx::IntSize const& size) = 0; |     virtual CommandResult paint_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const&, Gfx::IntPoint const& center, Gfx::IntSize const& size, Vector<Gfx::Path> const& clip_paths = {}) = 0; | ||||||
|     virtual CommandResult paint_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const&, Gfx::IntPoint const& position) = 0; |     virtual CommandResult paint_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const&, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths = {}) = 0; | ||||||
|     virtual CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) = 0; |     virtual CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) = 0; | ||||||
|     virtual CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) = 0; |     virtual CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) = 0; | ||||||
|     virtual CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) = 0; |     virtual CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) = 0; | ||||||
|  | @ -66,7 +66,8 @@ public: | ||||||
|         Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, |         Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, | ||||||
|         Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, |         Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, | ||||||
|         Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, |         Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, | ||||||
|         Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius) |         Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, | ||||||
|  |         Vector<Gfx::Path> const& clip_paths = {}) | ||||||
|         = 0; |         = 0; | ||||||
|     virtual CommandResult fill_path_using_color(Gfx::Path const&, Color const& color, Gfx::Painter::WindingRule, Gfx::FloatPoint const& aa_translation) = 0; |     virtual CommandResult fill_path_using_color(Gfx::Path const&, Color const& color, Gfx::Painter::WindingRule, Gfx::FloatPoint const& aa_translation) = 0; | ||||||
|     virtual CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) = 0; |     virtual CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) = 0; | ||||||
|  |  | ||||||
|  | @ -146,19 +146,19 @@ RadialGradientData resolve_radial_gradient_data(Layout::NodeWithStyleAndBoxModel | ||||||
|     return { resolved_color_stops }; |     return { resolved_color_stops }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void paint_linear_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, LinearGradientData const& data) | void paint_linear_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, LinearGradientData const& data, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     context.recording_painter().fill_rect_with_linear_gradient(gradient_rect.to_type<int>(), data); |     context.recording_painter().fill_rect_with_linear_gradient(gradient_rect.to_type<int>(), data, clip_paths); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void paint_conic_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, ConicGradientData const& data, DevicePixelPoint position) | void paint_conic_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, ConicGradientData const& data, DevicePixelPoint position, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     context.recording_painter().fill_rect_with_conic_gradient(gradient_rect.to_type<int>(), data, position.to_type<int>()); |     context.recording_painter().fill_rect_with_conic_gradient(gradient_rect.to_type<int>(), data, position.to_type<int>(), clip_paths); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void paint_radial_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, RadialGradientData const& data, DevicePixelPoint center, DevicePixelSize size) | void paint_radial_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, RadialGradientData const& data, DevicePixelPoint center, DevicePixelSize size, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     context.recording_painter().fill_rect_with_radial_gradient(gradient_rect.to_type<int>(), data, center.to_type<int>(), size.to_type<int>()); |     context.recording_painter().fill_rect_with_radial_gradient(gradient_rect.to_type<int>(), data, center.to_type<int>(), size.to_type<int>(), clip_paths); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -20,8 +20,8 @@ LinearGradientData resolve_linear_gradient_data(Layout::NodeWithStyleAndBoxModel | ||||||
| ConicGradientData resolve_conic_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSS::ConicGradientStyleValue const&); | ConicGradientData resolve_conic_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSS::ConicGradientStyleValue const&); | ||||||
| RadialGradientData resolve_radial_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize, CSS::RadialGradientStyleValue const&); | RadialGradientData resolve_radial_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize, CSS::RadialGradientStyleValue const&); | ||||||
| 
 | 
 | ||||||
| void paint_linear_gradient(PaintContext&, DevicePixelRect const&, LinearGradientData const&); | void paint_linear_gradient(PaintContext&, DevicePixelRect const&, LinearGradientData const&, Vector<Gfx::Path> const& clip_paths = {}); | ||||||
| void paint_conic_gradient(PaintContext&, DevicePixelRect const&, ConicGradientData const&, DevicePixelPoint position); | void paint_conic_gradient(PaintContext&, DevicePixelRect const&, ConicGradientData const&, DevicePixelPoint position, Vector<Gfx::Path> const& clip_paths = {}); | ||||||
| void paint_radial_gradient(PaintContext&, DevicePixelRect const&, RadialGradientData const&, DevicePixelPoint position, DevicePixelSize size); | void paint_radial_gradient(PaintContext&, DevicePixelRect const&, RadialGradientData const&, DevicePixelPoint position, DevicePixelSize size, Vector<Gfx::Path> const& clip_paths = {}); | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <AK/GenericShorthands.h> | #include <AK/GenericShorthands.h> | ||||||
|  | #include <LibGfx/Font/ScaledFont.h> | ||||||
| #include <LibUnicode/CharacterTypes.h> | #include <LibUnicode/CharacterTypes.h> | ||||||
| #include <LibWeb/CSS/SystemColor.h> | #include <LibWeb/CSS/SystemColor.h> | ||||||
| #include <LibWeb/DOM/Document.h> | #include <LibWeb/DOM/Document.h> | ||||||
|  | @ -455,7 +456,72 @@ void PaintableBox::paint_background(PaintContext& context) const | ||||||
|     if (computed_values().border_top().width != 0 || computed_values().border_right().width != 0 || computed_values().border_bottom().width != 0 || computed_values().border_left().width != 0) |     if (computed_values().border_top().width != 0 || computed_values().border_right().width != 0 || computed_values().border_bottom().width != 0 || computed_values().border_left().width != 0) | ||||||
|         background_rect = absolute_border_box_rect(); |         background_rect = absolute_border_box_rect(); | ||||||
| 
 | 
 | ||||||
|     Painting::paint_background(context, layout_box(), background_rect, background_color, computed_values().image_rendering(), background_layers, normalized_border_radii_data()); |     Vector<Gfx::Path> text_clip_paths {}; | ||||||
|  |     if (background_layers && !background_layers->is_empty() && background_layers->last().clip == CSS::BackgroundBox::Text) { | ||||||
|  |         text_clip_paths = compute_text_clip_paths(context); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Painting::paint_background(context, layout_box(), background_rect, background_color, computed_values().image_rendering(), background_layers, normalized_border_radii_data(), text_clip_paths); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Vector<Gfx::Path> PaintableBox::compute_text_clip_paths(PaintContext& context) const | ||||||
|  | { | ||||||
|  |     Vector<Gfx::Path> text_clip_paths; | ||||||
|  |     auto add_text_clip_path = [&](PaintableFragment const& fragment) { | ||||||
|  |         // Scale to the device pixels.
 | ||||||
|  |         Gfx::Path glyph_run_path; | ||||||
|  |         for (auto glyph : fragment.glyph_run().glyphs()) { | ||||||
|  |             glyph.visit([&](auto& glyph) { | ||||||
|  |                 glyph.font = *glyph.font->with_size(glyph.font->point_size() * static_cast<float>(context.device_pixels_per_css_pixel())); | ||||||
|  |                 glyph.position = glyph.position.scaled(context.device_pixels_per_css_pixel()); | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             if (glyph.has<Gfx::DrawGlyph>()) { | ||||||
|  |                 auto const& draw_glyph = glyph.get<Gfx::DrawGlyph>(); | ||||||
|  | 
 | ||||||
|  |                 // Get the path for the glyph.
 | ||||||
|  |                 Gfx::Path glyph_path; | ||||||
|  |                 auto const& scaled_font = static_cast<Gfx::ScaledFont const&>(*draw_glyph.font); | ||||||
|  |                 auto glyph_id = scaled_font.glyph_id_for_code_point(draw_glyph.code_point); | ||||||
|  |                 scaled_font.append_glyph_path_to(glyph_path, glyph_id); | ||||||
|  | 
 | ||||||
|  |                 // Transform the path to the fragment's position.
 | ||||||
|  |                 // FIXME: Record glyphs and use Painter::draw_glyphs() instead to avoid this duplicated code.
 | ||||||
|  |                 auto top_left = draw_glyph.position + Gfx::FloatPoint(scaled_font.glyph_left_bearing(draw_glyph.code_point), 0); | ||||||
|  |                 auto glyph_position = Gfx::GlyphRasterPosition::get_nearest_fit_for(top_left); | ||||||
|  |                 auto transform = Gfx::AffineTransform {}.translate(glyph_position.blit_position.to_type<float>()); | ||||||
|  |                 glyph_run_path.append_path(glyph_path.copy_transformed(transform)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Calculate the baseline start position.
 | ||||||
|  |         auto fragment_absolute_rect = fragment.absolute_rect(); | ||||||
|  |         auto fragment_absolute_device_rect = context.enclosing_device_rect(fragment_absolute_rect); | ||||||
|  |         DevicePixelPoint baseline_start { fragment_absolute_device_rect.x(), fragment_absolute_device_rect.y() + context.rounded_device_pixels(fragment.baseline()) }; | ||||||
|  | 
 | ||||||
|  |         // Add the path to text_clip_paths.
 | ||||||
|  |         auto transform = Gfx::AffineTransform {}.translate(baseline_start.to_type<int>().to_type<float>()); | ||||||
|  |         text_clip_paths.append(glyph_run_path.copy_transformed(transform)); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     for_each_in_inclusive_subtree([&](auto& paintable) { | ||||||
|  |         if (is<PaintableWithLines>(paintable)) { | ||||||
|  |             auto const& paintable_lines = static_cast<PaintableWithLines const&>(paintable); | ||||||
|  |             for (auto const& fragment : paintable_lines.fragments()) { | ||||||
|  |                 if (is<Layout::TextNode>(fragment.layout_node())) | ||||||
|  |                     add_text_clip_path(fragment); | ||||||
|  |             } | ||||||
|  |         } else if (is<InlinePaintable>(paintable)) { | ||||||
|  |             auto const& inline_paintable = static_cast<InlinePaintable const&>(paintable); | ||||||
|  |             for (auto const& fragment : inline_paintable.fragments()) { | ||||||
|  |                 if (is<Layout::TextNode>(fragment.layout_node())) | ||||||
|  |                     add_text_clip_path(fragment); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return TraversalDecision::Continue; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return text_clip_paths; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void PaintableBox::paint_box_shadow(PaintContext& context) const | void PaintableBox::paint_box_shadow(PaintContext& context) const | ||||||
|  |  | ||||||
|  | @ -227,6 +227,8 @@ protected: | ||||||
|     virtual CSSPixelRect compute_absolute_paint_rect() const; |     virtual CSSPixelRect compute_absolute_paint_rect() const; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  |     Vector<Gfx::Path> compute_text_clip_paths(PaintContext&) const; | ||||||
|  | 
 | ||||||
|     [[nodiscard]] virtual bool is_paintable_box() const final { return true; } |     [[nodiscard]] virtual bool is_paintable_box() const final { return true; } | ||||||
| 
 | 
 | ||||||
|     enum class ScrollDirection { |     enum class ScrollDirection { | ||||||
|  |  | ||||||
|  | @ -34,11 +34,12 @@ void RecordingPainter::blit_corner_clipping(u32 id, Gfx::IntRect border_rect) | ||||||
|     append(BlitCornerClipping { id, border_rect = state().translation.map(border_rect) }); |     append(BlitCornerClipping { id, border_rect = state().translation.map(border_rect) }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RecordingPainter::fill_rect(Gfx::IntRect const& rect, Color color) | void RecordingPainter::fill_rect(Gfx::IntRect const& rect, Color color, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     append(FillRect { |     append(FillRect { | ||||||
|         .rect = state().translation.map(rect), |         .rect = state().translation.map(rect), | ||||||
|         .color = color, |         .color = color, | ||||||
|  |         .clip_paths = clip_paths, | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -118,29 +119,31 @@ void RecordingPainter::fill_ellipse(Gfx::IntRect const& a_rect, Color color, Gfx | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RecordingPainter::fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data) | void RecordingPainter::fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     append(PaintLinearGradient { |     append(PaintLinearGradient { | ||||||
|         .gradient_rect = state().translation.map(gradient_rect), |         .gradient_rect = state().translation.map(gradient_rect), | ||||||
|         .linear_gradient_data = data, |         .linear_gradient_data = data, | ||||||
|     }); |         .clip_paths = clip_paths }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RecordingPainter::fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position) | void RecordingPainter::fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     append(PaintConicGradient { |     append(PaintConicGradient { | ||||||
|         .rect = state().translation.map(rect), |         .rect = state().translation.map(rect), | ||||||
|         .conic_gradient_data = data, |         .conic_gradient_data = data, | ||||||
|         .position = position }); |         .position = position, | ||||||
|  |         .clip_paths = clip_paths }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RecordingPainter::fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size) | void RecordingPainter::fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     append(PaintRadialGradient { |     append(PaintRadialGradient { | ||||||
|         .rect = state().translation.map(rect), |         .rect = state().translation.map(rect), | ||||||
|         .radial_gradient_data = data, |         .radial_gradient_data = data, | ||||||
|         .center = center, |         .center = center, | ||||||
|         .size = size }); |         .size = size, | ||||||
|  |         .clip_paths = clip_paths }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RecordingPainter::draw_rect(Gfx::IntRect const& rect, Color color, bool rough) | void RecordingPainter::draw_rect(Gfx::IntRect const& rect, Color color, bool rough) | ||||||
|  | @ -161,13 +164,14 @@ void RecordingPainter::draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bit | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RecordingPainter::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) | void RecordingPainter::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     append(DrawScaledImmutableBitmap { |     append(DrawScaledImmutableBitmap { | ||||||
|         .dst_rect = state().translation.map(dst_rect), |         .dst_rect = state().translation.map(dst_rect), | ||||||
|         .bitmap = bitmap, |         .bitmap = bitmap, | ||||||
|         .src_rect = src_rect, |         .src_rect = src_rect, | ||||||
|         .scaling_mode = scaling_mode, |         .scaling_mode = scaling_mode, | ||||||
|  |         .clip_paths = clip_paths, | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -327,10 +331,10 @@ void RecordingPainter::paint_text_shadow(int blur_radius, Gfx::IntRect bounding_ | ||||||
|         .draw_location = state().translation.map(draw_location) }); |         .draw_location = state().translation.map(draw_location) }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::AntiAliasingPainter::CornerRadius top_left_radius, Gfx::AntiAliasingPainter::CornerRadius top_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius) | void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::AntiAliasingPainter::CornerRadius top_left_radius, Gfx::AntiAliasingPainter::CornerRadius top_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     if (!top_left_radius && !top_right_radius && !bottom_right_radius && !bottom_left_radius) { |     if (!top_left_radius && !top_right_radius && !bottom_right_radius && !bottom_left_radius) { | ||||||
|         fill_rect(rect, color); |         fill_rect(rect, color, clip_paths); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -341,21 +345,23 @@ void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, | ||||||
|         .top_right_radius = top_right_radius, |         .top_right_radius = top_right_radius, | ||||||
|         .bottom_left_radius = bottom_left_radius, |         .bottom_left_radius = bottom_left_radius, | ||||||
|         .bottom_right_radius = bottom_right_radius, |         .bottom_right_radius = bottom_right_radius, | ||||||
|  |         .clip_paths = clip_paths, | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius) | void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     fill_rect_with_rounded_corners(a_rect, color, radius, radius, radius, radius); |     fill_rect_with_rounded_corners(a_rect, color, radius, radius, radius, radius, clip_paths); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius) | void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius, Vector<Gfx::Path> const& clip_paths) | ||||||
| { | { | ||||||
|     fill_rect_with_rounded_corners(a_rect, color, |     fill_rect_with_rounded_corners(a_rect, color, | ||||||
|         { top_left_radius, top_left_radius }, |         { top_left_radius, top_left_radius }, | ||||||
|         { top_right_radius, top_right_radius }, |         { top_right_radius, top_right_radius }, | ||||||
|         { bottom_right_radius, bottom_right_radius }, |         { bottom_right_radius, bottom_right_radius }, | ||||||
|         { bottom_left_radius, bottom_left_radius }); |         { bottom_left_radius, bottom_left_radius }, | ||||||
|  |         clip_paths); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RecordingPainter::draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness = 1) | void RecordingPainter::draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness = 1) | ||||||
|  |  | ||||||
|  | @ -44,7 +44,7 @@ class RecordingPainter { | ||||||
|     AK_MAKE_NONMOVABLE(RecordingPainter); |     AK_MAKE_NONMOVABLE(RecordingPainter); | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     void fill_rect(Gfx::IntRect const& rect, Color color); |     void fill_rect(Gfx::IntRect const& rect, Color color, Vector<Gfx::Path> const& clip_paths = {}); | ||||||
| 
 | 
 | ||||||
|     struct FillPathUsingColorParams { |     struct FillPathUsingColorParams { | ||||||
|         Gfx::Path path; |         Gfx::Path path; | ||||||
|  | @ -84,14 +84,14 @@ public: | ||||||
| 
 | 
 | ||||||
|     void fill_ellipse(Gfx::IntRect const& a_rect, Color color, Gfx::AntiAliasingPainter::BlendMode blend_mode = Gfx::AntiAliasingPainter::BlendMode::Normal); |     void fill_ellipse(Gfx::IntRect const& a_rect, Color color, Gfx::AntiAliasingPainter::BlendMode blend_mode = Gfx::AntiAliasingPainter::BlendMode::Normal); | ||||||
| 
 | 
 | ||||||
|     void fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data); |     void fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data, Vector<Gfx::Path> const& clip_paths = {}); | ||||||
|     void fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position); |     void fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths = {}); | ||||||
|     void fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size); |     void fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size, Vector<Gfx::Path> const& clip_paths = {}); | ||||||
| 
 | 
 | ||||||
|     void draw_rect(Gfx::IntRect const& rect, Color color, bool rough = false); |     void draw_rect(Gfx::IntRect const& rect, Color color, bool rough = false); | ||||||
| 
 | 
 | ||||||
|     void draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode = Gfx::Painter::ScalingMode::NearestNeighbor); |     void draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode = Gfx::Painter::ScalingMode::NearestNeighbor); | ||||||
|     void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode = Gfx::Painter::ScalingMode::NearestNeighbor); |     void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode = Gfx::Painter::ScalingMode::NearestNeighbor, Vector<Gfx::Path> const& clip_paths = {}); | ||||||
| 
 | 
 | ||||||
|     void draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness = 1, Gfx::Painter::LineStyle style = Gfx::Painter::LineStyle::Solid, Color alternate_color = Color::Transparent); |     void draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness = 1, Gfx::Painter::LineStyle style = Gfx::Painter::LineStyle::Solid, Color alternate_color = Color::Transparent); | ||||||
| 
 | 
 | ||||||
|  | @ -137,9 +137,9 @@ public: | ||||||
|     void paint_inner_box_shadow_params(PaintOuterBoxShadowParams params); |     void paint_inner_box_shadow_params(PaintOuterBoxShadowParams params); | ||||||
|     void paint_text_shadow(int blur_radius, Gfx::IntRect bounding_rect, Gfx::IntRect text_rect, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, int fragment_baseline, Gfx::IntPoint draw_location); |     void paint_text_shadow(int blur_radius, Gfx::IntRect bounding_rect, Gfx::IntRect text_rect, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, int fragment_baseline, Gfx::IntPoint draw_location); | ||||||
| 
 | 
 | ||||||
|     void fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::AntiAliasingPainter::CornerRadius top_left_radius, Gfx::AntiAliasingPainter::CornerRadius top_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius); |     void fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::AntiAliasingPainter::CornerRadius top_left_radius, Gfx::AntiAliasingPainter::CornerRadius top_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius, Vector<Gfx::Path> const& clip_paths = {}); | ||||||
|     void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius); |     void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius, Vector<Gfx::Path> const& clip_paths = {}); | ||||||
|     void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius); |     void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius, Vector<Gfx::Path> const& clip_paths = {}); | ||||||
| 
 | 
 | ||||||
|     void draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness); |     void draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Zac Brannelly
						Zac Brannelly