diff --git a/Userland/Libraries/LibGfx/AntiAliasingPainter.cpp b/Userland/Libraries/LibGfx/AntiAliasingPainter.cpp index 96465a35f08..fe53c674713 100644 --- a/Userland/Libraries/LibGfx/AntiAliasingPainter.cpp +++ b/Userland/Libraries/LibGfx/AntiAliasingPainter.cpp @@ -10,7 +10,6 @@ # pragma GCC optimize("O3") #endif -#include "FillPathImplementation.h" #include #include #include @@ -213,14 +212,12 @@ void AntiAliasingPainter::draw_line(FloatPoint actual_from, FloatPoint actual_to void AntiAliasingPainter::fill_path(Path const& path, Color color, Painter::WindingRule rule) { - Detail::fill_path(m_underlying_painter, path, color, rule, m_transform.translation()); + m_underlying_painter.antialiased_fill_path(path, 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()); - }); + m_underlying_painter.antialiased_fill_path(path, paint_style, rule, m_transform.translation()); } void AntiAliasingPainter::stroke_path(Path const& path, Color color, float thickness) diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index e1b90ad9e46..5ffa912f9fa 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -10,6 +10,7 @@ set(SOURCES Color.cpp CursorParams.cpp DDSLoader.cpp + FillPathImplementation.cpp Filters/ColorBlindnessFilter.cpp Filters/FastBoxBlurFilter.cpp Filters/LumaFilter.cpp diff --git a/Userland/Libraries/LibGfx/FillPathImplementation.h b/Userland/Libraries/LibGfx/FillPathImplementation.cpp similarity index 62% rename from Userland/Libraries/LibGfx/FillPathImplementation.h rename to Userland/Libraries/LibGfx/FillPathImplementation.cpp index 8b139f3bcad..b817a0808d5 100644 --- a/Userland/Libraries/LibGfx/FillPathImplementation.h +++ b/Userland/Libraries/LibGfx/FillPathImplementation.cpp @@ -1,18 +1,87 @@ /* * Copyright (c) 2021, Ali Mohammad Pur + * Copyright (c) 2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause */ -#pragma once - #include #include #include #include #include -namespace Gfx::Detail { +#if defined(AK_COMPILER_GCC) +# pragma GCC optimize("O3") +#endif + +namespace Gfx { + +template +ALWAYS_INLINE void Painter::draw_scanline_for_fill_path(int y, T x_start, T x_end, TColorOrFunction color) +{ + // Fill path should scale the scanlines before calling this. + VERIFY(scale() == 1); + + constexpr bool is_floating_point = IsSameIgnoringCV; + constexpr bool has_constant_color = IsSameIgnoringCV; + + int x1 = 0; + int x2 = 0; + u8 left_subpixel_alpha = 0; + u8 right_subpixel_alpha = 0; + if constexpr (is_floating_point) { + x1 = ceilf(x_start); + x2 = floorf(x_end); + left_subpixel_alpha = (x1 - x_start) * 255; + right_subpixel_alpha = (x_end - x2) * 255; + x1 -= left_subpixel_alpha > 0; + x2 += right_subpixel_alpha > 0; + } else { + x1 = x_start; + x2 = x_end; + } + + IntRect scanline(x1, y, x2 - x1, 1); + scanline = scanline.translated(translation()); + auto clipped = scanline.intersected(clip_rect()); + if (clipped.is_empty()) + return; + + auto get_color = [&](int offset) { + if constexpr (has_constant_color) { + return color; + } else { + return color(offset); + } + }; + + if constexpr (is_floating_point) { + // Paint left and right subpixels (then remove them from the scanline). + auto get_color_with_alpha = [&](int offset, u8 alpha) { + auto color_at_offset = get_color(offset); + u8 color_alpha = (alpha * color_at_offset.alpha()) / 255; + return color_at_offset.with_alpha(color_alpha); + }; + if (clipped.left() == scanline.left() && left_subpixel_alpha) + set_physical_pixel(clipped.top_left(), get_color_with_alpha(0, left_subpixel_alpha), true); + if (clipped.right() == scanline.right() && right_subpixel_alpha) + set_physical_pixel(clipped.top_right(), get_color_with_alpha(scanline.width(), right_subpixel_alpha), true); + clipped.shrink(0, right_subpixel_alpha > 0, 0, left_subpixel_alpha > 0); + } + + if constexpr (has_constant_color) { + if (color.alpha() == 255) { + // Speedy path: Constant color and no alpha blending. + fast_u32_fill(m_target->scanline(clipped.y()) + clipped.x(), color.value(), clipped.width()); + return; + } + } + + for (int x = clipped.x(); x <= clipped.right(); x++) { + set_physical_pixel({ x, clipped.y() }, get_color(x - scanline.x()), true); + } +} [[maybe_unused]] inline void approximately_place_on_int_grid(FloatPoint ffrom, FloatPoint fto, IntPoint& from, IntPoint& to, Optional previous_to) { @@ -36,13 +105,8 @@ namespace Gfx::Detail { from.set_x(previous_to.value().x()); } -enum class FillPathMode { - PlaceOnIntGrid, - AllowFloatingPoints, -}; - -template -void fill_path(Painter& painter, Path const& path, ColorOrFunction color, Gfx::Painter::WindingRule winding_rule, Optional offset = {}) +template +void Painter::fill_path_impl(Path const& path, ColorOrFunction color, Gfx::Painter::WindingRule winding_rule, Optional offset) { using GridCoordinateType = Conditional; using PointType = Point; @@ -58,9 +122,9 @@ void fill_path(Painter& painter, Path const& path, ColorOrFunction color, Gfx::P if (x1 > x2) swap(x1, x2); if constexpr (IsSameIgnoringCV) { - painter.draw_scanline_for_fill_path(y, x1, x2 + 1, color); + draw_scanline_for_fill_path(y, x1, x2 + 1, color); } else { - painter.draw_scanline_for_fill_path(y, x1, x2 + 1, [&](int offset) { + draw_scanline_for_fill_path(y, x1, x2 + 1, [&](int offset) { return color(IntPoint(x1 + offset, y) - draw_origin); }); } @@ -124,7 +188,7 @@ void fill_path(Painter& painter, Path const& path, ColorOrFunction color, Gfx::P }); if constexpr (fill_path_mode == FillPathMode::PlaceOnIntGrid && FILL_PATH_DEBUG) { if ((int)scanline % 10 == 0) { - painter.draw_text(Gfx::Rect(active_list.last().x - 20, scanline, 20, 10), DeprecatedString::number((int)scanline)); + draw_text(Gfx::Rect(active_list.last().x - 20, scanline, 20, 10), DeprecatedString::number((int)scanline)); } } @@ -199,4 +263,33 @@ void fill_path(Painter& painter, Path const& path, ColorOrFunction color, Gfx::P } } } + +void Painter::fill_path(Path const& path, Color color, WindingRule winding_rule) +{ + VERIFY(scale() == 1); // FIXME: Add scaling support. + fill_path_impl(path, 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) { + fill_path_impl(path, move(sampler), rule); + }); +} + +void Painter::antialiased_fill_path(Path const& path, Color color, WindingRule rule, FloatPoint translation) +{ + VERIFY(scale() == 1); // FIXME: Add scaling support. + fill_path_impl(path, color, rule, translation); +} + +void Painter::antialiased_fill_path(Path const& path, PaintStyle const& paint_style, WindingRule rule, FloatPoint translation) +{ + VERIFY(scale() == 1); // FIXME: Add scaling support. + paint_style.paint(enclosing_int_rect(path.bounding_box()), [&](PaintStyle::SamplerFunction sampler) { + fill_path_impl(path, move(sampler), rule, translation); + }); +} + } diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index 5fd5c2f53dc..3f3ca806a68 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -2390,20 +2389,6 @@ 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); -} - -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) { auto bright_color = palette.threed_highlight(); diff --git a/Userland/Libraries/LibGfx/Painter.h b/Userland/Libraries/LibGfx/Painter.h index 369acfa947e..c93023f3ef7 100644 --- a/Userland/Libraries/LibGfx/Painter.h +++ b/Userland/Libraries/LibGfx/Painter.h @@ -141,6 +141,7 @@ public: Nonzero, EvenOdd, }; + void fill_path(Path const&, Color, WindingRule rule = WindingRule::Nonzero); void fill_path(Path const&, PaintStyle const& paint_style, WindingRule rule = WindingRule::Nonzero); @@ -181,78 +182,9 @@ public: int scale() const { return state().scale; } - template - ALWAYS_INLINE void draw_scanline_for_fill_path(int y, T x_start, T x_end, TColorOrFunction color) - { - // Note: This is really an internal function for FillPathImplementation.h to use. - // This allows fill path to clip more of the pixels and reduce the number of clipping checks - // to the number of scanlines (and allows for a fast fill). - - // Fill path should scale the scanlines before calling this. - VERIFY(scale() == 1); - - constexpr bool is_floating_point = IsSameIgnoringCV; - constexpr bool has_constant_color = IsSameIgnoringCV; - - int x1 = 0; - int x2 = 0; - u8 left_subpixel_alpha = 0; - u8 right_subpixel_alpha = 0; - if constexpr (is_floating_point) { - x1 = ceilf(x_start); - x2 = floorf(x_end); - left_subpixel_alpha = (x1 - x_start) * 255; - right_subpixel_alpha = (x_end - x2) * 255; - x1 -= left_subpixel_alpha > 0; - x2 += right_subpixel_alpha > 0; - } else { - x1 = x_start; - x2 = x_end; - } - - IntRect scanline(x1, y, x2 - x1, 1); - scanline = scanline.translated(translation()); - auto clipped = scanline.intersected(clip_rect()); - if (clipped.is_empty()) - return; - - auto get_color = [&](int offset) { - if constexpr (has_constant_color) { - return color; - } else { - return color(offset); - } - }; - - if constexpr (is_floating_point) { - // Paint left and right subpixels (then remove them from the scanline). - auto get_color_with_alpha = [&](int offset, u8 alpha) { - auto color_at_offset = get_color(offset); - u8 color_alpha = (alpha * color_at_offset.alpha()) / 255; - return color_at_offset.with_alpha(color_alpha); - }; - if (clipped.left() == scanline.left() && left_subpixel_alpha) - set_physical_pixel(clipped.top_left(), get_color_with_alpha(0, left_subpixel_alpha), true); - if (clipped.right() == scanline.right() && right_subpixel_alpha) - set_physical_pixel(clipped.top_right(), get_color_with_alpha(scanline.width(), right_subpixel_alpha), true); - clipped.shrink(0, right_subpixel_alpha > 0, 0, left_subpixel_alpha > 0); - } - - if constexpr (has_constant_color) { - if (color.alpha() == 255) { - // Speedy path: Constant color and no alpha blending. - fast_u32_fill(m_target->scanline(clipped.y()) + clipped.x(), color.value(), clipped.width()); - return; - } - } - - for (int x = clipped.x(); x <= clipped.right(); x++) { - set_physical_pixel({ x, clipped.y() }, get_color(x - scanline.x()), true); - } - } - protected: friend GradientLine; + friend AntiAliasingPainter; IntRect to_physical(IntRect const& r) const { return r.translated(translation()) * scale(); } IntPoint to_physical(IntPoint p) const { return p.translated(translation()) * scale(); } @@ -285,6 +217,17 @@ private: bool text_contains_bidirectional_text(Utf8View const&, TextDirection); template void do_draw_text(FloatRect const&, Utf8View const& text, Font const&, TextAlignment, TextElision, TextWrapping, DrawGlyphFunction); + + void antialiased_fill_path(Path const&, Color, WindingRule rule, FloatPoint translation); + void antialiased_fill_path(Path const&, PaintStyle const& paint_style, WindingRule rule, FloatPoint translation); + enum class FillPathMode { + PlaceOnIntGrid, + AllowFloatingPoints, + }; + template + void draw_scanline_for_fill_path(int y, T x_start, T x_end, TColorOrFunction color); + template + void fill_path_impl(Path const& path, ColorOrFunction color, Gfx::Painter::WindingRule winding_rule, Optional offset = {}); }; class PainterStateSaver {