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

LibWeb: Introduce dedicated painting command for borders

Currently, in CPU painter, border painting is implemented by building
a Gfx::Path that is filled by Gfx::AntiAliasingPainter. In the GPU
painter, we will likely want to do something different, and with a
special command, it becomes possible.

Also, by making this change, the CPU executor also benefits because now
we can skip building paths for borders that are out of the viewport.
This commit is contained in:
Aliaksandr Kalenik 2023-11-19 23:19:51 +01:00 committed by Andreas Kling
parent 70353b79af
commit 29ff1f67be
11 changed files with 50 additions and 12 deletions

View file

@ -115,7 +115,7 @@ Gfx::Color border_color(BorderEdge edge, BordersDataDevicePixels const& borders_
return border_data.color;
}
void paint_border(RecordingPainter& painter, BorderEdge edge, DevicePixelRect const& rect, Gfx::AntiAliasingPainter::CornerRadius const& radius, Gfx::AntiAliasingPainter::CornerRadius const& opposite_radius, BordersDataDevicePixels const& borders_data, Gfx::Path& path, bool last)
void paint_border(Gfx::Painter& painter, BorderEdge edge, DevicePixelRect const& rect, Gfx::AntiAliasingPainter::CornerRadius const& radius, Gfx::AntiAliasingPainter::CornerRadius const& opposite_radius, BordersDataDevicePixels const& borders_data, Gfx::Path& path, bool last)
{
auto const& border_data = [&] {
switch (edge) {
@ -243,7 +243,8 @@ void paint_border(RecordingPainter& painter, BorderEdge edge, DevicePixelRect co
// If joined borders have the same color, combine them to draw together.
if (ready_to_draw) {
path.close_all_subpaths();
painter.fill_path({ .path = path, .color = color, .winding_rule = Gfx::Painter::WindingRule::EvenOdd });
Gfx::AntiAliasingPainter aa_painter(painter);
aa_painter.fill_path(path, color, Gfx::Painter::WindingRule::EvenOdd);
path.clear();
}
};
@ -543,7 +544,7 @@ void paint_border(RecordingPainter& painter, BorderEdge edge, DevicePixelRect co
}
}
void paint_all_borders(RecordingPainter& painter, DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data)
void paint_all_borders(Gfx::Painter& painter, DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data)
{
if (borders_data.top.width <= 0 && borders_data.right.width <= 0 && borders_data.left.width <= 0 && borders_data.bottom.width <= 0)
return;

View file

@ -28,8 +28,8 @@ enum class BorderEdge {
// Returns OptionalNone if there is no outline to paint.
Optional<BordersData> borders_data_for_outline(Layout::Node const&, Color outline_color, CSS::OutlineStyle outline_style, CSSPixels outline_width);
void paint_border(RecordingPainter& painter, BorderEdge edge, DevicePixelRect const& rect, Gfx::AntiAliasingPainter::CornerRadius const& radius, Gfx::AntiAliasingPainter::CornerRadius const& opposite_radius, BordersDataDevicePixels const& borders_data, Gfx::Path& path, bool last);
void paint_all_borders(RecordingPainter& context, DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const&);
void paint_border(Gfx::Painter& painter, BorderEdge edge, DevicePixelRect const& rect, Gfx::AntiAliasingPainter::CornerRadius const& radius, Gfx::AntiAliasingPainter::CornerRadius const& opposite_radius, BordersDataDevicePixels const& borders_data, Gfx::Path& path, bool last);
void paint_all_borders(Gfx::Painter& painter, DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const&);
Gfx::Color border_color(BorderEdge edge, BordersDataDevicePixels const& borders_data);

View file

@ -134,9 +134,9 @@ void InlinePaintable::paint(PaintContext& context, PaintPhase phase) const
border_radii_data.inflate(outline_data->top.width + outline_offset_y, outline_data->right.width + outline_offset_x, outline_data->bottom.width + outline_offset_y, outline_data->left.width + outline_offset_x);
borders_rect.inflate(outline_data->top.width + outline_offset_y, outline_data->right.width + outline_offset_x, outline_data->bottom.width + outline_offset_y, outline_data->left.width + outline_offset_x);
paint_all_borders(context.painter(), context.rounded_device_rect(borders_rect), border_radii_data.as_corners(context), outline_data->to_device_pixels(context));
context.painter().paint_borders(context.rounded_device_rect(borders_rect), border_radii_data.as_corners(context), outline_data->to_device_pixels(context));
} else {
paint_all_borders(context.painter(), context.rounded_device_rect(borders_rect), border_radii_data.as_corners(context), borders_data.to_device_pixels(context));
context.painter().paint_borders(context.rounded_device_rect(borders_rect), border_radii_data.as_corners(context), borders_data.to_device_pixels(context));
}
return IterationDecision::Continue;

View file

@ -246,7 +246,7 @@ void PaintableBox::paint(PaintContext& context, PaintPhase phase) const
border_radius_data.inflate(outline_width + outline_offset_y, outline_width + outline_offset_x, outline_width + outline_offset_y, outline_width + outline_offset_x);
borders_rect.inflate(outline_width + outline_offset_y, outline_width + outline_offset_x, outline_width + outline_offset_y, outline_width + outline_offset_x);
paint_all_borders(context.painter(), context.rounded_device_rect(borders_rect), border_radius_data.as_corners(context), borders_data->to_device_pixels(context));
context.painter().paint_borders(context.rounded_device_rect(borders_rect), border_radius_data.as_corners(context), borders_data->to_device_pixels(context));
}
}
@ -313,7 +313,7 @@ void PaintableBox::paint_border(PaintContext& context) const
.bottom = box_model().border.bottom == 0 ? CSS::BorderData() : computed_values().border_bottom(),
.left = box_model().border.left == 0 ? CSS::BorderData() : computed_values().border_left(),
};
paint_all_borders(context.painter(), context.rounded_device_rect(absolute_border_box_rect()), normalized_border_radii_data().as_corners(context), borders_data.to_device_pixels(context));
context.painter().paint_borders(context.rounded_device_rect(absolute_border_box_rect()), normalized_border_radii_data().as_corners(context), borders_data.to_device_pixels(context));
}
void PaintableBox::paint_backdrop_filter(PaintContext& context) const

View file

@ -409,6 +409,12 @@ CommandResult PaintingCommandExecutorCPU::blit_corner_clipping(BorderRadiusCorne
return CommandResult::Continue;
}
CommandResult PaintingCommandExecutorCPU::paint_borders(DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data)
{
paint_all_borders(painter(), border_rect, corner_radii, borders_data);
return CommandResult::Continue;
}
bool PaintingCommandExecutorCPU::would_be_fully_clipped_by_painter(Gfx::IntRect rect) const
{
return !painter().clip_rect().intersects(rect.translated(painter().translation()));

View file

@ -44,6 +44,7 @@ public:
CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const&, int amplitude, int thickness) override;
CommandResult sample_under_corners(BorderRadiusCornerClipper&) override;
CommandResult blit_corner_clipping(BorderRadiusCornerClipper&) override;
CommandResult paint_borders(DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data) override;
bool would_be_fully_clipped_by_painter(Gfx::IntRect) const override;

View file

@ -220,6 +220,15 @@ CommandResult PaintingCommandExecutorGPU::blit_corner_clipping(BorderRadiusCorne
return CommandResult::Continue;
}
CommandResult PaintingCommandExecutorGPU::paint_borders(DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data)
{
// FIXME
(void)border_rect;
(void)corner_radii;
(void)borders_data;
return CommandResult::Continue;
}
bool PaintingCommandExecutorGPU::would_be_fully_clipped_by_painter(Gfx::IntRect) const
{
// FIXME

View file

@ -44,6 +44,7 @@ public:
CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const&, int amplitude, int thickness) override;
CommandResult sample_under_corners(BorderRadiusCornerClipper&) override;
CommandResult blit_corner_clipping(BorderRadiusCornerClipper&) override;
CommandResult paint_borders(DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data) override;
bool would_be_fully_clipped_by_painter(Gfx::IntRect) const override;

View file

@ -383,6 +383,11 @@ void RecordingPainter::draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2
.thickness = thickness });
}
void RecordingPainter::paint_borders(DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data)
{
push_command(PaintBorders { border_rect, corner_radii, borders_data });
}
static Optional<Gfx::IntRect> command_bounding_rectangle(PaintingCommand const& command)
{
return command.visit(
@ -512,6 +517,9 @@ void RecordingPainter::execute(PaintingCommandExecutor& executor)
},
[&](BlitCornerClipping const& command) {
return executor.blit_corner_clipping(command.corner_clipper);
},
[&](PaintBorders const& command) {
return executor.paint_borders(command.border_rect, command.corner_radii, command.borders_data);
});
if (result == CommandResult::SkipStackingContext) {

View file

@ -302,6 +302,14 @@ struct BlitCornerClipping {
[[nodiscard]] Gfx::IntRect bounding_rect() const;
};
struct PaintBorders {
DevicePixelRect border_rect;
CornerRadii corner_radii;
BordersDataDevicePixels borders_data;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return border_rect.to_type<int>(); }
};
using PaintingCommand = Variant<
DrawGlyphRun,
DrawText,
@ -333,7 +341,8 @@ using PaintingCommand = Variant<
DrawRect,
DrawTriangleWave,
SampleUnderCorners,
BlitCornerClipping>;
BlitCornerClipping,
PaintBorders>;
class PaintingCommandExecutor {
public:
@ -370,6 +379,7 @@ public:
virtual CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const& color, int amplitude, int thickness) = 0;
virtual CommandResult sample_under_corners(BorderRadiusCornerClipper&) = 0;
virtual CommandResult blit_corner_clipping(BorderRadiusCornerClipper&) = 0;
virtual CommandResult paint_borders(DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data) = 0;
virtual bool would_be_fully_clipped_by_painter(Gfx::IntRect) const = 0;
@ -476,6 +486,8 @@ public:
void draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness);
void paint_borders(DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data);
void execute(PaintingCommandExecutor&);
RecordingPainter()

View file

@ -351,7 +351,7 @@ static void paint_separate_cell_borders(PaintableBox const& cell_box, HashMap<Ce
.left = cell_box.box_model().border.left == 0 ? CSS::BorderData() : cell_box.computed_values().border_left(),
};
auto cell_rect = cell_coordinates_to_device_rect.get({ cell_box.table_cell_coordinates()->row_index, cell_box.table_cell_coordinates()->column_index }).value();
paint_all_borders(context.painter(), cell_rect, cell_box.normalized_border_radii_data().as_corners(context), borders_data.to_device_pixels(context));
context.painter().paint_borders(cell_rect, cell_box.normalized_border_radii_data().as_corners(context), borders_data.to_device_pixels(context));
}
void paint_table_borders(PaintContext& context, PaintableBox const& table_paintable)
@ -436,7 +436,7 @@ void paint_table_borders(PaintContext& context, PaintableBox const& table_painta
.bottom = cell_box.box_model().border.bottom == 0 ? CSS::BorderData() : cell_box.computed_values().border_bottom(),
.left = cell_box.box_model().border.left == 0 ? CSS::BorderData() : cell_box.computed_values().border_left(),
};
paint_all_borders(context.painter(), context.rounded_device_rect(cell_box.absolute_border_box_rect()), cell_box.normalized_border_radii_data().as_corners(context), borders_data.to_device_pixels(context));
context.painter().paint_borders(context.rounded_device_rect(cell_box.absolute_border_box_rect()), cell_box.normalized_border_radii_data().as_corners(context), borders_data.to_device_pixels(context));
}
}
}