fix: clamp percentage polygon to width-1/height-1

ParsePercentagePolygon clamped to [0, width] / [0, height], so a new
zone created at 100% produced pixel coords equal to the monitor
dimensions. The rasterizer requires hi_x < width and hi_y < height,
so the runtime then logged warnings like
  "polygon hi_x (1920) >= image width (1920), clamping".

Clamp to width-1 / height-1 instead. Updates existing tests that
encoded the previous behavior.
This commit is contained in:
Isaac Connor committed 2026-05-13 11:38:42 -04:00
1 parent 4a167f7f30
commit e1eb7876c6
2 files changed
+24 -23

No files matched your search

+4 -3
View File
@@ -862,9 +862,10 @@ bool Zone::ParsePercentagePolygon(const char *poly_string, unsigned int width, u
int32 px_x = static_cast<int32>(std::lround(pct_x * mon_w / 100.0));
int32 px_y = static_cast<int32>(std::lround(pct_y * mon_h / 100.0));
// Clamp to monitor bounds
px_x = std::clamp(px_x, static_cast<int32>(0), static_cast<int32>(width));
px_y = std::clamp(px_y, static_cast<int32>(0), static_cast<int32>(height));
// Clamp to monitor bounds. Max valid pixel index is width-1/height-1;
// values equal to width/height cause out-of-bounds warnings in the rasterizer.
px_x = std::clamp(px_x, static_cast<int32>(0), static_cast<int32>(width) - 1);
px_y = std::clamp(px_y, static_cast<int32>(0), static_cast<int32>(height) - 1);
Debug(3, "Percentage coord %.2f,%.2f -> pixel %d,%d", pct_x, pct_y, px_x, px_y);
vertices.emplace_back(px_x, px_y);
+20 -20
View File
@@ -28,9 +28,9 @@ TEST_CASE("Zone::ParsePercentagePolygon: full-frame zone at 1920x1080", "[Zone]"
REQUIRE(result == true);
REQUIRE(polygon.GetVertices().size() == 4);
REQUIRE(polygon.GetVertices()[0] == Vector2(0, 0));
REQUIRE(polygon.GetVertices()[1] == Vector2(1920, 0));
REQUIRE(polygon.GetVertices()[2] == Vector2(1920, 1080));
REQUIRE(polygon.GetVertices()[3] == Vector2(0, 1080));
REQUIRE(polygon.GetVertices()[1] == Vector2(1919, 0));
REQUIRE(polygon.GetVertices()[2] == Vector2(1919, 1079));
REQUIRE(polygon.GetVertices()[3] == Vector2(0, 1079));
}
TEST_CASE("Zone::ParsePercentagePolygon: center 50% zone", "[Zone]") {
@@ -68,8 +68,8 @@ TEST_CASE("Zone::ParsePercentagePolygon: different resolution", "[Zone]") {
640, 480, polygon);
REQUIRE(result == true);
REQUIRE(polygon.GetVertices()[1] == Vector2(640, 0));
REQUIRE(polygon.GetVertices()[2] == Vector2(640, 480));
REQUIRE(polygon.GetVertices()[1] == Vector2(639, 0));
REQUIRE(polygon.GetVertices()[2] == Vector2(639, 479));
}
TEST_CASE("Zone::ParsePercentagePolygon: clamping beyond 100%", "[Zone]") {
@@ -79,8 +79,8 @@ TEST_CASE("Zone::ParsePercentagePolygon: clamping beyond 100%", "[Zone]") {
1920, 1080, polygon);
REQUIRE(result == true);
// 110% should be clamped to monitor width
REQUIRE(polygon.GetVertices()[1].x_ == 1920);
// 110% should be clamped to max valid pixel index (width-1)
REQUIRE(polygon.GetVertices()[1].x_ == 1919);
}
TEST_CASE("Zone::ParsePercentagePolygon: triangle", "[Zone]") {
@@ -114,9 +114,9 @@ TEST_CASE("Zone::ParsePercentagePolygon: integer coords still work", "[Zone]") {
REQUIRE(result == true);
REQUIRE(polygon.GetVertices()[0] == Vector2(0, 0));
REQUIRE(polygon.GetVertices()[1] == Vector2(1920, 0));
REQUIRE(polygon.GetVertices()[2] == Vector2(1920, 1080));
REQUIRE(polygon.GetVertices()[3] == Vector2(0, 1080));
REQUIRE(polygon.GetVertices()[1] == Vector2(1919, 0));
REQUIRE(polygon.GetVertices()[2] == Vector2(1919, 1079));
REQUIRE(polygon.GetVertices()[3] == Vector2(0, 1079));
}
TEST_CASE("Zone::ParsePolygonString: basic pixel parsing", "[Zone]") {
@@ -166,9 +166,9 @@ TEST_CASE("Zone::ParsePercentagePolygon: percentage to pixel conversion", "[Zone
auto const &verts = polygon.GetVertices();
REQUIRE(verts.size() == 4);
REQUIRE(verts[0] == Vector2(0, 0));
REQUIRE(verts[1] == Vector2(1920, 0));
REQUIRE(verts[2] == Vector2(1920, 1080));
REQUIRE(verts[3] == Vector2(0, 1080));
REQUIRE(verts[1] == Vector2(1919, 0));
REQUIRE(verts[2] == Vector2(1919, 1079));
REQUIRE(verts[3] == Vector2(0, 1079));
}
SECTION("50% rectangle converts to half-resolution pixels") {
@@ -184,16 +184,16 @@ TEST_CASE("Zone::ParsePercentagePolygon: percentage to pixel conversion", "[Zone
}
SECTION("values are clamped to monitor bounds") {
// 100% should clamp to exact monitor dimensions
// 100% should clamp to max valid pixel index (width-1, height-1)
bool ok = Zone::ParsePercentagePolygon("0,0 100,0 100,100 0,100", width, height, polygon);
REQUIRE(ok);
auto const &verts = polygon.GetVertices();
for (auto const &v : verts) {
REQUIRE(v.x_ >= 0);
REQUIRE(v.x_ <= static_cast<int>(width));
REQUIRE(v.x_ < static_cast<int>(width));
REQUIRE(v.y_ >= 0);
REQUIRE(v.y_ <= static_cast<int>(height));
REQUIRE(v.y_ < static_cast<int>(height));
}
}
}
@@ -213,12 +213,12 @@ TEST_CASE("Zone: pixel values through ParsePercentagePolygon produce wrong resul
auto const &verts = polygon.GetVertices();
REQUIRE(verts.size() == 4);
// 639% of 1920 = 12268.8 -> clamped to 1920
// 479% of 1080 = 5173.2 -> clamped to 1080
// 639% of 1920 = 12268.8 -> clamped to width-1
// 479% of 1080 = 5173.2 -> clamped to height-1
// All non-zero coords get clamped to monitor bounds — the zone is
// degenerate (covers the full monitor instead of a sub-region)
REQUIRE(verts[1] == Vector2(static_cast<int>(width), 0));
REQUIRE(verts[2] == Vector2(static_cast<int>(width), static_cast<int>(height)));
REQUIRE(verts[1] == Vector2(static_cast<int>(width) - 1, 0));
REQUIRE(verts[2] == Vector2(static_cast<int>(width) - 1, static_cast<int>(height) - 1));
}
// --- Auto-detect format tests ---