code wiki / _hdl_build / nx_contrast.nx

nx_contrast.nx source

↩ module page · 91 lines · 4451 B

1// nx_contrast.nx -- SOVEREIGN WCAG 2.1 + APCA contrast CLI. 2// ★CONSOLIDATED 2026-07-21: the luminance/contrast/APCA KERNEL now lives in nx_uiq_color (the ONE family 3// contrast kernel); this is a THIN CLI over it (OO consolidation, D001 migrate-on-touch). The duplicate 4// srgb_lin/lum/ratio100/apca_* that used to live here are DELETED -> no behaviour lost, one source of truth. 5// Behaviour-preserving, PROVEN by --kat reproducing the reference values (21.00 / 4.54 / APCA 106.0 / -107.9 / 63.1). 6// usage: nx_contrast <fg-hex> <bg-hex> e.g. nx_contrast 767676 ffffff | nx_contrast "#000" "#fff" 7// emits JSON: {"fg","bg","ratio","aa","aaa","aa_large","apca_lc"} | --kat runs the reference battery 8// license_tier: ORIGINAL genealogy: w3.org/TR/WCAG21 relative-luminance + contrast-ratio 9import "nx_uiq_color.nx" 10const K_MAGIC_2100: i64 = 2100 11const K_MAGIC_1050: i64 = 1050 12const K_MAGIC_1070: i64 = 1070 13const K_MAGIC_1090: i64 = 1090 14 15// parse "#rrggbb" or "rrggbb" -> out[0..2]=r,g,b ; 0 ok / -1 bad. (hex helper shared: uiq_hxn.) 16func parse_hex(s: *u8, out: *i64) -> i64 { 17 var i: i64=0 18 if s[0]==(35 as u8) { i=1 } 19 var v: i64=0; var k: i64=0 20 while k<6 { let h: i64=uiq_hxn(s[i+k] as i64); if h<0 { return 0-1 } v=(v<<4)|h; k=k+1 } 21 out[0]=(v>>16)&255; out[1]=(v>>8)&255; out[2]=v&255 22 return 0 23} 24func pbool(b: i64) -> i64 { if b==1 { uiq_w("true" as *u8) } else { uiq_w("false" as *u8) } return 0 } 25func papca(v: i64) -> i64 { 26 var m: i64=v 27 if m<0 { uiq_w("-" as *u8); m=0-m } 28 uiq_pn(m/10); uiq_w("." as *u8); uiq_pn(m%10) 29 return 0 30} 31 32func run_kat() -> i64 { 33 uiq_w("=== nx_contrast KAT (consolidated onto nx_uiq_color kernel) ===\n" as *u8) 34 var fails: i64=0 35 let bw: i64 = uiq_ratio100(0,0,0,255,255,255) 36 uiq_w(" black/white = " as *u8); uiq_pratio(bw); uiq_w(" expect 21.00\n" as *u8) 37 if bw!=K_MAGIC_2100 { fails=fails+1 } 38 let ww: i64 = uiq_ratio100(255,255,255,255,255,255) 39 uiq_w(" white/white = " as *u8); uiq_pratio(ww); uiq_w(" expect 1.00\n" as *u8) 40 if ww!=100 { fails=fails+1 } 41 let g6: i64 = uiq_ratio100(118,118,118,255,255,255) 42 uiq_w(" #767676/white = " as *u8); uiq_pratio(g6); uiq_w(" expect ~4.54\n" as *u8) 43 if g6<452 { fails=fails+1 } 44 if g6>456 { fails=fails+1 } 45 let ka: i64 = uiq_apca_lc10(0,0,0,255,255,255) 46 uiq_w(" APCA blk-on-wht Lc = " as *u8); papca(ka); uiq_w(" expect ~106.0\n" as *u8) 47 if ka<K_MAGIC_1050 { fails=fails+1 } 48 if ka>K_MAGIC_1070 { fails=fails+1 } 49 let kw: i64 = uiq_apca_lc10(255,255,255,0,0,0) 50 uiq_w(" APCA wht-on-blk Lc = " as *u8); papca(kw); uiq_w(" expect ~-107.9\n" as *u8) 51 if kw>(0-K_MAGIC_1070) { fails=fails+1 } 52 if kw<(0-K_MAGIC_1090) { fails=fails+1 } 53 let k8: i64 = uiq_apca_lc10(136,136,136,255,255,255) 54 uiq_w(" APCA #888-on-wht Lc = " as *u8); papca(k8); uiq_w(" expect ~63.1\n" as *u8) 55 if k8<620 { fails=fails+1 } 56 if k8>640 { fails=fails+1 } 57 uiq_w("KAT fails=" as *u8); uiq_pn(fails); uiq_w("\n" as *u8) 58 if fails==0 { uiq_w("VERDICT=GREEN\n" as *u8); return 0 } 59 uiq_w("VERDICT=RED\n" as *u8) 60 return fails 61} 62 63func main(argc: i64, argv: *i64) -> i64 { 64 if argc>=2 { 65 let a1: *u8 = argv[1] as *u8 66 if a1[0]==(45 as u8) { let f: i64=run_kat(); sys_exit(f); return f } 67 } 68 if argc<3 { uiq_w("usage: nx_contrast <fg-hex> <bg-hex> (or --kat)\n" as *u8); sys_exit(2); return 2 } 69 let fg: *i64 = sys_mmap(32) as *i64 70 let bg: *i64 = sys_mmap(32) as *i64 71 let a1: *u8 = argv[1] as *u8 72 let a2: *u8 = argv[2] as *u8 73 if parse_hex(a1, fg)!=0 { uiq_w("{\"error\":\"bad fg hex (want 6-digit)\"}\n" as *u8); sys_exit(2); return 2 } 74 if parse_hex(a2, bg)!=0 { uiq_w("{\"error\":\"bad bg hex (want 6-digit)\"}\n" as *u8); sys_exit(2); return 2 } 75 let r100: i64 = uiq_ratio100(fg[0],fg[1],fg[2], bg[0],bg[1],bg[2]) 76 var aa: i64=0 77 if r100>=450 { aa=1 } 78 var aaa: i64=0 79 if r100>=700 { aaa=1 } 80 var aal: i64=0 81 if r100>=300 { aal=1 } 82 uiq_w("{\"fg\":\"" as *u8); uiq_w(a1); uiq_w("\",\"bg\":\"" as *u8); uiq_w(a2) 83 uiq_w("\",\"ratio\":" as *u8); uiq_pratio(r100) 84 uiq_w(",\"aa\":" as *u8); pbool(aa) 85 uiq_w(",\"aaa\":" as *u8); pbool(aaa) 86 uiq_w(",\"aa_large\":" as *u8); pbool(aal) 87 let lc10: i64 = uiq_apca_lc10(fg[0],fg[1],fg[2], bg[0],bg[1],bg[2]) 88 uiq_w(",\"apca_lc\":" as *u8); papca(lc10) 89 uiq_w("}\n" as *u8) 90 sys_exit(0); return 0 91}