diff --git a/Tests/LibWeb/Ref/css-background-clip-text.html b/Tests/LibWeb/Ref/css-background-clip-text.html new file mode 100644 index 00000000000..a9c3c256eaa --- /dev/null +++ b/Tests/LibWeb/Ref/css-background-clip-text.html @@ -0,0 +1,84 @@ + + + + + + + Document + + + + + + + +

The background extends behind the border.

+

+ The background extends to the inside edge of the border. +

+

+ The background extends only to the edge of the content box. +

+
+ The background is clipped to the foreground text. + Some other text in a sub-element +
+
+ The background is clipped to the foreground text. + Some other text in a sub-element +
+
+ The background is clipped to the foreground text. + Some other text in a sub-element +
+
+ Testing text. +
+
The is nested text that should still be clipped to the background
+
+
+ + diff --git a/Tests/LibWeb/Ref/reference/css-background-clip-text-ref.html b/Tests/LibWeb/Ref/reference/css-background-clip-text-ref.html new file mode 100644 index 00000000000..5cfb475e25b --- /dev/null +++ b/Tests/LibWeb/Ref/reference/css-background-clip-text-ref.html @@ -0,0 +1,15 @@ + + + diff --git a/Tests/LibWeb/Ref/reference/images/css-background-clip-text.png b/Tests/LibWeb/Ref/reference/images/css-background-clip-text.png new file mode 100644 index 00000000000..ed6617f9bd0 Binary files /dev/null and b/Tests/LibWeb/Ref/reference/images/css-background-clip-text.png differ diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h index e0338a5247f..1327198b4cb 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h @@ -36,7 +36,7 @@ public: virtual void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const {}; 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 const& clip_paths = {}) const = 0; virtual Optional color_if_single_pixel_bitmap() const { return {}; } }; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp index efe6ae695f6..72e7e209734 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp @@ -42,10 +42,10 @@ void ConicGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModelM 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 const& clip_paths) const { 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 diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h index 4b1bad4fff1..2a5a73b138a 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h @@ -25,7 +25,7 @@ public: 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 const& clip_paths = {}) const override; virtual bool equals(StyleValue const& other) const override; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp index 587f0602b74..b88253d37b0 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp @@ -131,11 +131,11 @@ Optional ImageStyleValue::natural_aspect_ratio() const 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 const& clip_paths) const { if (auto const* b = bitmap(m_current_frame_index, dest_rect.size().to_type()); b != nullptr) { auto scaling_mode = to_gfx_scaling_mode(image_rendering, b->rect(), dest_rect.to_type()); - context.recording_painter().draw_scaled_immutable_bitmap(dest_rect.to_type(), *b, b->rect(), scaling_mode); + context.recording_painter().draw_scaled_immutable_bitmap(dest_rect.to_type(), *b, b->rect(), scaling_mode, clip_paths); } } diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h index 875f65c1cee..b237a26e37f 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h @@ -45,7 +45,7 @@ public: Optional natural_aspect_ratio() 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 const& clip_paths = {}) const override; virtual Optional color_if_single_pixel_bitmap() const override; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp index 5a623b16048..842bb320616 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp @@ -109,10 +109,10 @@ void LinearGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModel 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 const& clip_paths) const { 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); } } diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h index 74f84de7861..06b46e192f9 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h @@ -60,7 +60,7 @@ public: void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const override; 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 const& clip_paths = {}) const override; private: LinearGradientStyleValue(GradientDirection direction, Vector color_stop_list, GradientType type, GradientRepeating repeating) diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp index 94dcc179424..fbaa48f8712 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp @@ -207,12 +207,13 @@ bool RadialGradientStyleValue::equals(StyleValue const& other) const 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 const& clip_paths) const { VERIFY(m_resolved.has_value()); Painting::paint_radial_gradient(context, dest_rect, m_resolved->data, 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); } } diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h index 2f8c6bd795d..bfe5556de88 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h @@ -51,7 +51,7 @@ public: 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 const& clip_paths = {}) const override; virtual bool equals(StyleValue const& other) const override; diff --git a/Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp b/Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp index 1be5c43df4c..de24a0f5562 100644 --- a/Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp +++ b/Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp @@ -61,7 +61,7 @@ static CSSPixelSize run_default_sizing_algorithm( } // 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 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 const* background_layers, BorderRadiiData const& border_radii, Vector const& clip_paths) { 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(), background_color, color_box.radii.top_left.as_corner(context), color_box.radii.top_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) return; @@ -388,10 +389,10 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet fill_rect = fill_rect->united(image_device_rect); } }); - painter.fill_rect(fill_rect->to_type(), color.value()); + painter.fill_rect(fill_rect->to_type(), color.value(), clip_paths); } else { 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); }); } } diff --git a/Userland/Libraries/LibWeb/Painting/BackgroundPainting.h b/Userland/Libraries/LibWeb/Painting/BackgroundPainting.h index 32c5fbad409..8da6a9240c6 100644 --- a/Userland/Libraries/LibWeb/Painting/BackgroundPainting.h +++ b/Userland/Libraries/LibWeb/Painting/BackgroundPainting.h @@ -12,6 +12,6 @@ namespace Web::Painting { -void paint_background(PaintContext&, Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelRect const&, Color background_color, CSS::ImageRendering, Vector const*, BorderRadiiData const&); +void paint_background(PaintContext&, Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelRect const&, Color background_color, CSS::ImageRendering, Vector const*, BorderRadiiData const&, Vector const& clip_paths = {}); } diff --git a/Userland/Libraries/LibWeb/Painting/Command.h b/Userland/Libraries/LibWeb/Painting/Command.h index 7bf45740b52..6943aed39b9 100644 --- a/Userland/Libraries/LibWeb/Painting/Command.h +++ b/Userland/Libraries/LibWeb/Painting/Command.h @@ -65,6 +65,7 @@ struct DrawText { struct FillRect { Gfx::IntRect rect; Color color; + Vector clip_paths; [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } @@ -85,6 +86,7 @@ struct DrawScaledImmutableBitmap { NonnullRefPtr bitmap; Gfx::IntRect src_rect; Gfx::Painter::ScalingMode scaling_mode; + Vector clip_paths; [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; } void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } @@ -128,6 +130,7 @@ struct PopStackingContext { }; struct PaintLinearGradient { Gfx::IntRect gradient_rect; LinearGradientData linear_gradient_data; + Vector clip_paths; [[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; } @@ -170,6 +173,7 @@ struct FillRectWithRoundedCorners { Gfx::AntiAliasingPainter::CornerRadius top_right_radius; Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius; Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius; + Vector clip_paths; [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } @@ -334,6 +338,7 @@ struct PaintRadialGradient { RadialGradientData radial_gradient_data; Gfx::IntPoint center; Gfx::IntSize size; + Vector clip_paths; [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } @@ -344,6 +349,7 @@ struct PaintConicGradient { Gfx::IntRect rect; ConicGradientData conic_gradient_data; Gfx::IntPoint position; + Vector clip_paths; [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } diff --git a/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.cpp b/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.cpp index 40e3e5cabe2..9f1844c3db0 100644 --- a/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.cpp +++ b/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.cpp @@ -55,10 +55,38 @@ CommandResult CommandExecutorCPU::draw_text(Gfx::IntRect const& rect, String con return CommandResult::Continue; } -CommandResult CommandExecutorCPU::fill_rect(Gfx::IntRect const& rect, Color const& color) +template +void apply_clip_paths_to_painter(Gfx::IntRect const& rect, Callback callback, Vector const& clip_paths, Gfx::Painter& target_painter) { - auto& painter = this->painter(); - painter.fill_rect(rect, color); + // Setup a painter for a background canvas that we will paint to first. + 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() - 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 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; } @@ -69,10 +97,16 @@ CommandResult CommandExecutorCPU::draw_scaled_bitmap(Gfx::IntRect const& dst_rec 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 const& clip_paths) { - auto& painter = this->painter(); - painter.draw_scaled_bitmap(dst_rect, immutable_bitmap.bitmap(), src_rect, 1, scaling_mode); + auto paint_op = [&](Gfx::Painter& painter) { + 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; } @@ -200,12 +234,18 @@ CommandResult CommandExecutorCPU::pop_stacking_context() 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 const& clip_paths) { - auto const& data = linear_gradient_data; - painter().fill_rect_with_linear_gradient( - gradient_rect, data.color_stops.list, - data.gradient_angle, data.color_stops.repeat_length); + auto paint_op = [&](Gfx::Painter& painter) { + painter.fill_rect_with_linear_gradient( + gradient_rect, linear_gradient_data.color_stops.list, + 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; } @@ -255,16 +295,23 @@ CommandResult CommandExecutorCPU::paint_text_shadow(int blur_radius, Gfx::IntRec 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 const& clip_paths) { - Gfx::AntiAliasingPainter aa_painter(painter()); - aa_painter.fill_rect_with_rounded_corners( - rect, - color, - top_left_radius, - top_right_radius, - bottom_right_radius, - bottom_left_radius); + auto paint_op = [&](Gfx::Painter& painter) { + Gfx::AntiAliasingPainter aa_painter(painter); + aa_painter.fill_rect_with_rounded_corners( + rect, + color, + top_left_radius, + top_right_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; } @@ -380,15 +427,29 @@ CommandResult CommandExecutorCPU::draw_rect(Gfx::IntRect const& rect, Color cons 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 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; } -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 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; } diff --git a/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.h b/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.h index 1e7b01385d9..333d0a6394d 100644 --- a/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.h +++ b/Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.h @@ -15,18 +15,18 @@ class CommandExecutorCPU : public CommandExecutor { public: CommandResult draw_glyph_run(Vector 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> const&) override; - CommandResult fill_rect(Gfx::IntRect const& rect, Color const&) override; + CommandResult fill_rect(Gfx::IntRect const& rect, Color const&, Vector 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_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 const& clip_paths = {}) override; CommandResult set_clip_rect(Gfx::IntRect const& 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 mask) 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 const& clip_paths = {}) override; CommandResult paint_outer_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, 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 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_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; @@ -38,8 +38,8 @@ public: 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 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_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position) 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 const& clip_paths = {}) override; + CommandResult paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position, Vector const& clip_paths = {}) 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 blit_corner_clipping(u32 id) override; diff --git a/Userland/Libraries/LibWeb/Painting/CommandExecutorGPU.cpp b/Userland/Libraries/LibWeb/Painting/CommandExecutorGPU.cpp index 4d8779180ee..e21c64df6d4 100644 --- a/Userland/Libraries/LibWeb/Painting/CommandExecutorGPU.cpp +++ b/Userland/Libraries/LibWeb/Painting/CommandExecutorGPU.cpp @@ -53,8 +53,9 @@ CommandResult CommandExecutorGPU::draw_text(Gfx::IntRect const&, String const&, 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 const&) { + // FIXME: Support clip paths painter().fill_rect(rect, color); return CommandResult::Continue; } @@ -80,8 +81,9 @@ CommandResult CommandExecutorGPU::draw_scaled_bitmap(Gfx::IntRect const& dst_rec 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 const&) { + // TODO: Support clip paths painter().draw_scaled_immutable_bitmap(dst_rect, immutable_bitmap, src_rect, to_accelgfx_scaling_mode(scaling_mode)); return CommandResult::Continue; } @@ -160,8 +162,9 @@ CommandResult CommandExecutorGPU::pop_stacking_context() 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 const&) { + // FIXME: Support clip paths painter().fill_rect_with_linear_gradient(rect, data.color_stops.list, data.gradient_angle, data.color_stops.repeat_length); return CommandResult::Continue; } @@ -201,8 +204,9 @@ CommandResult CommandExecutorGPU::paint_text_shadow(int blur_radius, Gfx::IntRec 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 const&) { + // FIXME: Support clip paths painter().fill_rect_with_rounded_corners( rect, color, { static_cast(top_left_radius.horizontal_radius), static_cast(top_left_radius.vertical_radius) }, @@ -286,13 +290,13 @@ CommandResult CommandExecutorGPU::draw_rect(Gfx::IntRect const&, Color const&, b 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 const&) { // FIXME 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 const&) { // FIXME return CommandResult::Continue; diff --git a/Userland/Libraries/LibWeb/Painting/CommandExecutorGPU.h b/Userland/Libraries/LibWeb/Painting/CommandExecutorGPU.h index e78344fb7ee..8a09d52ab08 100644 --- a/Userland/Libraries/LibWeb/Painting/CommandExecutorGPU.h +++ b/Userland/Libraries/LibWeb/Painting/CommandExecutorGPU.h @@ -16,18 +16,18 @@ class CommandExecutorGPU : public CommandExecutor { public: CommandResult draw_glyph_run(Vector 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> const&) override; - CommandResult fill_rect(Gfx::IntRect const& rect, Color const&) override; + CommandResult fill_rect(Gfx::IntRect const& rect, Color const&, Vector 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_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 const& clip_paths = {}) override; CommandResult set_clip_rect(Gfx::IntRect const& 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 mask) 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 const& clip_paths = {}) override; CommandResult paint_outer_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, 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 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_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; @@ -39,8 +39,8 @@ public: 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 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_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position) 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 const& clip_paths = {}) override; + CommandResult paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position, Vector const& clip_paths = {}) 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 blit_corner_clipping(u32) override; diff --git a/Userland/Libraries/LibWeb/Painting/CommandList.cpp b/Userland/Libraries/LibWeb/Painting/CommandList.cpp index 22f3b716b65..7bc14df9673 100644 --- a/Userland/Libraries/LibWeb/Painting/CommandList.cpp +++ b/Userland/Libraries/LibWeb/Painting/CommandList.cpp @@ -96,7 +96,7 @@ void CommandList::execute(CommandExecutor& executor) command.elision, command.wrapping, command.font); }, [&](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) { 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) { 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) { return executor.set_clip_rect(command.rect); @@ -122,15 +122,15 @@ void CommandList::execute(CommandExecutor& executor) return executor.pop_stacking_context(); }, [&](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) { 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) { return executor.paint_conic_gradient(command.rect, command.conic_gradient_data, - command.position); + command.position, command.clip_paths); }, [&](PaintOuterBoxShadow const& command) { 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_right_radius, command.bottom_left_radius, - command.bottom_right_radius); + command.bottom_right_radius, + command.clip_paths); }, [&](FillPathUsingColor const& command) { return executor.fill_path_using_color(command.path, command.color, command.winding_rule, diff --git a/Userland/Libraries/LibWeb/Painting/CommandList.h b/Userland/Libraries/LibWeb/Painting/CommandList.h index 849f9c142ba..d06569eaf6c 100644 --- a/Userland/Libraries/LibWeb/Painting/CommandList.h +++ b/Userland/Libraries/LibWeb/Painting/CommandList.h @@ -49,16 +49,16 @@ public: virtual CommandResult draw_glyph_run(Vector 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> const&) = 0; - virtual CommandResult fill_rect(Gfx::IntRect const&, Color const&) = 0; + virtual CommandResult fill_rect(Gfx::IntRect const&, Color const&, Vector 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_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 const& clip_paths = {}) = 0; virtual CommandResult set_clip_rect(Gfx::IntRect const& 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 mask) = 0; virtual CommandResult pop_stacking_context() = 0; - virtual CommandResult paint_linear_gradient(Gfx::IntRect const&, LinearGradientData const&) = 0; - virtual CommandResult paint_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const&, Gfx::IntPoint const& center, Gfx::IntSize const& size) = 0; - virtual CommandResult paint_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const&, Gfx::IntPoint const& position) = 0; + virtual CommandResult paint_linear_gradient(Gfx::IntRect const&, LinearGradientData const&, Vector const& clip_paths = {}) = 0; + virtual CommandResult paint_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const&, Gfx::IntPoint const& center, Gfx::IntSize const& size, Vector const& clip_paths = {}) = 0; + virtual CommandResult paint_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const&, Gfx::IntPoint const& position, Vector const& clip_paths = {}) = 0; virtual CommandResult paint_outer_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, 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_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, - Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius) + Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, + Vector const& clip_paths = {}) = 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; diff --git a/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp b/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp index c66d632eaff..bab68948503 100644 --- a/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp +++ b/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp @@ -146,19 +146,19 @@ RadialGradientData resolve_radial_gradient_data(Layout::NodeWithStyleAndBoxModel 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 const& clip_paths) { - context.recording_painter().fill_rect_with_linear_gradient(gradient_rect.to_type(), data); + context.recording_painter().fill_rect_with_linear_gradient(gradient_rect.to_type(), 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 const& clip_paths) { - context.recording_painter().fill_rect_with_conic_gradient(gradient_rect.to_type(), data, position.to_type()); + context.recording_painter().fill_rect_with_conic_gradient(gradient_rect.to_type(), data, position.to_type(), 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 const& clip_paths) { - context.recording_painter().fill_rect_with_radial_gradient(gradient_rect.to_type(), data, center.to_type(), size.to_type()); + context.recording_painter().fill_rect_with_radial_gradient(gradient_rect.to_type(), data, center.to_type(), size.to_type(), clip_paths); } } diff --git a/Userland/Libraries/LibWeb/Painting/GradientPainting.h b/Userland/Libraries/LibWeb/Painting/GradientPainting.h index c4e7c55017d..a6ac04b51f7 100644 --- a/Userland/Libraries/LibWeb/Painting/GradientPainting.h +++ b/Userland/Libraries/LibWeb/Painting/GradientPainting.h @@ -20,8 +20,8 @@ LinearGradientData resolve_linear_gradient_data(Layout::NodeWithStyleAndBoxModel ConicGradientData resolve_conic_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSS::ConicGradientStyleValue const&); RadialGradientData resolve_radial_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize, CSS::RadialGradientStyleValue const&); -void paint_linear_gradient(PaintContext&, DevicePixelRect const&, LinearGradientData const&); -void paint_conic_gradient(PaintContext&, DevicePixelRect const&, ConicGradientData const&, DevicePixelPoint position); -void paint_radial_gradient(PaintContext&, DevicePixelRect const&, RadialGradientData const&, DevicePixelPoint position, DevicePixelSize size); +void paint_linear_gradient(PaintContext&, DevicePixelRect const&, LinearGradientData const&, Vector const& clip_paths = {}); +void paint_conic_gradient(PaintContext&, DevicePixelRect const&, ConicGradientData const&, DevicePixelPoint position, Vector const& clip_paths = {}); +void paint_radial_gradient(PaintContext&, DevicePixelRect const&, RadialGradientData const&, DevicePixelPoint position, DevicePixelSize size, Vector const& clip_paths = {}); } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index 0a3f8cd5b0f..ca5436aafe1 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -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) 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 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 PaintableBox::compute_text_clip_paths(PaintContext& context) const +{ + Vector 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(context.device_pixels_per_css_pixel())); + glyph.position = glyph.position.scaled(context.device_pixels_per_css_pixel()); + }); + + if (glyph.has()) { + auto const& draw_glyph = glyph.get(); + + // Get the path for the glyph. + Gfx::Path glyph_path; + auto const& scaled_font = static_cast(*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()); + 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().to_type()); + text_clip_paths.append(glyph_run_path.copy_transformed(transform)); + }; + + for_each_in_inclusive_subtree([&](auto& paintable) { + if (is(paintable)) { + auto const& paintable_lines = static_cast(paintable); + for (auto const& fragment : paintable_lines.fragments()) { + if (is(fragment.layout_node())) + add_text_clip_path(fragment); + } + } else if (is(paintable)) { + auto const& inline_paintable = static_cast(paintable); + for (auto const& fragment : inline_paintable.fragments()) { + if (is(fragment.layout_node())) + add_text_clip_path(fragment); + } + } + return TraversalDecision::Continue; + }); + + return text_clip_paths; } void PaintableBox::paint_box_shadow(PaintContext& context) const diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.h b/Userland/Libraries/LibWeb/Painting/PaintableBox.h index 5a4ea664300..31d3b454e43 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.h +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.h @@ -227,6 +227,8 @@ protected: virtual CSSPixelRect compute_absolute_paint_rect() const; private: + Vector compute_text_clip_paths(PaintContext&) const; + [[nodiscard]] virtual bool is_paintable_box() const final { return true; } enum class ScrollDirection { diff --git a/Userland/Libraries/LibWeb/Painting/RecordingPainter.cpp b/Userland/Libraries/LibWeb/Painting/RecordingPainter.cpp index 3b6e9d446eb..4cf9f738832 100644 --- a/Userland/Libraries/LibWeb/Painting/RecordingPainter.cpp +++ b/Userland/Libraries/LibWeb/Painting/RecordingPainter.cpp @@ -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) }); } -void RecordingPainter::fill_rect(Gfx::IntRect const& rect, Color color) +void RecordingPainter::fill_rect(Gfx::IntRect const& rect, Color color, Vector const& clip_paths) { append(FillRect { .rect = state().translation.map(rect), .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 const& clip_paths) { append(PaintLinearGradient { .gradient_rect = state().translation.map(gradient_rect), .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 const& clip_paths) { append(PaintConicGradient { .rect = state().translation.map(rect), .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 const& clip_paths) { append(PaintRadialGradient { .rect = state().translation.map(rect), .radial_gradient_data = data, .center = center, - .size = size }); + .size = size, + .clip_paths = clip_paths }); } 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 const& clip_paths) { append(DrawScaledImmutableBitmap { .dst_rect = state().translation.map(dst_rect), .bitmap = bitmap, .src_rect = src_rect, .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) }); } -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 const& clip_paths) { if (!top_left_radius && !top_right_radius && !bottom_right_radius && !bottom_left_radius) { - fill_rect(rect, color); + fill_rect(rect, color, clip_paths); return; } @@ -341,21 +345,23 @@ void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, .top_right_radius = top_right_radius, .bottom_left_radius = bottom_left_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 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 const& clip_paths) { fill_rect_with_rounded_corners(a_rect, color, { top_left_radius, top_left_radius }, { top_right_radius, top_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) diff --git a/Userland/Libraries/LibWeb/Painting/RecordingPainter.h b/Userland/Libraries/LibWeb/Painting/RecordingPainter.h index a1cf69317d5..16d743a6d32 100644 --- a/Userland/Libraries/LibWeb/Painting/RecordingPainter.h +++ b/Userland/Libraries/LibWeb/Painting/RecordingPainter.h @@ -44,7 +44,7 @@ class RecordingPainter { AK_MAKE_NONMOVABLE(RecordingPainter); public: - void fill_rect(Gfx::IntRect const& rect, Color color); + void fill_rect(Gfx::IntRect const& rect, Color color, Vector const& clip_paths = {}); struct FillPathUsingColorParams { 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_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data); - void fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position); - void fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size); + void fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data, Vector const& clip_paths = {}); + void fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position, Vector const& clip_paths = {}); + void fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size, Vector const& clip_paths = {}); 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_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 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); @@ -137,9 +137,9 @@ public: void paint_inner_box_shadow_params(PaintOuterBoxShadowParams params); void paint_text_shadow(int blur_radius, Gfx::IntRect bounding_rect, Gfx::IntRect text_rect, Span 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& a_rect, Color color, int 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); + 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 const& clip_paths = {}); + void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius, Vector 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, Vector const& clip_paths = {}); void draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness);