diff --git a/Userland/Libraries/LibGfx/AntiAliasingPainter.cpp b/Userland/Libraries/LibGfx/AntiAliasingPainter.cpp index ef37f29b3c..efe4133151 100644 --- a/Userland/Libraries/LibGfx/AntiAliasingPainter.cpp +++ b/Userland/Libraries/LibGfx/AntiAliasingPainter.cpp @@ -50,11 +50,6 @@ void AntiAliasingPainter::draw_anti_aliased_line(FloatPoint actual_from, FloatPo // Axis-aligned lines: if (mapped_from.y() == mapped_to.y()) { auto start_point = (mapped_from.x() < mapped_to.x() ? mapped_from : mapped_to).translated(0, -int_thickness / 2); - if constexpr (path_hacks == FixmeEnableHacksForBetterPathPainting::Yes) { - // FIXME: SVG fill_path() hack: - // SVG asks for 1px scanlines at floating point y values, if they're not snapped to a pixel they look faint. - start_point.set_y(floorf(start_point.y())); - } return fill_rect(Gfx::FloatRect(start_point, { length, thickness }), color); } if (mapped_from.x() == mapped_to.x()) { @@ -213,9 +208,20 @@ void AntiAliasingPainter::draw_line(FloatPoint actual_from, FloatPoint actual_to draw_anti_aliased_line(actual_from, actual_to, color, thickness, style, alternate_color, line_length_mode); } -void AntiAliasingPainter::fill_path(Path& path, Color color, Painter::WindingRule rule) +// FIXME: In the fill_paths() m_transform.translation() throws away any other transforms +// this currently does not matter -- but may in future. + +void AntiAliasingPainter::fill_path(Path const& path, Color color, Painter::WindingRule rule) { - Detail::fill_path(*this, path, color, rule); + Detail::fill_path( + m_underlying_painter, path, [=](IntPoint) { return color; }, rule, m_transform.translation()); +} + +void AntiAliasingPainter::fill_path(Path const& path, PaintStyle const& paint_style, Painter::WindingRule rule) +{ + paint_style.paint(enclosing_int_rect(path.bounding_box()), [&](PaintStyle::SamplerFunction sampler) { + Detail::fill_path(m_underlying_painter, path, move(sampler), rule, m_transform.translation()); + }); } void AntiAliasingPainter::stroke_path(Path const& path, Color color, float thickness) diff --git a/Userland/Libraries/LibGfx/AntiAliasingPainter.h b/Userland/Libraries/LibGfx/AntiAliasingPainter.h index 8c3e39795c..f12988f8b6 100644 --- a/Userland/Libraries/LibGfx/AntiAliasingPainter.h +++ b/Userland/Libraries/LibGfx/AntiAliasingPainter.h @@ -29,12 +29,10 @@ public: void draw_line(IntPoint, IntPoint, Color, float thickness = 1, Painter::LineStyle style = Painter::LineStyle::Solid, Color alternate_color = Color::Transparent, LineLengthMode line_length_mode = LineLengthMode::PointToPoint); void draw_line(FloatPoint, FloatPoint, Color, float thickness = 1, Painter::LineStyle style = Painter::LineStyle::Solid, Color alternate_color = Color::Transparent, LineLengthMode line_length_mode = LineLengthMode::PointToPoint); void draw_line_for_path(FloatPoint, FloatPoint, Color, float thickness = 1, Painter::LineStyle style = Painter::LineStyle::Solid, Color alternate_color = Color::Transparent, LineLengthMode line_length_mode = LineLengthMode::PointToPoint); - void draw_line_for_fill_path(FloatPoint from, FloatPoint to, Color color, float thickness = 1) - { - draw_line_for_path(from, to, color, thickness, Painter::LineStyle::Solid, Color {}, LineLengthMode::Distance); - } - void fill_path(Path&, Color, Painter::WindingRule rule = Painter::WindingRule::Nonzero); + void fill_path(Path const&, Color, Painter::WindingRule rule = Painter::WindingRule::Nonzero); + void fill_path(Path const&, PaintStyle const& paint_style, Painter::WindingRule rule = Painter::WindingRule::Nonzero); + void stroke_path(Path const&, Color, float thickness); void draw_quadratic_bezier_curve(FloatPoint control_point, FloatPoint, FloatPoint, Color, float thickness = 1, Painter::LineStyle style = Painter::LineStyle::Solid); void draw_cubic_bezier_curve(FloatPoint control_point_0, FloatPoint control_point_1, FloatPoint, FloatPoint, Color, float thickness = 1, Painter::LineStyle style = Painter::LineStyle::Solid); @@ -75,6 +73,8 @@ public: void fill_rect_with_rounded_corners(IntRect const&, Color, CornerRadius top_left, CornerRadius top_right, CornerRadius bottom_right, CornerRadius bottom_left, BlendMode blend_mode = BlendMode::Normal); + Gfx::Painter& underlying_painter() { return m_underlying_painter; } + private: struct Range { int min; diff --git a/Userland/Libraries/LibGfx/FillPathImplementation.h b/Userland/Libraries/LibGfx/FillPathImplementation.h index 64ba7ef6b7..aa82f25e32 100644 --- a/Userland/Libraries/LibGfx/FillPathImplementation.h +++ b/Userland/Libraries/LibGfx/FillPathImplementation.h @@ -41,16 +41,40 @@ enum class FillPathMode { AllowFloatingPoints, }; -template -void fill_path(Painter& painter, Path const& path, Color color, Gfx::Painter::WindingRule winding_rule) +template +void fill_path(Painter& painter, Path const& path, ColorFunction color_function, Gfx::Painter::WindingRule winding_rule, Optional offset = {}) { using GridCoordinateType = Conditional; using PointType = Point; - auto draw_line = [&](auto... args) { - if constexpr (requires { painter.draw_line_for_fill_path(args...); }) - painter.draw_line_for_fill_path(args...); - else - painter.draw_line(args...); + + auto draw_scanline = [&](int y, float x1, float x2) { + const auto draw_offset = offset.value_or({ 0, 0 }); + const auto draw_origin = (path.bounding_box().top_left() + draw_offset).to_type(); + // FIMXE: Offset is added here to handle floating point translations in the AA painter, + // really this should be done there but this function is a bit too specialised. + y = floorf(y + draw_offset.y()); + x1 += draw_offset.x(); + x2 += draw_offset.x(); + if (x1 > x2) + swap(x1, x2); + auto set_pixel = [&](int x, int y, Color color) { + painter.set_pixel(x, y, color, true); + }; + if constexpr (fill_path_mode == FillPathMode::AllowFloatingPoints) { + int int_x1 = ceilf(x1); + int int_x2 = floorf(x2); + float left_subpixel = int_x1 - x1; + float right_subpixel = x2 - int_x2; + auto left_color = color_function(IntPoint(int_x1 - 1, y) - draw_origin); + auto right_color = color_function(IntPoint(int_x2, y) - draw_origin); + set_pixel(int_x1 - 1, y, left_color.with_alpha(left_color.alpha() * left_subpixel)); + set_pixel(int_x2, y, right_color.with_alpha(right_color.alpha() * right_subpixel)); + for (int x = int_x1; x < int_x2; x++) + set_pixel(x, y, color_function(IntPoint(x, y) - draw_origin)); + } else { + for (int x = x1; x < int(x2); x++) + set_pixel(x, y, color_function(IntPoint(x, y) - draw_origin)); + } }; auto const& segments = path.split_lines(); @@ -136,7 +160,7 @@ void fill_path(Painter& painter, Path const& path, Color color, Gfx::Painter::Wi // inside the shape dbgln_if(FILL_PATH_DEBUG, "y={}: {} at {}: {} -- {}", scanline, winding_number, i, from, to); - draw_line(from, to, color, 1); + draw_scanline(floorf(scanline), from.x(), to.x()); } auto is_passing_through_maxima = scanline == previous.maximum_y @@ -159,7 +183,7 @@ void fill_path(Painter& painter, Path const& path, Color color, Gfx::Painter::Wi active_list.last().x -= active_list.last().inverse_slope; } else { auto point = PointType(active_list[0].x, scanline); - draw_line(point, point, color); + draw_scanline(floorf(scanline), point.x(), point.x()); // update the x coord active_list.first().x -= active_list.first().inverse_slope; @@ -185,12 +209,5 @@ void fill_path(Painter& painter, Path const& path, Color color, Gfx::Painter::Wi active_list.append(segment); } } - - if constexpr (FILL_PATH_DEBUG) { - size_t i { 0 }; - for (auto& segment : segments) { - draw_line(PointType(segment.from), PointType(segment.to), Color::from_hsv(i++ * 360.0 / segments.size(), 1.0, 1.0), 1); - } - } } } diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index 3fe13376dd..f385e8122f 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -2373,7 +2373,16 @@ void Painter::stroke_path(Path const& path, Color color, int thickness) void Painter::fill_path(Path const& path, Color color, WindingRule winding_rule) { VERIFY(scale() == 1); // FIXME: Add scaling support. - Detail::fill_path(*this, path, color, winding_rule); + Detail::fill_path( + *this, path, [=](IntPoint) { return color; }, winding_rule); +} + +void Painter::fill_path(Path const& path, PaintStyle const& paint_style, Painter::WindingRule rule) +{ + VERIFY(scale() == 1); // FIXME: Add scaling support. + paint_style.paint(enclosing_int_rect(path.bounding_box()), [&](PaintStyle::SamplerFunction sampler) { + Detail::fill_path(*this, path, move(sampler), rule); + }); } void Painter::blit_disabled(IntPoint location, Gfx::Bitmap const& bitmap, IntRect const& rect, Palette const& palette) diff --git a/Userland/Libraries/LibGfx/Painter.h b/Userland/Libraries/LibGfx/Painter.h index 1b5d54e64e..537c16260b 100644 --- a/Userland/Libraries/LibGfx/Painter.h +++ b/Userland/Libraries/LibGfx/Painter.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include + namespace Gfx { class Painter { @@ -138,6 +140,7 @@ public: EvenOdd, }; void fill_path(Path const&, Color, WindingRule rule = WindingRule::Nonzero); + void fill_path(Path const&, PaintStyle const& paint_style, WindingRule rule = WindingRule::Nonzero); Font const& font() const {