LibWeb: Merge StrokePathUsingPaintStyle and StrokePathUsingColor

Use `Variant<PaintStyle, Gfx::Color>` in new `StrokePath` instead of
duplicating two almost identical display list items.
This commit is contained in:
Aliaksandr Kalenik 2025-08-02 20:04:12 +02:00 committed by Jelle Raaijmakers
commit 5c11a541d3
Notes: github-actions[bot] 2025-08-03 08:43:52 +00:00
12 changed files with 37 additions and 114 deletions

View file

@ -9,6 +9,7 @@
#include <AK/DistinctNumeric.h> #include <AK/DistinctNumeric.h>
#include <AK/Variant.h> #include <AK/Variant.h>
#include <LibGfx/Forward.h>
#include <LibIPC/Forward.h> #include <LibIPC/Forward.h>
#include <LibJS/Forward.h> #include <LibJS/Forward.h>
@ -42,6 +43,7 @@ class DisplayListRecorder;
class SVGGradientPaintStyle; class SVGGradientPaintStyle;
class ScrollStateSnapshot; class ScrollStateSnapshot;
using PaintStyle = RefPtr<SVGGradientPaintStyle>; using PaintStyle = RefPtr<SVGGradientPaintStyle>;
using PaintStyleOrColor = Variant<PaintStyle, Gfx::Color>;
using ScrollStateSnapshotByDisplayList = HashMap<NonnullRefPtr<DisplayList>, ScrollStateSnapshot>; using ScrollStateSnapshotByDisplayList = HashMap<NonnullRefPtr<DisplayList>, ScrollStateSnapshot>;
} }

View file

@ -216,8 +216,7 @@ void DisplayListPlayer::execute_impl(DisplayList& display_list, ScrollStateSnaps
else HANDLE_COMMAND(FillRectWithRoundedCorners, fill_rect_with_rounded_corners) else HANDLE_COMMAND(FillRectWithRoundedCorners, fill_rect_with_rounded_corners)
else HANDLE_COMMAND(FillPathUsingColor, fill_path_using_color) else HANDLE_COMMAND(FillPathUsingColor, fill_path_using_color)
else HANDLE_COMMAND(FillPathUsingPaintStyle, fill_path_using_paint_style) else HANDLE_COMMAND(FillPathUsingPaintStyle, fill_path_using_paint_style)
else HANDLE_COMMAND(StrokePathUsingColor, stroke_path_using_color) else HANDLE_COMMAND(StrokePath, stroke_path)
else HANDLE_COMMAND(StrokePathUsingPaintStyle, stroke_path_using_paint_style)
else HANDLE_COMMAND(DrawEllipse, draw_ellipse) else HANDLE_COMMAND(DrawEllipse, draw_ellipse)
else HANDLE_COMMAND(FillEllipse, fill_ellipse) else HANDLE_COMMAND(FillEllipse, fill_ellipse)
else HANDLE_COMMAND(DrawLine, draw_line) else HANDLE_COMMAND(DrawLine, draw_line)

View file

@ -57,8 +57,7 @@ private:
virtual void fill_rect_with_rounded_corners(FillRectWithRoundedCorners const&) = 0; virtual void fill_rect_with_rounded_corners(FillRectWithRoundedCorners const&) = 0;
virtual void fill_path_using_color(FillPathUsingColor const&) = 0; virtual void fill_path_using_color(FillPathUsingColor const&) = 0;
virtual void fill_path_using_paint_style(FillPathUsingPaintStyle const&) = 0; virtual void fill_path_using_paint_style(FillPathUsingPaintStyle const&) = 0;
virtual void stroke_path_using_color(StrokePathUsingColor const&) = 0; virtual void stroke_path(StrokePath const&) = 0;
virtual void stroke_path_using_paint_style(StrokePathUsingPaintStyle const&) = 0;
virtual void draw_ellipse(DrawEllipse const&) = 0; virtual void draw_ellipse(DrawEllipse const&) = 0;
virtual void fill_ellipse(FillEllipse const&) = 0; virtual void fill_ellipse(FillEllipse const&) = 0;
virtual void draw_line(DrawLine const&) = 0; virtual void draw_line(DrawLine const&) = 0;

View file

@ -147,14 +147,9 @@ void FillPathUsingPaintStyle::dump(StringBuilder& builder) const
builder.appendff("FillPathUsingPaintStyle"); builder.appendff("FillPathUsingPaintStyle");
} }
void StrokePathUsingColor::dump(StringBuilder& builder) const void StrokePath::dump(StringBuilder& builder) const
{ {
builder.appendff("StrokePathUsingColor"); builder.appendff("StrokePath");
}
void StrokePathUsingPaintStyle::dump(StringBuilder& builder) const
{
builder.appendff("StrokePathUsingPaintStyle");
} }
void DrawEllipse::dump(StringBuilder& builder) const void DrawEllipse::dump(StringBuilder& builder) const

View file

@ -246,7 +246,7 @@ struct FillPathUsingPaintStyle {
void dump(StringBuilder&) const; void dump(StringBuilder&) const;
}; };
struct StrokePathUsingColor { struct StrokePath {
Gfx::Path::CapStyle cap_style; Gfx::Path::CapStyle cap_style;
Gfx::Path::JoinStyle join_style; Gfx::Path::JoinStyle join_style;
float miter_limit; float miter_limit;
@ -254,7 +254,8 @@ struct StrokePathUsingColor {
float dash_offset; float dash_offset;
Gfx::IntRect path_bounding_rect; Gfx::IntRect path_bounding_rect;
Gfx::Path path; Gfx::Path path;
Color color; float opacity;
PaintStyleOrColor paint_style_or_color;
float thickness; float thickness;
Gfx::FloatPoint aa_translation; Gfx::FloatPoint aa_translation;
@ -268,29 +269,6 @@ struct StrokePathUsingColor {
void dump(StringBuilder&) const; void dump(StringBuilder&) const;
}; };
struct StrokePathUsingPaintStyle {
Gfx::Path::CapStyle cap_style;
Gfx::Path::JoinStyle join_style;
float miter_limit;
Vector<float> dash_array;
float dash_offset;
Gfx::IntRect path_bounding_rect;
Gfx::Path path;
PaintStyle paint_style;
float thickness;
float opacity = 1.0f;
Gfx::FloatPoint aa_translation;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
void translate_by(Gfx::IntPoint const& offset)
{
path_bounding_rect.translate_by(offset);
aa_translation.translate_by(offset.to_type<float>());
}
void dump(StringBuilder&) const;
};
struct DrawEllipse { struct DrawEllipse {
Gfx::IntRect rect; Gfx::IntRect rect;
Color color; Color color;
@ -508,8 +486,7 @@ using DisplayListCommand = Variant<
FillRectWithRoundedCorners, FillRectWithRoundedCorners,
FillPathUsingColor, FillPathUsingColor,
FillPathUsingPaintStyle, FillPathUsingPaintStyle,
StrokePathUsingColor, StrokePath,
StrokePathUsingPaintStyle,
DrawEllipse, DrawEllipse,
FillEllipse, FillEllipse,
DrawLine, DrawLine,

View file

@ -660,30 +660,20 @@ void DisplayListPlayerSkia::fill_path_using_paint_style(FillPathUsingPaintStyle
surface().canvas().drawPath(path, paint); surface().canvas().drawPath(path, paint);
} }
void DisplayListPlayerSkia::stroke_path_using_color(StrokePathUsingColor const& command) void DisplayListPlayerSkia::stroke_path(StrokePath const& command)
{ {
auto& canvas = surface().canvas(); auto path = to_skia_path(command.path);
path.offset(command.aa_translation.x(), command.aa_translation.y());
SkPaint paint; SkPaint paint;
if (command.paint_style_or_color.has<PaintStyle>()) {
auto const& paint_style = command.paint_style_or_color.get<PaintStyle>();
paint = paint_style_to_skia_paint(*paint_style, command.bounding_rect().to_type<float>());
paint.setAlphaf(command.opacity);
} else {
auto const& color = command.paint_style_or_color.get<Color>();
paint.setColor(to_skia_color(color));
}
paint.setAntiAlias(true); paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(command.thickness);
paint.setStrokeCap(to_skia_cap(command.cap_style));
paint.setStrokeJoin(to_skia_join(command.join_style));
paint.setColor(to_skia_color(command.color));
paint.setStrokeMiter(command.miter_limit);
paint.setPathEffect(SkDashPathEffect::Make(command.dash_array.data(), command.dash_array.size(), command.dash_offset));
auto path = to_skia_path(command.path);
path.offset(command.aa_translation.x(), command.aa_translation.y());
canvas.drawPath(path, paint);
}
void DisplayListPlayerSkia::stroke_path_using_paint_style(StrokePathUsingPaintStyle const& command)
{
auto path = to_skia_path(command.path);
path.offset(command.aa_translation.x(), command.aa_translation.y());
auto paint = paint_style_to_skia_paint(*command.paint_style, command.bounding_rect().to_type<float>());
paint.setAntiAlias(true);
paint.setAlphaf(command.opacity);
paint.setStyle(SkPaint::Style::kStroke_Style); paint.setStyle(SkPaint::Style::kStroke_Style);
paint.setStrokeWidth(command.thickness); paint.setStrokeWidth(command.thickness);
paint.setStrokeCap(to_skia_cap(command.cap_style)); paint.setStrokeCap(to_skia_cap(command.cap_style));

View file

@ -42,8 +42,7 @@ private:
void fill_rect_with_rounded_corners(FillRectWithRoundedCorners const&) override; void fill_rect_with_rounded_corners(FillRectWithRoundedCorners const&) override;
void fill_path_using_color(FillPathUsingColor const&) override; void fill_path_using_color(FillPathUsingColor const&) override;
void fill_path_using_paint_style(FillPathUsingPaintStyle const&) override; void fill_path_using_paint_style(FillPathUsingPaintStyle const&) override;
void stroke_path_using_color(StrokePathUsingColor const&) override; void stroke_path(StrokePath const&) override;
void stroke_path_using_paint_style(StrokePathUsingPaintStyle const&) override;
void draw_ellipse(DrawEllipse const&) override; void draw_ellipse(DrawEllipse const&) override;
void fill_ellipse(FillEllipse const&) override; void fill_ellipse(FillEllipse const&) override;
void draw_line(DrawLine const&) override; void draw_line(DrawLine const&) override;

View file

@ -99,12 +99,12 @@ void DisplayListRecorder::fill_path(FillPathUsingPaintStyleParams params)
}); });
} }
void DisplayListRecorder::stroke_path(StrokePathUsingColorParams params) void DisplayListRecorder::stroke_path(StrokePathParams params)
{ {
// Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing. // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing.
if (!params.thickness) if (!params.thickness)
return; return;
if (params.color.alpha() == 0) if (params.paint_style_or_color.has<Gfx::Color>() && params.paint_style_or_color.get<Gfx::Color>().alpha() == 0)
return; return;
auto aa_translation = params.translation.value_or(Gfx::FloatPoint {}); auto aa_translation = params.translation.value_or(Gfx::FloatPoint {});
auto path_bounding_rect = params.path.bounding_box().translated(aa_translation); auto path_bounding_rect = params.path.bounding_box().translated(aa_translation);
@ -113,7 +113,7 @@ void DisplayListRecorder::stroke_path(StrokePathUsingColorParams params)
auto path_bounding_int_rect = enclosing_int_rect(path_bounding_rect); auto path_bounding_int_rect = enclosing_int_rect(path_bounding_rect);
if (path_bounding_int_rect.is_empty()) if (path_bounding_int_rect.is_empty())
return; return;
APPEND(StrokePathUsingColor { APPEND(StrokePath {
.cap_style = params.cap_style, .cap_style = params.cap_style,
.join_style = params.join_style, .join_style = params.join_style,
.miter_limit = params.miter_limit, .miter_limit = params.miter_limit,
@ -121,35 +121,9 @@ void DisplayListRecorder::stroke_path(StrokePathUsingColorParams params)
.dash_offset = params.dash_offset, .dash_offset = params.dash_offset,
.path_bounding_rect = path_bounding_int_rect, .path_bounding_rect = path_bounding_int_rect,
.path = move(params.path), .path = move(params.path),
.color = params.color,
.thickness = params.thickness,
.aa_translation = aa_translation,
});
}
void DisplayListRecorder::stroke_path(StrokePathUsingPaintStyleParams params)
{
// Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing.
if (!params.thickness)
return;
auto aa_translation = params.translation.value_or(Gfx::FloatPoint {});
auto path_bounding_rect = params.path.bounding_box().translated(aa_translation);
// Increase path bounding box by `thickness` to account for stroke.
path_bounding_rect.inflate(params.thickness, params.thickness);
auto path_bounding_int_rect = enclosing_int_rect(path_bounding_rect);
if (path_bounding_int_rect.is_empty())
return;
APPEND(StrokePathUsingPaintStyle {
.cap_style = params.cap_style,
.join_style = params.join_style,
.miter_limit = params.miter_limit,
.dash_array = move(params.dash_array),
.dash_offset = params.dash_offset,
.path_bounding_rect = path_bounding_int_rect,
.path = move(params.path),
.paint_style = params.paint_style,
.thickness = params.thickness,
.opacity = params.opacity, .opacity = params.opacity,
.paint_style_or_color = params.paint_style_or_color,
.thickness = params.thickness,
.aa_translation = aa_translation, .aa_translation = aa_translation,
}); });
} }

View file

@ -56,32 +56,19 @@ public:
}; };
void fill_path(FillPathUsingPaintStyleParams params); void fill_path(FillPathUsingPaintStyleParams params);
struct StrokePathUsingColorParams { struct StrokePathParams {
Gfx::Path::CapStyle cap_style; Gfx::Path::CapStyle cap_style;
Gfx::Path::JoinStyle join_style; Gfx::Path::JoinStyle join_style;
float miter_limit; float miter_limit;
Vector<float> dash_array; Vector<float> dash_array;
float dash_offset; float dash_offset;
Gfx::Path path; Gfx::Path path;
Gfx::Color color; float opacity = 1.0f;
PaintStyleOrColor paint_style_or_color;
float thickness; float thickness;
Optional<Gfx::FloatPoint> translation = {}; Optional<Gfx::FloatPoint> translation = {};
}; };
void stroke_path(StrokePathUsingColorParams params); void stroke_path(StrokePathParams);
struct StrokePathUsingPaintStyleParams {
Gfx::Path::CapStyle cap_style;
Gfx::Path::JoinStyle join_style;
float miter_limit;
Vector<float> dash_array;
float dash_offset;
Gfx::Path path;
PaintStyle paint_style;
float thickness;
float opacity;
Optional<Gfx::FloatPoint> translation = {};
};
void stroke_path(StrokePathUsingPaintStyleParams params);
void draw_ellipse(Gfx::IntRect const& a_rect, Color color, int thickness); void draw_ellipse(Gfx::IntRect const& a_rect, Color color, int thickness);

