Skip to content

Fix negative coordinates in tiled image sampling - #598

Merged
treeform merged 2 commits into
masterfrom
fix/tiled-image-negative-coordinates
Sep 13, 2026
Merged

treeform merged 2 commits into
masterfrom
fix/tiled-image-negative-coordinates

Conversation

@treeform

@treeform treeform commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Rotating a TiledImagePaint can produce negative source coordinates. The wrapped sampler uses signed mod, leaving negative pixel indexes that raise IndexDefect in debug builds and cause misplaced pixels or out-of-bounds reads in release builds. The example in #597 reproduces both symptoms on current master.

Wrap the base X/Y coordinates with an expanded integer template, following janAkali's diagnosis in the forum discussion. Image dimensions are positive, so a negative remainder can be corrected by adding the dimension. Derive the two adjacent indexes with boundary checks, reducing the sampler to two modulo operations without out-of-line wrapping calls.

Add regression tests comparing wrapped sampling with an explicitly repeated image, covering negative and fractional coordinates, tile seams, non-square and single-pixel dimensions, and five rotation angles including the reported -7 degrees. Include the tests in the standard runner and add tests/bench_images_tiled.nim for repeatable rendering/sampler benchmarks.

Fixes #597.

Validation on Windows with Nim 2.2.10:

  • The new regression test fails on the original code and passes after the fix.
  • Regression tests pass in debug and release builds.
  • The original example passes in debug and release after the fix.
  • The full standard test suite passes with both C and C++ backends.
  • Existing tiled-image snapshots match exactly; git diff --check passes.

Performance on a Ryzen 9 5900X, Nim 2.2.10, Windows amd64, GCC 11.1.0, -d:release / -O3, no LTO: six interleaved rounds pinned to one logical CPU, with warmup and matching pixel checksums. Times are median milliseconds per operation. Shared comparisons use nonnegative source coordinates so the pre-fix build does not perform invalid reads.

Workload Before fix Initial function fix This revision Change vs before
Rotated 4x4 stripes, 256x160 1.185 1.521 1.210 +2.1%
Rotated 4x4 stripes, 1024x1024 30.529 39.234 31.482 +3.1%
Rotated 63x47 texture, 1024x1024 89.437 67.802 90.015 +0.6%
Unrotated 64x64 tiling, 1024x1024 13.757 25.756 12.500 -9.1%
Ordinary non-tiled smooth draw 23.191 14.586 23.146 -0.2%

Disassembly confirms the initial euclMod implementation leaves four external calls per wrapped sample; the revised sampler has none. The function variant also changes the compiler's placement of thread-local exception-flag lookups and happens to accelerate textured/non-tiled workloads. The revised version does not preserve every incidental gain of that variant; it stays close to the original baseline on the measured workloads. Results are specific to this compiler/platform.

Run the benchmark with nim c -r -d:release -d:benchAffinity tests/bench_images_tiled.nim. Append negative to include the original failing-coordinate case; that case must only be run against fixed builds.

@treeform
treeform merged commit 88e0741 into master Sep 13, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rotated TiledImagePaint causes misplaced pixels and IndexDefect.

1 participant