LibGfx: Move Gfx::Painter::WindingRule => Gfx::WindingRule

This commit is contained in:
Andreas Kling 2024-06-05 10:21:28 +02:00 committed by Andreas Kling
parent 254d040ff4
commit 57906a4e1b
Notes: sideshowbarker 2024-07-17 01:53:23 +09:00
16 changed files with 68 additions and 55 deletions

View File

@ -9,6 +9,7 @@
#include <LibGfx/Painter.h>
#include <LibGfx/Path.h>
#include <LibGfx/Quad.h>
#include <LibGfx/WindingRule.h>
namespace Gfx {
@ -33,8 +34,8 @@ public:
draw_line(line.a(), line.b(), color, thickness, style, alternate_color, line_length_mode);
}
void fill_path(Path const&, Color, Painter::WindingRule rule = Painter::WindingRule::Nonzero);
void fill_path(Path const&, PaintStyle const& paint_style, float opacity = 1.0f, Painter::WindingRule rule = Painter::WindingRule::Nonzero);
void fill_path(Path const&, Color, WindingRule rule = WindingRule::Nonzero);
void fill_path(Path const&, PaintStyle const& paint_style, float opacity = 1.0f, WindingRule rule = WindingRule::Nonzero);
void stroke_path(Path const&, Color, float thickness);
void stroke_path(Path const&, PaintStyle const& paint_style, float thickness, float opacity = 1.0f);

View File

