From a044e9cf4fc1a039c0b47f983a210b8c963a6beb Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Tue, 23 Apr 2024 12:07:58 +0200 Subject: [PATCH] LibWeb: Add `background-clip: text` support for InlinePaintable Moves background mask calculation into `paint_background()` that is shared between PaintableBox and InlinePaintable. --- ...inline-paintable-background-clip-text.html | 11 +++ ...ne-paintable-background-clip-text-ref.html | 10 +++ .../LibWeb/Painting/BackgroundPainting.cpp | 76 +++++++++++++++++-- .../LibWeb/Painting/BackgroundPainting.h | 2 +- .../LibWeb/Painting/PaintableBox.cpp | 67 +--------------- .../Libraries/LibWeb/Painting/PaintableBox.h | 2 - 6 files changed, 92 insertions(+), 76 deletions(-) create mode 100644 Tests/LibWeb/Ref/inline-paintable-background-clip-text.html create mode 100644 Tests/LibWeb/Ref/reference/inline-paintable-background-clip-text-ref.html diff --git a/Tests/LibWeb/Ref/inline-paintable-background-clip-text.html b/Tests/LibWeb/Ref/inline-paintable-background-clip-text.html new file mode 100644 index 0000000000..fd0aca6215 --- /dev/null +++ b/Tests/LibWeb/Ref/inline-paintable-background-clip-text.html @@ -0,0 +1,11 @@ + + + +Hello diff --git a/Tests/LibWeb/Ref/reference/inline-paintable-background-clip-text-ref.html b/Tests/LibWeb/Ref/reference/inline-paintable-background-clip-text-ref.html new file mode 100644 index 0000000000..c1ff4f0bf6 --- /dev/null +++ b/Tests/LibWeb/Ref/reference/inline-paintable-background-clip-text-ref.html @@ -0,0 +1,10 @@ + + +
Hello
diff --git a/Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp b/Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp index de24a0f556..d788d57cc5 100644 --- a/Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp +++ b/Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp @@ -7,14 +7,11 @@ */ #include +#include #include #include #include -#include -#include -#include -#include -#include +#include namespace Web::Painting { @@ -60,9 +57,74 @@ static CSSPixelSize run_default_sizing_algorithm( return default_size; } -// 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, Vector const& clip_paths) +static Vector compute_text_clip_paths(PaintContext& context, Paintable const& paintable) { + 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)); + }; + + paintable.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; +} + +// 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) +{ + Vector clip_paths {}; + if (background_layers && !background_layers->is_empty() && background_layers->last().clip == CSS::BackgroundBox::Text) { + clip_paths = compute_text_clip_paths(context, *layout_node.paintable()); + } + auto& painter = context.recording_painter(); struct BackgroundBox { diff --git a/Userland/Libraries/LibWeb/Painting/BackgroundPainting.h b/Userland/Libraries/LibWeb/Painting/BackgroundPainting.h index 8da6a9240c..32c5fbad40 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&, Vector const& clip_paths = {}); +void paint_background(PaintContext&, Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelRect const&, Color background_color, CSS::ImageRendering, Vector const*, BorderRadiiData const&); } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index 09ee1f2a6c..e1d254110a 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -478,72 +478,7 @@ 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(); - 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; + Painting::paint_background(context, layout_box(), background_rect, background_color, computed_values().image_rendering(), background_layers, normalized_border_radii_data()); } 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 82c0dab16f..974fe48e36 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.h +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.h @@ -227,8 +227,6 @@ 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 {