code wiki / (root) / nx_phototwin.nx

nx_phototwin.nx

buildroot/runtime/nx_phototwin.nx

26393 B509 linesdepth 11pulls 52 transitivereach 0 importersview sourcekind tool
docsdependenciesstructsconstsfunctions

about

nx_phototwin.nx -- THE PHOTOGRAPH-TO-SUBSTRATE TWIN (2026-08-24, /compare/phototwin PT2 PT3 PT7). license_tier: ORIGINAL No hw writes (Rule 26). nx_phototwin report <image> <image.icc> <substrate.icc> [conf] gamut-coverage report of a photograph nx_phototwin proof <image> <image.icc> <substrate.icc> <outdir> [conf] twin.png (chroma-clipped) + heat.png (dE00 map) + proof.txt nx_phototwin wash <ledger.tsv> <sku> [conf] wash-fastness ledger verdict for one SKU nx_phototwin bench <ledger.tsv> [conf] the swatch bench over EVERY sku, refusing partial coverage EXIT: 0 OK | 1 REFUSED (tolerance exceeded, wash failed) | 2 usage | 3 UNOBSERVABLE (undecodable image, unreadable profile or conf, no ledger rows) -- the third state is never a pass and never a failure of the subject. TWO PROFILES, TWO ROLES. Pixels are interpreted through the IMAGE profile (RGB -> TRC -> matrix -> XYZ(D50) -> CIELAB, one ruler, nx_colorsci_lib) and tested against the SUBSTRATE profile's gamut -- EXACTLY for a matrix/TRC profile (linear RGB through its inverse in [0,1]; the cube is the gamut). Out-of-gamut colours are chroma-clipped at constant L and hue (the twin), then rendered back through the image profile so the soft proof displays in the image's own space. dE00 between photograph and twin is the proof: mean and max against knowledge/colorsci.conf (printing.org bar). MEASURED 2026-08-24 before this shape: one profile for both roles made the narrow-substrate control VACUOUS (the image's codes were read as substrate codes and were inside by construction), and a segment-maxima descriptor read 18 percent of an sRGB photograph outside sRGB -- both retired here. The descriptor stays in the lib for MEASURED swatch gamuts (PT7), which have no matrix. The mapping is MEMOISED PER EXACT 24-BIT COLOUR (a photograph has ~1e5-1e6 distinct colours, never 4e6); the unique-colour count is printed so the cost is on the record. SUBSTRATE HONESTY: until a swatch bench measures the pigment-on-Supima profile, a proof whose substrate is the image profile itself is a positive CONTROL and says so in proof.txt. WASH LEDGER (knowledge/phototwin_wash.tsv): rows swatch sku blank line cycle L a b, Lab measured before (cycle 0) and after declared ISO 105-C06 cycles; dE00 per cycle against cycle 0; a SKU with no rows is UNMEASURED (exit 3).

dependencies 4 imports · 0 importers

nx_syscalls.nx nx_colorsci_lib.nx nx_img_to_rgb.nx nx_png_write.nx nx_phototwin.nx

imports: nx_syscalls.nxnx_colorsci_lib.nxnx_img_to_rgb.nxnx_png_write.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main pt_usage pt_w sys_write cs_ctx sys_mmap nxa_die sys_write ↻ sys_exit nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ fq_ctx sys_mmap ↻ fq_atan_small fq_mul fq_atanh_small fq_mul ↻ fq_mul ↻ fq_div fq_sqrt fq_bitlen fq_isqrt vm_isqrt fq_div ↻ fq_from_int fq_mul ↻ sys_mmap ↻ pt_streq pt_run_wash pt_conf cs_conf_int

structs

none

consts

31const PT_EXIT_OK: i64 = 0
32const PT_EXIT_REFUSED: i64 = 1
33const PT_EXIT_USAGE: i64 = 2
34const PT_EXIT_UNOBSERVABLE: i64 = 3
35const PT_CONF_DEFAULT: *u8 = "knowledge/colorsci.conf"
36const PT_CHANNELS: i64 = 3
37const PT_BYTE: i64 = 256
38const PT_BYTE_MAX: i64 = 255
39const PT_MEMO_SLOTS: i64 = 2097152 // 2^21 slots: above any photograph's distinct-colour count
40const PT_MEMO_MASK: i64 = 2097151
41const PT_HASH_KNUTH: i64 = 2654435761 // Knuth multiplicative hashing constant (TAOCP vol 3, 6.4)
42const PT_HASH_SHIFT: i64 = 11 // 32 - 21: take the top bits of the 32-bit product
43const PT_U32_MASK: i64 = 4294967295
44const PT_MEMO_EMPTY: i64 = 0 - 1
45const PT_MEMO_ARRAYS: i64 = 4 // key, permil, sector-or-codes, de
46const PT_SLOT: i64 = 8
47const PT_PATH_CAP: i64 = 4096
48const PT_LINE_CAP: i64 = 4096
49const PT_PERMIL: i64 = 1000
50const PT_DEG_FULL: i64 = 360
51const PT_TAB: i64 = 9
52const PT_NL: i64 = 10
53const PT_HASH: i64 = 35
54const PT_LF_SWATCH: i64 = 0
55const PT_LF_SKU: i64 = 1
56const PT_LF_CYCLE: i64 = 4
57const PT_LF_L: i64 = 5
58const PT_LF_A: i64 = 6
59const PT_LF_B: i64 = 7
60const PT_LEDGER_MAX_ROWS: i64 = 65536
61const PT_ROW_STRIDE: i64 = 8 // swatch_off swatch_len sku_off sku_len cycle L a b
62const PT_MIN_CYCLES: i64 = 2 // a swatch proves fastness only with cycle 0 AND at least one later cycle
63const PT_HEAT_MAX: i64 = 255
64const PT_MODE_0644: i64 = 420
65const PT_R_PIXELS: i64 = 0
66const PT_R_UNIQUE: i64 = 1
67const PT_R_INSIDE: i64 = 2
68const PT_R_OUTSIDE: i64 = 3
69const PT_R_WORST_PERMIL: i64 = 4
70const PT_R_SECT0: i64 = 8
71const PT_STATS_SLOTS: i64 = 4
72const PT_OUT_SLOTS: i64 = 8

functions

74func pt_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
75func pt_wn(v: i64) -> i64
88func pt_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var p: i64 = o; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } d[p] = 0 as u8; return p }
called by 1: pt_soft_proof
89func pt_catn(d: *u8, o: i64, v: i64) -> i64
called by 1: pt_soft_proof calls 1: sys_mmap
102func pt_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] != (0 as u8) { return 0 } return 1 }
called by 1: main
103func pt_write_text(path: *u8, s: *u8, n: i64) -> i64
111func pt_conf(path: *u8, key: *u8, missing: *i64) -> i64
118func pt_memo_alloc() -> *i64
called by 1: main calls 1: sys_mmap
124func pt_memo_arr(m: *i64, k: i64) -> *i64 { return (m as i64 + PT_MEMO_SLOTS * PT_SLOT * k) as *i64 }
125func pt_memo_slot(m: *i64, key: i64) -> i64
140func pt_gamut_report(cs: *i64, img: *i64, sub: *i64, sect: *i64, rgb: *u8, w: i64, h: i64, memo: *i64, out: *i64) -> i64
178func pt_print_report(out: *i64, hs: i64) -> i64
called by 1: main calls 2: pt_wpt_wn
198func pt_gamut_map(cs: *i64, img: *i64, sub: *i64, rgb: *u8, w: i64, h: i64, memo: *i64, iters: i64, twin: *u8, de: *i64, max_tol_micro: i64, stats: *i64) -> i64
253func pt_soft_proof(cs: *i64, img: *i64, sub: *i64, rgb: *u8, w: i64, h: i64, memo: *i64, outdir: *u8, conf: *u8, missing: *i64, control: i64) -> i64
310func pt_field(buf: *u8, s: i64, e: i64, idx: i64, box: *i64) -> i64
called by 1: pt_ledger_load
326func pt_span_eq(buf: *u8, off: i64, len: i64, s: *u8) -> i64
called by 1: pt_wash_ledger
332func pt_span_same(buf: *u8, a: i64, an: i64, b: i64, bn: i64) -> i64
called by 1: pt_wash_ledger
338func pt_ledger_load(buf: *u8, n: i64, rows: *i64, bad: *i64) -> i64
366func pt_wash_ledger(cs: *i64, buf: *u8, rows: *i64, nrows: i64, sku: *u8, tol_micro: i64, out: *i64) -> i64
421func pt_bench_gate(cs: *i64, buf: *u8, rows: *i64, nrows: i64, tol_micro: i64, out: *i64) -> i64
called by 1: pt_run_wash calls 1: pt_wash_ledger
424func pt_print_wash(out: *i64, rc: i64) -> i64
called by 1: pt_run_wash calls 2: pt_wpt_wn
432func pt_run_wash(cs: *i64, ledger: *u8, sku: *u8, conf: *u8, missing: *i64, bench: i64) -> i64
452func pt_usage() -> i64
called by 1: main calls 1: pt_w
457func main(argc: i64, argv: *i64) -> i64