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:
Zac Brannelly 2024-03-03 14:59:02 +11:00 committed by Alexander Kalenik
parent 4a3680cafc
commit 9165faca5e
27 changed files with 353 additions and 105 deletions

View 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>

View 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">

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

View file

@ -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<Gfx::Path> const& clip_paths = {}) const = 0;
virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const { return {}; }
};

View file

@ -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<Gfx::Path> 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

View file

@ -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<Gfx::Path> const& clip_paths = {}) const override;
virtual bool equals(StyleValue const& other) const override;

View file

@ -131,11 +131,11 @@ Optional<CSSPixelFraction> 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<Gfx::Path> const& clip_paths) const
{
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>());
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);
}
}

View file

@ -45,7 +45,7 @@ public:
Optional<CSSPixelFraction> 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<Gfx::Path> const& clip_paths = {}) const override;
virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const override;

View file

@ -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<Gfx::Path> 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);
}
}

View file

@ -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<Gfx::Path> const& clip_paths = {}) const override;
private:
LinearGradientStyleValue(GradientDirection direction, Vector<LinearColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating)

View file

@ -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<Gfx::Path> 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);
}
}

View file

@ -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<Gfx::Path> const& clip_paths = {}) const override;
virtual bool equals(StyleValue const& other) const override;

View file

@ -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<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();
@ -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>(),
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<int>(), color.value());
painter.fill_rect(fill_rect->to_type<int>(), 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);
});
}
}

View file

@ -12,6 +12,6 @@
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 = {});
}

View file

@ -65,6 +65,7 @@ struct DrawText {
struct FillRect {
Gfx::IntRect rect;
Color color;
Vector<Gfx::Path> 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<Gfx::ImmutableBitmap> bitmap;
Gfx::IntRect src_rect;
Gfx::Painter::ScalingMode scaling_mode;
Vector<Gfx::Path> 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<Gfx::Path> 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<Gfx::Path> 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<Gfx::Path> 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<Gfx::Path> clip_paths;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }

View file

@ -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<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();
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<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;
}
@ -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<Gfx::Path> 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<Gfx::Path> 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<Gfx::Path> 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<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;
}
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;
}

View file

@ -15,18 +15,18 @@ class CommandExecutorCPU : public CommandExecutor {
public:
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 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_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 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 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_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 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_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<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, 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 sample_under_corners(u32 id, CornerRadii const&, Gfx::IntRect const&, CornerClip) override;
CommandResult blit_corner_clipping(u32 id) override;

View file

@ -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<Gfx::Path> 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<Gfx::Path> 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<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);
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<Gfx::Path> const&)
{
// FIXME: Support clip paths
painter().fill_rect_with_rounded_corners(
rect, color,
{ 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;
}
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
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
return CommandResult::Continue;

View file

@ -16,18 +16,18 @@ class CommandExecutorGPU : public CommandExecutor {
public:
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 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_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 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 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_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 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_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<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, 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 sample_under_corners(u32 id, CornerRadii const&, Gfx::IntRect const&, CornerClip) override;
CommandResult blit_corner_clipping(u32) override;

View file

@ -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,

View file

@ -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_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_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 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 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<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, Vector<Gfx::Path> const& clip_paths = {}) = 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_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;
@ -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<Gfx::Path> 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;

View file

@ -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<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);
}
}

View file

@ -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<Gfx::Path> const& clip_paths = {});
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, Vector<Gfx::Path> const& clip_paths = {});
}

View file

@ -6,6 +6,7 @@
*/
#include <AK/GenericShorthands.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibUnicode/CharacterTypes.h>
#include <LibWeb/CSS/SystemColor.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)
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

View file

@ -227,6 +227,8 @@ protected:
virtual CSSPixelRect compute_absolute_paint_rect() const;
private:
Vector<Gfx::Path> compute_text_clip_paths(PaintContext&) const;
[[nodiscard]] virtual bool is_paintable_box() const final { return true; }
enum class ScrollDirection {

View file

@ -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<Gfx::Path> 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<Gfx::Path> 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<Gfx::Path> 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<Gfx::Path> 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<Gfx::Path> 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<Gfx::Path> 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<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,
{ 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)

View file

@ -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<Gfx::Path> 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<Gfx::Path> const& clip_paths = {});
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, Vector<Gfx::Path> 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<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);
@ -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<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& 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<Gfx::Path> const& clip_paths = {});
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, Vector<Gfx::Path> const& clip_paths = {});
void draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness);