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/Variant.h>
#include <LibGfx/Forward.h>
#include <LibIPC/Forward.h>
#include <LibJS/Forward.h>
@ -42,6 +43,7 @@ class DisplayListRecorder;
class SVGGradientPaintStyle;
class ScrollStateSnapshot;
using PaintStyle = RefPtr<SVGGradientPaintStyle>;
using PaintStyleOrColor = Variant<PaintStyle, Gfx::Color>;
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(FillPathUsingColor, fill_path_using_color)
else HANDLE_COMMAND(FillPathUsingPaintStyle, fill_path_using_paint_style)
else HANDLE_COMMAND(StrokePathUsingColor, stroke_path_using_color)
else HANDLE_COMMAND(StrokePathUsingPaintStyle, stroke_path_using_paint_style)
else HANDLE_COMMAND(StrokePath, stroke_path)
else HANDLE_COMMAND(DrawEllipse, draw_ellipse)
else HANDLE_COMMAND(FillEllipse, fill_ellipse)
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_path_using_color(FillPathUsingColor 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_using_paint_style(StrokePathUsingPaintStyle const&) = 0;
virtual void stroke_path(StrokePath const&) = 0;
virtual void draw_ellipse(DrawEllipse const&) = 0;
virtual void fill_ellipse(FillEllipse const&) = 0;
virtual void draw_line(DrawLine const&) = 0;

View file

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

View file

@ -246,7 +246,7 @@ struct FillPathUsingPaintStyle {
void dump(StringBuilder&) const;
};
struct StrokePathUsingColor {
struct StrokePath {
Gfx::Path::CapStyle cap_style;
Gfx::Path::JoinStyle join_style;
float miter_limit;
@ -254,7 +254,8 @@ struct StrokePathUsingColor {
float dash_offset;
Gfx::IntRect path_bounding_rect;
Gfx::Path path;
Color color;
float opacity;
PaintStyleOrColor paint_style_or_color;
float thickness;
Gfx::FloatPoint aa_translation;
@ -268,29 +269,6 @@ struct StrokePathUsingColor {
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 {
Gfx::IntRect rect;
Color color;
@ -508,8 +486,7 @@ using DisplayListCommand = Variant<
FillRectWithRoundedCorners,
FillPathUsingColor,
FillPathUsingPaintStyle,
StrokePathUsingColor,
StrokePathUsingPaintStyle,
StrokePath,
DrawEllipse,
FillEllipse,
DrawLine,

View file

@ -660,30 +660,20 @@ void DisplayListPlayerSkia::fill_path_using_paint_style(FillPathUsingPaintStyle
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;
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);
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.setStyle(SkPaint::Style::kStroke_Style);
paint.setStrokeWidth(command.thickness);
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_path_using_color(FillPathUsingColor const&) override;
void fill_path_using_paint_style(FillPathUsingPaintStyle const&) override;
void stroke_path_using_color(StrokePathUsingColor const&) override;
void stroke_path_using_paint_style(StrokePathUsingPaintStyle const&) override;
void stroke_path(StrokePath const&) override;
void draw_ellipse(DrawEllipse const&) override;
void fill_ellipse(FillEllipse 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.
if (!params.thickness)
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;
auto aa_translation = params.translation.value_or(Gfx::FloatPoint {});
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);
if (path_bounding_int_rect.is_empty())
return;
APPEND(StrokePathUsingColor {
APPEND(StrokePath {
.cap_style = params.cap_style,
.join_style = params.join_style,
.miter_limit = params.miter_limit,
@ -121,35 +121,9 @@ void DisplayListRecorder::stroke_path(StrokePathUsingColorParams params)
.dash_offset = params.dash_offset,
.path_bounding_rect = path_bounding_int_rect,
.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,
.paint_style_or_color = params.paint_style_or_color,
.thickness = params.thickness,
.aa_translation = aa_translation,
});
}

View file

@ -56,32 +56,19 @@ public:
};
void fill_path(FillPathUsingPaintStyleParams params);
struct StrokePathUsingColorParams {
struct StrokePathParams {
Gfx::Path::CapStyle cap_style;
Gfx::Path::JoinStyle join_style;
float miter_limit;
Vector<float> dash_array;
float dash_offset;
Gfx::Path path;
Gfx::Color color;
float opacity = 1.0f;
PaintStyleOrColor paint_style_or_color;
float thickness;
Optional<Gfx::FloatPoint> translation = {};
};
void stroke_path(StrokePathUsingColorParams params);
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 stroke_path(StrokePathParams);
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/Painting/DisplayListRecorder.h>
#include <LibWeb/Painting/MediaPaintable.h>
#include <LibWeb/Painting/PaintStyle.h>
#include <LibWeb/UIEvents/MouseButton.h>
namespace Web::Painting {
@ -242,7 +243,7 @@ void MediaPaintable::paint_control_bar_speaker(DisplayListRecordingContext& cont
.dash_array = {},
.dash_offset = 0,
.path = path,
.color = speaker_button_color,
.paint_style_or_color = speaker_button_color,
.thickness = 1,
});

View file

@ -904,7 +904,7 @@ void paint_text_decoration(DisplayListRecordingContext& context, TextPaintable c
.dash_array = {},
.dash_offset = 0,
.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()),
});
break;

View file

@ -176,9 +176,9 @@ void SVGPathPaintable::paint(DisplayListRecordingContext& context, PaintPhase ph
.dash_array = stroke_dasharray,
.dash_offset = stroke_dashoffset,
.path = path,
.paint_style = *paint_style,
.thickness = stroke_thickness,
.opacity = stroke_opacity,
.paint_style_or_color = *paint_style,
.thickness = stroke_thickness,
.translation = offset,
});
} 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_offset = stroke_dashoffset,
.path = path,
.color = stroke_color->with_opacity(stroke_opacity),
.paint_style_or_color = stroke_color->with_opacity(stroke_opacity),
.thickness = stroke_thickness,
.translation = offset,
});