code wiki / _hdl_build / nx_visual_diff_cli.nx
nx_visual_diff_cli.nx source
↩ module page · 71 lines · 3968 B
1// nx_visual_diff_cli.nx -- CLI for the sovereign visual-parity instrument (nx_visual_diff lib).
2// Speaks plain english + numbers. usage:
3// nx_visual_diff_cli grid <A.png> <B.png> <ay> <by> <cmp_w> <cmp_h> <gx> <gy> <thresh>
4// nx_visual_diff_cli extent <A.png> <tol>
5// grid: compare A rows[ay..ay+cmp_h) vs B rows[by..by+cmp_h) over x[0..cmp_w), gx x gy cells; a cell
6// matches when mean-channel delta <= thresh (0..255). extent: last content row vs the page background.
7// license_tier: ORIGINAL
8import "nx_visual_diff.nx"
9import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
10
11func vc_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
12// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
13// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
14// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
15// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
16func vc_n(v: i64) -> i64 { nxi_out(v); return 0 }
17func vc_atoi(s: *u8) -> i64 { var v: i64=0; var i: i64=0; var neg: i64=0; if s[0]==(45 as u8){neg=1;i=1} while s[i]!=(0 as u8){ let c: i64=(s[i] as i64)&255; if c>=48 { if c<=57 { v=v*10+(c-48) } } i=i+1 } if neg==1 { return 0-v } return v }
18func vc_eq(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 1 } return 0 }
19
20func main(argc: i64, argv: *i64) -> i64 {
21 if argc < 3 { vc_w("usage: grid <A> <B> <ay> <by> <w> <h> <gx> <gy> <thresh> | extent <A> <tol>\n" as *u8); sys_exit(2); return 2 }
22 let mode: *u8 = argv[1] as *u8
23
24 if vc_eq(mode, "extent\x00" as *u8) == 1 {
25 let im: *VdImg = vd_load(argv[2] as *u8)
26 if (im as i64) == 0 { vc_w("cannot load PNG\n" as *u8); sys_exit(3); return 3 }
27 var tol: i64 = 12
28 if argc >= 4 { tol = vc_atoi(argv[3] as *u8) }
29 let ext: i64 = vd_content_extent(im, tol)
30 vc_w("image " as *u8); vc_n(im.w); vc_w("x" as *u8); vc_n(im.h)
31 vc_w(" content extends to y=" as *u8); vc_n(ext)
32 vc_w(" (rendered page height in this shot)\n" as *u8)
33 sys_exit(0); return 0
34 }
35
36 if vc_eq(mode, "grid\x00" as *u8) == 1 {
37 if argc < 11 { vc_w("grid needs: A B ay by w h gx gy thresh\n" as *u8); sys_exit(2); return 2 }
38 let a: *VdImg = vd_load(argv[2] as *u8)
39 let b: *VdImg = vd_load(argv[3] as *u8)
40 if (a as i64) == 0 { vc_w("cannot load A\n" as *u8); sys_exit(3); return 3 }
41 if (b as i64) == 0 { vc_w("cannot load B\n" as *u8); sys_exit(3); return 3 }
42 let ay: i64 = vc_atoi(argv[4] as *u8)
43 let by: i64 = vc_atoi(argv[5] as *u8)
44 let cw: i64 = vc_atoi(argv[6] as *u8)
45 let ch: i64 = vc_atoi(argv[7] as *u8)
46 let gx: i64 = vc_atoi(argv[8] as *u8)
47 let gy: i64 = vc_atoi(argv[9] as *u8)
48 let th: i64 = vc_atoi(argv[10] as *u8)
49 let out: *i64 = sys_mmap(256) as *i64
50 let pm: i64 = vd_grid_parity(a, ay, b, by, cw, ch, gx, gy, th, out)
51 vc_w("visual parity: " as *u8); vc_n(pm / 10); vc_w("." as *u8); vc_n(pm % 10); vc_w("% of regions match" as *u8)
52 vc_w(" (" as *u8); vc_n(out[0]); vc_w("/" as *u8); vc_n(out[1])
53 vc_w(" cells, grid " as *u8); vc_n(gx); vc_w("x" as *u8); vc_n(gy)
54 vc_w(", tolerance " as *u8); vc_n(th); vc_w("/255)\n" as *u8)
55 vc_w("worst regions (cell x,y -> mean-color delta):\n" as *u8)
56 var s: i64 = 0
57 while s < VD_WORST_SLOTS {
58 let d: i64 = out[2 + s*3 + 2]
59 if d >= 0 {
60 vc_w(" cell " as *u8); vc_n(out[2+s*3]); vc_w("," as *u8); vc_n(out[2+s*3+1])
61 vc_w(" -> delta " as *u8); vc_n(d); vc_w("\n" as *u8)
62 }
63 s = s + 1
64 }
65 sys_exit(0); return 0
66 }
67
68 vc_w("unknown mode\n" as *u8)
69 sys_exit(2)
70 return 2
71}