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}