@ -99,13 +99,13 @@ EdgeFlagPathRasterizer<SamplesPerPixel>::EdgeFlagPathRasterizer(IntSize size)
}
template<unsigned SamplesPerPixel>
void EdgeFlagPathRasterizer<SamplesPerPixel>::fill(Painter& painter, Path const& path, Color color, Painter::WindingRule winding_rule, FloatPoint offset)
void EdgeFlagPathRasterizer<SamplesPerPixel>::fill(Painter& painter, Path const& path, Color color, WindingRule winding_rule, FloatPoint offset)
{
fill_internal(painter, path, color, winding_rule, offset);
}
template<unsigned SamplesPerPixel>
void EdgeFlagPathRasterizer<SamplesPerPixel>::fill(Painter& painter, Path const& path, PaintStyle const& style, float opacity, Painter::WindingRule winding_rule, FloatPoint offset)
void EdgeFlagPathRasterizer<SamplesPerPixel>::fill(Painter& painter, Path const& path, PaintStyle const& style, float opacity, WindingRule winding_rule, FloatPoint offset)
{
style.paint(enclosing_int_rect(path.bounding_box()), [&](PaintStyle::SamplerFunction sampler) {
if (opacity == 0.0f)
@ -122,7 +122,7 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::fill(Painter& painter, Path const&
}
template<unsigned SamplesPerPixel>
void EdgeFlagPathRasterizer<SamplesPerPixel>::fill_internal(Painter& painter, Path const& path, auto color_or_function, Painter::WindingRule winding_rule, FloatPoint offset)
void EdgeFlagPathRasterizer<SamplesPerPixel>::fill_internal(Painter& painter, Path const& path, auto color_or_function, WindingRule winding_rule, FloatPoint offset)
{
auto bounding_box = enclosing_int_rect(path.bounding_box().translated(offset));
auto dest_rect = bounding_box.translated(painter.translation());
@ -187,7 +187,7 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::fill_internal(Painter& painter, Pa
Detail::Edge* active_edges = nullptr;
if (winding_rule == Painter::WindingRule::EvenOdd) {
if (winding_rule == WindingRule::EvenOdd) {
auto plot_edge = [&](Detail::Edge& edge, int start_subpixel_y, int end_subpixel_y, EdgeExtent& edge_extent) {
for_each_sample(edge, start_subpixel_y, end_subpixel_y, edge_extent, [&](int xi, int, SampleType sample) {
m_scanline[xi] ^= sample;
@ -196,10 +196,10 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::fill_internal(Painter& painter, Pa
for (int scanline = min_scanline; scanline <= max_scanline; scanline++) {
auto edge_extent = empty_edge_extent();
active_edges = plot_edges_for_scanline(scanline, plot_edge, edge_extent, active_edges);
write_scanline<Painter::WindingRule::EvenOdd>(painter, scanline, edge_extent, color_or_function);
write_scanline<WindingRule::EvenOdd>(painter, scanline, edge_extent, color_or_function);
}
} else {
VERIFY(winding_rule == Painter::WindingRule::Nonzero);
VERIFY(winding_rule == WindingRule::Nonzero);
// Only allocate the winding buffer if needed.
// NOTE: non-zero fills are a fair bit less efficient. So if you can do an even-odd fill do that :^)
if (m_windings.is_empty())
@ -214,7 +214,7 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::fill_internal(Painter& painter, Pa
for (int scanline = min_scanline; scanline <= max_scanline; scanline++) {
auto edge_extent = empty_edge_extent();
active_edges = plot_edges_for_scanline(scanline, plot_edge, edge_extent, active_edges);
write_scanline<Painter::WindingRule::Nonzero>(painter, scanline, edge_extent, color_or_function);
write_scanline<WindingRule::Nonzero>(painter, scanline, edge_extent, color_or_function);
}
}
}
@ -345,10 +345,10 @@ auto EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_non_zero_scanline(EdgeE
}
template<unsigned SamplesPerPixel>
template<Painter::WindingRule WindingRule, typename Callback>
template<WindingRule WindingRule, typename Callback>
auto EdgeFlagPathRasterizer<SamplesPerPixel>::accumulate_scanline(EdgeExtent edge_extent, auto init, Callback callback)
{
if constexpr (WindingRule == Painter::WindingRule::EvenOdd)
if constexpr (WindingRule == WindingRule::EvenOdd)
return accumulate_even_odd_scanline(edge_extent, init, callback);
else
return accumulate_non_zero_scanline(edge_extent, init, callback);
@ -374,7 +374,7 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::fast_fill_solid_color_span(ARGB32*
}
template<unsigned SamplesPerPixel>
template<Painter::WindingRule WindingRule>
template<WindingRule WindingRule>
FLATTEN __attribute__((hot)) void EdgeFlagPathRasterizer<SamplesPerPixel>::write_scanline(Painter& painter, int scanline, EdgeExtent edge_extent, auto& color_or_function)
{
// Handle scanline clipping.
@ -383,7 +383,7 @@ FLATTEN __attribute__((hot)) void EdgeFlagPathRasterizer<SamplesPerPixel>::write
if (clipped_extent.min_x > clipped_extent.max_x) {
// Fully clipped. Unfortunately we still need to zero the scanline data.
edge_extent.memset_extent(m_scanline.data(), 0);
if constexpr (WindingRule == Painter::WindingRule::Nonzero)
if constexpr (WindingRule == WindingRule::Nonzero)
edge_extent.memset_extent(m_windings.data(), 0);
return;
}
@ -445,19 +445,19 @@ void Painter::fill_path(Path const& path, Color color, WindingRule winding_rule)
rasterizer.fill(*this, path, color, winding_rule);
}
void Painter::fill_path(Path const& path, PaintStyle const& paint_style, float opacity, Painter::WindingRule winding_rule)
void Painter::fill_path(Path const& path, PaintStyle const& paint_style, float opacity, WindingRule winding_rule)
{
EdgeFlagPathRasterizer<8> rasterizer(path_bounds(path));
rasterizer.fill(*this, path, paint_style, opacity, winding_rule);
}
void AntiAliasingPainter::fill_path(Path const& path, Color color, Painter::WindingRule winding_rule)
void AntiAliasingPainter::fill_path(Path const& path, Color color, WindingRule winding_rule)
{
EdgeFlagPathRasterizer<32> rasterizer(path_bounds(path));
rasterizer.fill(m_underlying_painter, path, color, winding_rule, m_transform.translation());
}
void AntiAliasingPainter::fill_path(Path const& path, PaintStyle const& paint_style, float opacity, Painter::WindingRule winding_rule)
void AntiAliasingPainter::fill_path(Path const& path, PaintStyle const& paint_style, float opacity, WindingRule winding_rule)
{
EdgeFlagPathRasterizer<32> rasterizer(path_bounds(path));
rasterizer.fill(m_underlying_painter, path, paint_style, opacity, winding_rule, m_transform.translation());

View File

@ -145,8 +145,8 @@ class EdgeFlagPathRasterizer {
public:
EdgeFlagPathRasterizer(IntSize);
void fill(Painter&, Path const&, Color, Painter::WindingRule, FloatPoint offset = {});
void fill(Painter&, Path const&, PaintStyle const&, float opacity, Painter::WindingRule, FloatPoint offset = {});
void fill(Painter&, Path const&, Color, WindingRule, FloatPoint offset = {});
void fill(Painter&, Path const&, PaintStyle const&, float opacity, WindingRule, FloatPoint offset = {});
private:
using SubpixelSample = Detail::Sample<SamplesPerPixel>;
@ -172,16 +172,16 @@ private:
}
};
void fill_internal(Painter&, Path const&, auto color_or_function, Painter::WindingRule, FloatPoint offset);
void fill_internal(Painter&, Path const&, auto color_or_function, WindingRule, FloatPoint offset);
Detail::Edge* plot_edges_for_scanline(int scanline, auto plot_edge, EdgeExtent&, Detail::Edge* active_edges = nullptr);
template<Painter::WindingRule>
template<WindingRule>
FLATTEN void write_scanline(Painter&, int scanline, EdgeExtent, auto& color_or_function);
Color scanline_color(int scanline, int offset, u8 alpha, auto& color_or_function);
void write_pixel(BitmapFormat format, ARGB32* scanline_ptr, int scanline, int offset, SampleType sample, auto& color_or_function);
void fast_fill_solid_color_span(ARGB32* scanline_ptr, int start, int end, Color color);
template<Painter::WindingRule, typename Callback>
template<WindingRule, typename Callback>
auto accumulate_scanline(EdgeExtent, auto, Callback);
auto accumulate_even_odd_scanline(EdgeExtent, auto, auto sample_callback);
auto accumulate_non_zero_scanline(EdgeExtent, auto, auto sample_callback);
@ -196,10 +196,10 @@ private:
WindingCounts winding;
};
template<Painter::WindingRule WindingRule>
template<WindingRule WindingRule>
constexpr auto initial_acc() const
{
if constexpr (WindingRule == Painter::WindingRule::EvenOdd)
if constexpr (WindingRule == WindingRule::EvenOdd)
return SampleType {};
else
return NonZeroAcc {};

View File

@ -483,10 +483,10 @@ void TinyVGDecodedImageData::draw_transformed(Painter& painter, AffineTransform
auto fill_path = draw_path;
fill_path.close_all_subpaths();
command.fill->visit(
[&](Color color) { aa_painter.fill_path(fill_path, color, Painter::WindingRule::EvenOdd); },
[&](Color color) { aa_painter.fill_path(fill_path, color, WindingRule::EvenOdd); },
[&](NonnullRefPtr<SVGGradientPaintStyle> style) {
const_cast<SVGGradientPaintStyle&>(*style).set_gradient_transform(transform);
aa_painter.fill_path(fill_path, style, 1.0f, Painter::WindingRule::EvenOdd);
aa_painter.fill_path(fill_path, style, 1.0f, WindingRule::EvenOdd);
});
}
if (command.stroke.has_value()) {

View File

@ -25,6 +25,7 @@
#include <LibGfx/TextDirection.h>
#include <LibGfx/TextElision.h>
#include <LibGfx/TextWrapping.h>
#include <LibGfx/WindingRule.h>
namespace Gfx {
@ -104,11 +105,6 @@ public:
void stroke_path(Path const&, Color, int thickness);
enum class WindingRule {
Nonzero,
EvenOdd,
};
void fill_path(Path const&, Color, WindingRule rule = WindingRule::Nonzero);
void fill_path(Path const&, PaintStyle const& paint_style, float opacity = 1.0f, WindingRule rule = WindingRule::Nonzero);

View File

@ -13,7 +13,7 @@ namespace Gfx {
struct ClipPath {
Path path;
Painter::WindingRule winding_rule;
WindingRule winding_rule;
};
class PathClipper {

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
namespace Gfx {
enum class WindingRule {
Nonzero,
EvenOdd,
};
}

View File

@ -92,7 +92,7 @@ Gfx::Path CanvasRenderingContext2D::rect_path(float x, float y, float width, flo
void CanvasRenderingContext2D::fill_rect(float x, float y, float width, float height)
{
return fill_internal(rect_path(x, y, width, height), Gfx::Painter::WindingRule::EvenOdd);
return fill_internal(rect_path(x, y, width, height), Gfx::WindingRule::EvenOdd);
}
void CanvasRenderingContext2D::clear_rect(float x, float y, float width, float height)
@ -255,7 +255,7 @@ Gfx::Path CanvasRenderingContext2D::text_path(StringView text, float x, float y,
void CanvasRenderingContext2D::fill_text(StringView text, float x, float y, Optional<double> max_width)
{
fill_internal(text_path(text, x, y, max_width), Gfx::Painter::WindingRule::Nonzero);
fill_internal(text_path(text, x, y, max_width), Gfx::WindingRule::Nonzero);
}
void CanvasRenderingContext2D::stroke_text(StringView text, float x, float y, Optional<double> max_width)
@ -292,17 +292,17 @@ void CanvasRenderingContext2D::stroke(Path2D const& path)
stroke_internal(transformed_path);
}
static Gfx::Painter::WindingRule parse_fill_rule(StringView fill_rule)
static Gfx::WindingRule parse_fill_rule(StringView fill_rule)
{
if (fill_rule == "evenodd"sv)
return Gfx::Painter::WindingRule::EvenOdd;
return Gfx::WindingRule::EvenOdd;
if (fill_rule == "nonzero"sv)
return Gfx::Painter::WindingRule::Nonzero;
return Gfx::WindingRule::Nonzero;
dbgln("Unrecognized fillRule for CRC2D.fill() - this problem goes away once we pass an enum instead of a string");
return Gfx::Painter::WindingRule::Nonzero;
return Gfx::WindingRule::Nonzero;
}
void CanvasRenderingContext2D::fill_internal(Gfx::Path const& path, Gfx::Painter::WindingRule winding_rule)
void CanvasRenderingContext2D::fill_internal(Gfx::Path const& path, Gfx::WindingRule winding_rule)
{
draw_clipped([&, this](auto& painter) mutable {
auto path_to_fill = path;
@ -537,7 +537,7 @@ CanvasRenderingContext2D::PreparedText CanvasRenderingContext2D::prepare_text(By
return prepared_text;
}
void CanvasRenderingContext2D::clip_internal(Gfx::Path& path, Gfx::Painter::WindingRule winding_rule)
void CanvasRenderingContext2D::clip_internal(Gfx::Path& path, Gfx::WindingRule winding_rule)
{
// FIXME: This should calculate the new clip path by intersecting the given path with the current one.
// See: https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-clip-dev

View File

@ -144,8 +144,8 @@ private:
Gfx::Path text_path(StringView text, float x, float y, Optional<double> max_width);
void stroke_internal(Gfx::Path const&);
void fill_internal(Gfx::Path const&, Gfx::Painter::WindingRule);
void clip_internal(Gfx::Path&, Gfx::Painter::WindingRule);
void fill_internal(Gfx::Path const&, Gfx::WindingRule);
void clip_internal(Gfx::Path&, Gfx::WindingRule);
JS::NonnullGCPtr<HTMLCanvasElement> m_element;
OwnPtr<Gfx::Painter> m_painter;

View File

@ -48,7 +48,7 @@ CommandResult AffineCommandExecutorCPU::fill_rect(FillRect const& command)
{
// FIXME: Somehow support clip_paths?
auto path = rect_path(command.rect.to_type<float>()).copy_transformed(stacking_context().transform);
aa_painter().fill_path(path, command.color, Gfx::Painter::WindingRule::EvenOdd);
aa_painter().fill_path(path, command.color, Gfx::WindingRule::EvenOdd);
return CommandResult::Continue;
}
@ -175,7 +175,7 @@ CommandResult AffineCommandExecutorCPU::fill_rect_with_rounded_corners(FillRectW
}
path = path.copy_transformed(stacking_context().transform);
aa_painter().fill_path(path, command.color, Gfx::Painter::WindingRule::EvenOdd);
aa_painter().fill_path(path, command.color, Gfx::WindingRule::EvenOdd);
return CommandResult::Continue;
}

View File

@ -191,7 +191,7 @@ void paint_border(RecordingPainter& painter, BorderEdge edge, DevicePixelRect co
path.close_all_subpaths();
painter.fill_path({ .path = path,
.color = color,
.winding_rule = Gfx::Painter::WindingRule::EvenOdd });
.winding_rule = Gfx::WindingRule::EvenOdd });
path.clear();
}
};

View File

@ -183,7 +183,7 @@ struct FillPathUsingColor {
Gfx::IntRect path_bounding_rect;
Gfx::Path path;
Color color;
Gfx::Painter::WindingRule winding_rule;
Gfx::WindingRule winding_rule;
Gfx::FloatPoint aa_translation;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
@ -199,7 +199,7 @@ struct FillPathUsingPaintStyle {
Gfx::IntRect path_bounding_rect;
Gfx::Path path;
NonnullRefPtr<Gfx::PaintStyle> paint_style;
Gfx::Painter::WindingRule winding_rule;
Gfx::WindingRule winding_rule;
float opacity;
Gfx::FloatPoint aa_translation;

View File

@ -88,7 +88,7 @@ void MarkerPaintable::paint(PaintContext& context, PaintPhase phase) const
path.line_to({ left + sin_60_deg * (right - left), (top + bottom) / 2 });
path.line_to({ left, bottom });
path.close();
context.recording_painter().fill_path({ .path = path, .color = color, .winding_rule = Gfx::Painter::WindingRule::EvenOdd });
context.recording_painter().fill_path({ .path = path, .color = color, .winding_rule = Gfx::WindingRule::EvenOdd });
break;
}
case CSS::ListStyleType::DisclosureOpen: {
@ -102,7 +102,7 @@ void MarkerPaintable::paint(PaintContext& context, PaintPhase phase) const
path.line_to({ right, top });
path.line_to({ (left + right) / 2, top + sin_60_deg * (bottom - top) });
path.close();
context.recording_painter().fill_path({ .path = path, .color = color, .winding_rule = Gfx::Painter::WindingRule::EvenOdd });
context.recording_painter().fill_path({ .path = path, .color = color, .winding_rule = Gfx::WindingRule::EvenOdd });
break;
}
case CSS::ListStyleType::Decimal:

View File

@ -54,7 +54,7 @@ void MediaPaintable::fill_triangle(RecordingPainter& painter, Gfx::IntPoint loca
painter.fill_path({
.path = path,
.color = color,
.winding_rule = Gfx::Painter::WindingRule::EvenOdd,
.winding_rule = Gfx::WindingRule::EvenOdd,
});
}
@ -227,7 +227,7 @@ void MediaPaintable::paint_control_bar_speaker(PaintContext& context, HTML::HTML
path.line_to(device_point(0, 11));
path.line_to(device_point(0, 4));
path.close();
context.recording_painter().fill_path({ .path = path, .color = speaker_button_color, .winding_rule = Gfx::Painter::WindingRule::EvenOdd });
context.recording_painter().fill_path({ .path = path, .color = speaker_button_color, .winding_rule = Gfx::WindingRule::EvenOdd });
path.clear();
path.move_to(device_point(13, 3));

View File

@ -49,7 +49,7 @@ public:
struct FillPathUsingColorParams {
Gfx::Path path;
Gfx::Color color;
Gfx::Painter::WindingRule winding_rule = Gfx::Painter::WindingRule::EvenOdd;
Gfx::WindingRule winding_rule = Gfx::WindingRule::EvenOdd;
Optional<Gfx::FloatPoint> translation = {};
};
void fill_path(FillPathUsingColorParams params);
@ -57,7 +57,7 @@ public:
struct FillPathUsingPaintStyleParams {
Gfx::Path path;
NonnullRefPtr<Gfx::PaintStyle> paint_style;
Gfx::Painter::WindingRule winding_rule = Gfx::Painter::WindingRule::EvenOdd;
Gfx::WindingRule winding_rule = Gfx::WindingRule::EvenOdd;
float opacity;
Optional<Gfx::FloatPoint> translation = {};
};

View File

@ -38,13 +38,13 @@ TraversalDecision SVGPathPaintable::hit_test(CSSPixelPoint position, HitTestType
return SVGGraphicsPaintable::hit_test(position, type, callback);
}
static Gfx::Painter::WindingRule to_gfx_winding_rule(SVG::FillRule fill_rule)
static Gfx::WindingRule to_gfx_winding_rule(SVG::FillRule fill_rule)
{
switch (fill_rule) {
case SVG::FillRule::Nonzero:
return Gfx::Painter::WindingRule::Nonzero;
return Gfx::WindingRule::Nonzero;
case SVG::FillRule::Evenodd:
return Gfx::Painter::WindingRule::EvenOdd;
return Gfx::WindingRule::EvenOdd;
default:
VERIFY_NOT_REACHED();
}