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}