nx_bluenoise.nx
buildroot/runtime/nx_bluenoise.nx
about
nx_bluenoise.nx -- SOVEREIGN BLUE-NOISE MASK GENERATOR (Ulichney void-and-cluster, integer-exact).
WHY THIS EXISTS: quantizing a photograph or a page of text to the ~16 grey levels an e-ink panel actually
has is what produces the "ink on paper" look. Doing that quantization with a THRESHOLD MASK decides whether
the result reads as paper texture or as a visible screen-door pattern. A blue-noise mask is the one whose
thresholded pattern has an isotropic power spectrum with no low-frequency energy -- the eye's own low-pass
then averages it to the right tone and never resolves structure. White noise clumps; ordered (Bayer) masks
show a lattice. Blue noise is the state of the art and has been since Ulichney 1993.
INTEGER-EXACT BY CONSTRUCTION: no float anywhere. The Gaussian is a 33-entry fixed-point table indexed by
SQUARED distance, so the filter is exact and the same on every machine -- a mask that differed per host
would make every downstream render non-reproducible.
COST, STATED: the classical algorithm is O(N^2) in cell count. 64x64 is ~16.7M ops (fast); the published
figure for 8192x8192 is about a MONTH, which is why the right shape is "generate a small mask ONCE, ship
it as data, tile it" rather than generating at render time. The 128 cap below is that decision made
explicit rather than discovered by a hang.
IMPRECISION I CHOSE TO LIVE WITH (documented so the next reader does not trust it as exact): Ulichney's
phases 3 and 4 reverse which class is the "minority" for filtering purposes. Because the filter is linear
and toroidal, energy_of_zeros = constant - energy_of_ones, so "tightest cluster of 0s" and "largest void
of 1s" select the SAME cell. The two phases therefore collapse into one loop here, exactly rather than
approximately.
nx_bluenoise gen <size> <out.raw> -- write size*size bytes, cell value = rank scaled to 0..255
nx_bluenoise stats <in.raw> <size> <fill> -- nearest-neighbour stats of the pattern thresholded at fill permil
nx_bluenoise selftest -- the gate
license_tier: ORIGINAL
dependencies 2 imports · 0 importers
imports: nx_gate_verdict.nxnx_syscalls.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 31 | const BN_MAGIC_4096: i64 = 4096 |
| 32 | const BN_MAGIC_3280: i64 = 3280 |
| 33 | const BN_MAGIC_2626: i64 = 2626 |
| 34 | const BN_MAGIC_2103: i64 = 2103 |
| 35 | const BN_MAGIC_1684: i64 = 1684 |
| 36 | const BN_MAGIC_1348: i64 = 1348 |
| 37 | const BN_MAGIC_1080: i64 = 1080 |
| 39 | const BN_MAX_SIZE: i64 = 128 // O(N^2): 128x128 is ~268M ops. Above this, generate offline and ship the bytes. |
| 40 | const BN_MIN_SIZE: i64 = 8 |
| 41 | const BN_DEFAULT_SIZE: i64 = 64 |
| 42 | const BN_KERN_R: i64 = 4 // kernel radius; sigma=1.5 is negligible past 4 (weight 3/4096) |
| 43 | const BN_KERN_MAXD2: i64 = 32 // max squared distance inside the radius-4 box |
| 44 | const BN_FIX: i64 = 4096 // fixed-point scale for the Gaussian |
| 45 | const BN_INIT_PERMIL: i64 = 100 // initial prototype fill (10%) -- Ulichney's recommended starting density |
| 46 | const BN_LCG_A: i64 = 1103515245 |
| 47 | const BN_LCG_C: i64 = 12345 |
| 48 | const BN_LCG_M: i64 = 2147483648 |
| 49 | const BN_SEED: i64 = 20260808 // FIXED seed: the mask must be byte-identical on every host, forever |
| 50 | const BN_BYTE_MAX: i64 = 256 |
functions
| 52 | func bn_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } |
| 53 | func bn_num(v: i64) -> i64 |
| 66 | func bn_gauss(d2: i64) -> i64 |
| 80 | func bn_splat(energy: *i64, size: i64, cx: i64, cy: i64, sign: i64) -> i64 |
| 104 | func bn_extreme(bin: *i64, energy: *i64, n: i64, want: i64, hi: i64) -> i64 called by 1: bn_generate |
| 123 | func bn_generate(size: i64, rank: *i64) -> i64 |
| 190 | func bn_nnstats(rank: *i64, size: i64, fill_permil: i64, out: *i64) -> i64 |
| 256 | func bn_white(size: i64, rank: *i64) -> i64 |
| 271 | func bn_atoi(s: *u8) -> i64 called by 1: main |
| 281 | func bn_write_raw(path: *u8, rank: *i64, n: i64) -> i64 |
| 292 | func bn_seq(a: *u8, b: *u8) -> i64 called by 1: main |
| 302 | func bn_selftest() -> i64 |
| 383 | func main(argc: i64, argv: *i64) -> i64 |