1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 06:47:34 +00:00

LibGfx: Update fill_path() to support taking a PaintStyle

This means fill_path() now paints the scanlines its self rather than
calling draw_line() which easily allows each pixel along the scanline
to have a different color.
This commit is contained in:
MacDue 2023-01-15 22:15:24 +00:00 committed by Andreas Kling
parent b31d768e95
commit 223cedc896
5 changed files with 64 additions and 29 deletions

View file

@ -41,16 +41,40 @@ enum class FillPathMode {
AllowFloatingPoints,
};
template<FillPathMode fill_path_mode, typename Painter>
void fill_path(Painter& painter, Path const& path, Color color, Gfx::Painter::WindingRule winding_rule)
template<FillPathMode fill_path_mode, typename ColorFunction>
void fill_path(Painter& painter, Path const& path, ColorFunction color_function, Gfx::Painter::WindingRule winding_rule, Optional<FloatPoint> offset = {})
{
using GridCoordinateType = Conditional<fill_path_mode == FillPathMode::PlaceOnIntGrid, int, float>;
using PointType = Point<GridCoordinateType>;
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<int>();
// 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);
}
}
}
}