1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 23:07:35 +00:00

LibWeb: Support CSS property background-clip: text

From https://drafts.csswg.org/css-backgrounds-4/#background-clip
"The background is painted within (clipped to) the intersection of the
border box and the geometry of the text in the element and its in-flow
and floated descendants"

This change implements it in the following way:
1. Traverse the descendants of the element, collecting the Gfx::Path of
   glyphs into a vector.
2. The vector of collected paths is saved in the background painting
   command.
3. The painting commands executor uses the list of glyphs to paint a
   mask for background clipping.

Co-authored-by: Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
This commit is contained in:
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

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