fix: scan-line polygon fill incorrectly filled non-convex polygon gaps

Image::Fill(Polygon) implements scan-line polygon fill but iterated
through active edges one at a time instead of in pairs. For convex
polygons (always exactly 2 active edges per scan line) this happened
to work, but for non-convex polygons it would fill the gaps between
concave sections.

A banana-shaped zone, for example, would have its inner concave area
incorrectly marked as inside the zone, causing motion detection to
trigger on the area the user explicitly drew the zone to avoid.

Fix by stepping the iterator by 2 to fill between pairs of edges
following the standard parity rule for scan-line polygon fill.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Isaac Connor
2026-04-09 09:33:52 -04:00
parent 47e6f2f4f4
commit 83ed47abf4

View File

@@ -2720,9 +2720,12 @@ void Image::Fill(Rgb colour, int density, const Polygon &polygon) {
std::sort(active_edges.begin(), active_edges.end(), PolygonFill::Edge::CompareX);
if (!(scan_line % density)) {
for (auto it = active_edges.begin(); it < active_edges.end() - 1; ++it) {
// Fill between pairs of active edges (parity rule). Stepping one
// edge at a time would incorrectly fill the gaps between arms of
// a non-convex polygon (e.g. a banana shape).
for (auto it = active_edges.begin(); it + 1 < active_edges.end(); it += 2) {
int32 lo_x = static_cast<int32>(it->min_x);
int32 hi_x = static_cast<int32>(std::next(it)->min_x);
int32 hi_x = static_cast<int32>((it + 1)->min_x);
if (colours == ZM_COLOUR_GRAY8) {
uint8 *p = &buffer[(scan_line * width) + lo_x];