LibWeb/Painting: Paint triangle waves using Skia

This has been left unimplemented since we switched to the Skia renderer.
Now `text-decoration-style: wavy` actually paints a wavy line. :^)

We had a text-decoration test, but it only checked `solid` lines, so
I've replaced it with a modified version of the old test page from
Serenity, without the blink option, and with some thickness parameters.

I did experiment with using a `SkPath1DPathEffect` to make it repeat the
pattern for us, but I couldn't make it look good at all.
This commit is contained in:
Sam Atkins 2025-02-27 19:23:27 +00:00
commit d8a73a8165
Notes: github-actions[bot] 2025-02-28 16:35:17 +00:00
3 changed files with 81 additions and 30 deletions

View file

@ -816,8 +816,77 @@ void DisplayListPlayerSkia::paint_conic_gradient(PaintConicGradient const& comma
surface().canvas().drawRect(to_skia_rect(rect), paint); surface().canvas().drawRect(to_skia_rect(rect), paint);
} }
void DisplayListPlayerSkia::draw_triangle_wave(DrawTriangleWave const&) void DisplayListPlayerSkia::draw_triangle_wave(DrawTriangleWave const& command)
{ {
// Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing.
if (!command.thickness)
return;
// FIXME: Support more than horizontal waves
if (command.p1.y() != command.p2.y()) {
dbgln("FIXME: Support more than horizontal waves");
return;
}
auto& canvas = surface().canvas();
auto from = to_skia_point(command.p1);
auto to = to_skia_point(command.p2);
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(command.thickness);
paint.setStrokeJoin(SkPaint::kRound_Join);
paint.setStrokeCap(SkPaint::kRound_Cap);
paint.setColor(to_skia_color(command.color));
SkPath path;
path.moveTo(from);
float const wavelength = command.amplitude * 2.0f;
float const half_wavelength = command.amplitude;
float const quarter_wavelength = command.amplitude / 2.0f;
auto position = from;
auto remaining = abs(to.x() - position.x());
while (remaining > wavelength) {
// Draw a whole wave
path.lineTo(position.x() + quarter_wavelength, position.y() - quarter_wavelength);
path.lineTo(position.x() + quarter_wavelength + half_wavelength, position.y() + quarter_wavelength);
path.lineTo(position.x() + wavelength, position.y());
position.offset(wavelength, 0);
remaining = abs(to.x() - position.x());
}
// Up
if (remaining > quarter_wavelength) {
path.lineTo(position.x() + quarter_wavelength, position.y() - quarter_wavelength);
position.offset(quarter_wavelength, 0);
remaining = abs(to.x() - position.x());
} else if (remaining >= 1) {
auto fraction = remaining / quarter_wavelength;
path.lineTo(position.x() + (fraction * quarter_wavelength), position.y() - (fraction * quarter_wavelength));
remaining = 0;
}
// Down
if (remaining > half_wavelength) {
path.lineTo(position.x() + half_wavelength, position.y() + quarter_wavelength);
position.offset(half_wavelength, 0);
remaining = abs(to.x() - position.x());
} else if (remaining >= 1) {
auto fraction = remaining / half_wavelength;
path.lineTo(position.x() + (fraction * half_wavelength), position.y() - quarter_wavelength + (fraction * half_wavelength));
remaining = 0;
}
// Back to middle
if (remaining >= 1) {
auto fraction = remaining / quarter_wavelength;
path.lineTo(position.x() + (fraction * quarter_wavelength), position.y() + ((1 - fraction) * quarter_wavelength));
}
canvas.drawPath(path, paint);
} }
void DisplayListPlayerSkia::add_rounded_rect_clip(AddRoundedRectClip const& command) void DisplayListPlayerSkia::add_rounded_rect_clip(AddRoundedRectClip const& command)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before After
Before After

View file

@ -3,37 +3,19 @@
<head> <head>
<link rel="match" href="../expected/text-decorations-ref.html" /> <link rel="match" href="../expected/text-decorations-ref.html" />
<style> <style>
.underline { .overline { text-decoration: wavy blue overline 2px; }
text-decoration: underline; .underline { text-decoration: red underline double; }
text-decoration-thickness: 2px; .strikethrough { text-decoration: line-through dotted green 5px; }
} .current-color { color: #8B4513; text-decoration: underline; }
.overboard { text-decoration: double overline underline line-through magenta; }
.overline {
text-decoration: overline;
text-decoration-thickness: 2px;
}
.line-through {
text-decoration: line-through;
text-decoration-thickness: 2px;
}
.underline-overline {
text-decoration: underline overline;
text-decoration-thickness: 2px;
}
.underline-line-through {
text-decoration: underline line-through;
text-decoration-thickness: 2px;
}
</style> </style>
</head> </head>
<body> <body>
<p class="underline">Hello</p> <p class="overline">Overline</p>
<p class="overline">Hello</p> <p class="underline">Underline</p>
<p class="line-through">Hello</p> <p class="strikethrough">Wombling</p>
<p class="underline-overline">Hello</p> <p class="blink">FREE!</p>
<p class="underline-line-through">Hello</p> <p class="current-color">This underline should match the text color</p>
<p class="overboard">This should have an underline, overline and line-through, all in glorious magenta.</p>
</body> </body>
</html> </html>