View file

@ -15,6 +15,7 @@
#include <LibWeb/Page/Page.h> #include <LibWeb/Page/Page.h>
#include <LibWeb/Painting/DisplayListRecorder.h> #include <LibWeb/Painting/DisplayListRecorder.h>
#include <LibWeb/Painting/MediaPaintable.h> #include <LibWeb/Painting/MediaPaintable.h>
#include <LibWeb/Painting/PaintStyle.h>
#include <LibWeb/UIEvents/MouseButton.h> #include <LibWeb/UIEvents/MouseButton.h>
namespace Web::Painting { namespace Web::Painting {
@ -242,7 +243,7 @@ void MediaPaintable::paint_control_bar_speaker(DisplayListRecordingContext& cont
.dash_array = {}, .dash_array = {},
.dash_offset = 0, .dash_offset = 0,
.path = path, .path = path,
.color = speaker_button_color, .paint_style_or_color = speaker_button_color,
.thickness = 1, .thickness = 1,
}); });

View file

@ -904,7 +904,7 @@ void paint_text_decoration(DisplayListRecordingContext& context, TextPaintable c
.dash_array = {}, .dash_array = {},
.dash_offset = 0, .dash_offset = 0,
.path = build_triangle_wave_path(line_start_point.to_type<int>(), line_end_point.to_type<int>(), amplitude), .path = build_triangle_wave_path(line_start_point.to_type<int>(), line_end_point.to_type<int>(), amplitude),
.color = line_color, .paint_style_or_color = line_color,
.thickness = static_cast<float>(device_line_thickness.value()), .thickness = static_cast<float>(device_line_thickness.value()),
}); });
break; break;

View file

@ -176,9 +176,9 @@ void SVGPathPaintable::paint(DisplayListRecordingContext& context, PaintPhase ph
.dash_array = stroke_dasharray, .dash_array = stroke_dasharray,
.dash_offset = stroke_dashoffset, .dash_offset = stroke_dashoffset,
.path = path, .path = path,
.paint_style = *paint_style,
.thickness = stroke_thickness,
.opacity = stroke_opacity, .opacity = stroke_opacity,
.paint_style_or_color = *paint_style,
.thickness = stroke_thickness,
.translation = offset, .translation = offset,
}); });
} else if (auto stroke_color = graphics_element.stroke_color(); stroke_color.has_value()) { } else if (auto stroke_color = graphics_element.stroke_color(); stroke_color.has_value()) {
@ -189,7 +189,7 @@ void SVGPathPaintable::paint(DisplayListRecordingContext& context, PaintPhase ph
.dash_array = stroke_dasharray, .dash_array = stroke_dasharray,
.dash_offset = stroke_dashoffset, .dash_offset = stroke_dashoffset,
.path = path, .path = path,
.color = stroke_color->with_opacity(stroke_opacity), .paint_style_or_color = stroke_color->with_opacity(stroke_opacity),
.thickness = stroke_thickness, .thickness = stroke_thickness,
.translation = offset, .translation = offset,
}); });