code wiki / _hdl_build / nx_uiq_color.nx

nx_uiq_color.nx source

↩ module page · 405 lines · 19388 B

1// nx_uiq_color.nx -- SOVEREIGN UI-QUALITY color engine (no main). 2// The ONE WCAG 2.x relative-luminance + contrast-ratio implementation for the whole nx_uiq_* family, 3// PLUS a CSS color parser (#rgb/#rrggbb/rgb()/named) and a custom-property color-token scanner, so the 4// family computes contrast on the REAL colors extracted from a page instead of one hardcoded token pair. 5// The math is reused verbatim from nx_contrast (KAT'd to the WCAG reference black/white=21.00, 6// #767676/white=4.54). CONSOLIDATION SEED: nx_contrast + nx_ui_contrast + nx_brand_guard migrate onto 7// this on next touch (D001 migrate-on-touch), collapsing 3 duplicate luminance kernels -> 1. 8// license_tier: ORIGINAL genealogy: w3.org/TR/WCAG21 relative-luminance + contrast-ratio (verbatim) 9import "nx_syscalls.nx" 10import "nx_tier.nx" 11import "nx_f64.nx" 12import "nx_f64_div.nx" 13import "nx_f64_cvt.nx" 14import "_pe_f64pow.nx" 15const K_MAGIC_1292: i64 = 1292 16const K_MAGIC_1055: i64 = 1055 17const K_MAGIC_2126: i64 = 2126 18const K_MAGIC_10000: i64 = 10000 19const K_MAGIC_7152: i64 = 7152 20const K_MAGIC_1000000: i64 = 1000000 21const K_MAGIC_2100: i64 = 2100 22const K_MAGIC_767676: i64 = 767676 23const K_MAGIC_2126729: i64 = 2126729 24const K_MAGIC_10000000: i64 = 10000000 25const K_MAGIC_7151522: i64 = 7151522 26const K_MAGIC_721750: i64 = 721750 27const K_MAGIC_22000: i64 = 22000 28const K_MAGIC_1414: i64 = 1414 29const K_MAGIC_100000: i64 = 100000 30 31// ---------- small io ---------- 32func uiq_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 33func uiq_pn(v: i64) -> i64 { 34 let t: *u8=sys_mmap(28); var m: i64=v 35 if m<0 { sys_write(1,"-" as *u8,1); m=0-m } 36 var k: i64=0 37 if m==0 { t[0]=48 as u8; k=1 } 38 while m>0 { t[k]=(48+(m-(m/10)*10)) as u8; m=m/10; k=k+1 } 39 let b: *u8=sys_mmap(28); var i: i64=0 40 while i<k { b[i]=t[k-1-i]; i=i+1 } 41 sys_write(1,b,k); return 0 42} 43// print an x100 ratio as "N.MM" 44func uiq_pratio(r100: i64) -> i64 { 45 uiq_pn(r100/100); uiq_w("." as *u8) 46 let f: i64 = r100-(r100/100)*100 47 if f<10 { uiq_w("0" as *u8) } 48 uiq_pn(f); return 0 49} 50func uiq_wslice(base: *u8, off: i64, len: i64) -> i64 { sys_write(1, ((base as i64)+off) as *u8, len); return 0 } 51 52// ---------- proven WCAG math (verbatim kernel from nx_contrast) ---------- 53func uiq_f64c(num: i64, den: i64) -> i64 { return nx_f64_div(nx_i64_to_f64(num), nx_i64_to_f64(den)) } 54func uiq_hxn(c: i64) -> i64 { 55 if c>=48 { if c<=57 { return c-48 } } 56 if c>=97 { if c<=102 { return c-87 } } 57 if c>=65 { if c<=70 { return c-55 } } 58 return 0-1 59} 60// sRGB channel (0..255) -> linear f64. Threshold cs<=0.03928 applied as the exact integer c<=10 (0.03928*255=10.02). 61func uiq_srgb_lin(c: i64) -> i64 { 62 let cs: i64 = uiq_f64c(c,255) 63 if c<=10 { return nx_f64_div(cs, uiq_f64c(K_MAGIC_1292,100)) } 64 let num: i64 = nx_f64_add(cs, uiq_f64c(55,1000)) 65 let base: i64 = nx_f64_div(num, uiq_f64c(K_MAGIC_1055,1000)) 66 return nx_f64_pow(base, uiq_f64c(24,10)) 67} 68func uiq_lum(r: i64, g: i64, b: i64) -> i64 { 69 let lr: i64 = nx_f64_mul(uiq_f64c(K_MAGIC_2126,K_MAGIC_10000), uiq_srgb_lin(r)) 70 let lg: i64 = nx_f64_mul(uiq_f64c(K_MAGIC_7152,K_MAGIC_10000), uiq_srgb_lin(g)) 71 let lb: i64 = nx_f64_mul(uiq_f64c(722,K_MAGIC_10000), uiq_srgb_lin(b)) 72 return nx_f64_add(nx_f64_add(lr,lg),lb) 73} 74// contrast ratio * 100 as an integer (21.00 -> 2100). 75func uiq_ratio100(fr: i64, fg: i64, fb: i64, br: i64, bg: i64, bb: i64) -> i64 { 76 let lf: i64 = uiq_lum(fr,fg,fb) 77 let lb: i64 = uiq_lum(br,bg,bb) 78 let iif: i64 = nx_f64_to_i64(nx_f64_mul(lf, nx_i64_to_f64(K_MAGIC_1000000))) 79 let iib: i64 = nx_f64_to_i64(nx_f64_mul(lb, nx_i64_to_f64(K_MAGIC_1000000))) 80 var lmax: i64 = lf 81 var lmin: i64 = lb 82 if iib>iif { lmax=lb; lmin=lf } 83 let p05: i64 = uiq_f64c(5,100) 84 let rat: i64 = nx_f64_div(nx_f64_add(lmax,p05), nx_f64_add(lmin,p05)) 85 return nx_f64_to_i64(nx_f64_mul(rat, nx_i64_to_f64(100))) 86} 87 88// ---------- CSS color parsing ---------- 89func uiq_clip(v: i64) -> i64 { if v<0 { return 0 } if v>255 { return 255 } return v } 90func uiq_hex2(base: *u8, off: i64) -> i64 { return uiq_hxn(base[off] as i64)*16 + uiq_hxn(base[off+1] as i64) } 91func uiq_hex1(base: *u8, off: i64) -> i64 { let h: i64=uiq_hxn(base[off] as i64); return h*16+h } 92// pull up to 3 maximal digit-runs from base[off..off+len) -> out[0..2]; returns how many found. 93func uiq_three_ints(base: *u8, off: i64, len: i64, out: *i64) -> i64 { 94 var got: i64=0; var i: i64=off; let end: i64=off+len 95 while i<end { 96 let c: i64=base[i] as i64 97 if c>=48 { if c<=57 { 98 var v: i64=0; var j: i64=i; var run: i64=1 99 while run==1 { if j<end { let d: i64=base[j] as i64; if d>=48 { if d<=57 { v=v*10+(d-48); j=j+1 } else { run=0 } } else { run=0 } } else { run=0 } } 100 if got<3 { out[got]=uiq_clip(v); got=got+1 } 101 i=j 102 } else { i=i+1 } } else { i=i+1 } 103 } 104 return got 105} 106func uiq_word_eq(base: *u8, off: i64, len: i64, lit: *u8) -> i64 { 107 var i: i64=0 108 while lit[i]!=(0 as u8) { if i>=len { return 0 } if base[off+i]!=lit[i] { return 0 } i=i+1 } 109 return 1 110} 111// parse a color slice base[off..off+len) -> out[0..2]=r,g,b ; 1 ok / 0 unparseable. 112func uiq_parse_color(base: *u8, off: i64, len: i64, out: *i64) -> i64 { 113 if len<=0 { return 0 } 114 let c0: i64=base[off] as i64 115 if c0==35 { // '#' 116 var nd: i64=0; var run: i64=1 117 while run==1 { if 1+nd<len { if uiq_hxn(base[off+1+nd] as i64)>=0 { nd=nd+1 } else { run=0 } } else { run=0 } } 118 if nd>=6 { out[0]=uiq_hex2(base,off+1); out[1]=uiq_hex2(base,off+3); out[2]=uiq_hex2(base,off+5); return 1 } 119 if nd>=3 { out[0]=uiq_hex1(base,off+1); out[1]=uiq_hex1(base,off+2); out[2]=uiq_hex1(base,off+3); return 1 } 120 return 0 121 } 122 if c0==114 { // 'r' -> rgb(/rgba( 123 if uiq_three_ints(base,off,len,out)==3 { return 1 } 124 return 0 125 } 126 if uiq_word_eq(base,off,len,"white" as *u8)==1 { out[0]=255; out[1]=255; out[2]=255; return 1 } 127 if uiq_word_eq(base,off,len,"black" as *u8)==1 { out[0]=0; out[1]=0; out[2]=0; return 1 } 128 return 0 129} 130 131// ---------- page color-token scanner ---------- 132// scan for `--name: <color>` ; record name offset/len (after the leading --) + parsed rgb (3 slots/token). 133// returns token count (bounded by cap). 134func uiq_scan_tokens(buf: *u8, n: i64, toff: *i64, tlen: *i64, trgb: *i64, cap: i64) -> i64 { 135 var count: i64=0; var i: i64=0 136 let rgb: *i64 = sys_mmap(32) as *i64 137 while i+3<n { 138 // a real custom property is `--<alpha|_>...`; the alpha guard rejects HTML `<!--`/`-->`. 139 var isname: i64=0 140 if buf[i]==(45 as u8) { if buf[i+1]==(45 as u8) { 141 let c2: i64=buf[i+2] as i64 142 if c2>=97 { if c2<=122 { isname=1 } } 143 if c2>=65 { if c2<=90 { isname=1 } } 144 if c2==95 { isname=1 } 145 } } 146 if isname==1 { 147 var ne: i64=i+2; var run: i64=1 148 while run==1 { if ne<n { let c: i64=buf[ne] as i64; if c==58 { run=0 } else { if c==59 { run=0 } else { if c==123 { run=0 } else { if c==125 { run=0 } else { ne=ne+1 } } } } } else { run=0 } } 149 if ne<n { if buf[ne]==(58 as u8) { 150 var vs: i64=ne+1; var sk: i64=1 151 while sk==1 { if vs<n { if buf[vs]==(32 as u8) { vs=vs+1 } else { sk=0 } } else { sk=0 } } 152 var ve: i64=vs; var run2: i64=1 153 while run2==1 { if ve<n { let d: i64=buf[ve] as i64; if d==59 { run2=0 } else { if d==125 { run2=0 } else { if d==10 { run2=0 } else { ve=ve+1 } } } } else { run2=0 } } 154 if uiq_parse_color(buf,vs,ve-vs,rgb)==1 { 155 if count<cap { 156 toff[count]=i+2; tlen[count]=ne-(i+2) 157 trgb[3*count]=rgb[0]; trgb[3*count+1]=rgb[1]; trgb[3*count+2]=rgb[2] 158 count=count+1 159 } 160 } 161 i=ve 162 } else { i=ne } } else { i=ne } 163 } else { i=i+1 } 164 } 165 return count 166} 167 168// ---------- role classification (name at buf[off..off+len)) ---------- 169func uiq_name_eq(buf: *u8, off: i64, len: i64, lit: *u8) -> i64 { 170 var i: i64=0 171 while lit[i]!=(0 as u8) { if i>=len { return 0 } if buf[off+i]!=lit[i] { return 0 } i=i+1 } 172 if i!=len { return 0 } 173 return 1 174} 175func uiq_name_has(buf: *u8, off: i64, len: i64, lit: *u8) -> i64 { 176 var ll: i64=0; while lit[ll]!=(0 as u8) { ll=ll+1 } 177 if ll==0 { return 0 } 178 var i: i64=0 179 while i+ll<=len { 180 var j: i64=0; var ok: i64=1 181 while j<ll { if buf[off+i+j]!=lit[j] { ok=0; j=ll } else { j=j+1 } } 182 if ok==1 { return 1 } 183 i=i+1 184 } 185 return 0 186} 187// text-role: exact short accent names OR text-ish substrings. 188func uiq_is_text(buf: *u8, off: i64, len: i64) -> i64 { 189 if uiq_name_eq(buf,off,len,"ac" as *u8)==1 { return 1 } 190 if uiq_name_eq(buf,off,len,"accent" as *u8)==1 { return 1 } 191 if uiq_name_eq(buf,off,len,"primary" as *u8)==1 { return 1 } 192 if uiq_name_eq(buf,off,len,"brand" as *u8)==1 { return 1 } 193 if uiq_name_has(buf,off,len,"fg" as *u8)==1 { return 1 } 194 if uiq_name_has(buf,off,len,"ink" as *u8)==1 { return 1 } 195 if uiq_name_has(buf,off,len,"text" as *u8)==1 { return 1 } 196 if uiq_name_has(buf,off,len,"mut" as *u8)==1 { return 1 } 197 if uiq_name_has(buf,off,len,"fore" as *u8)==1 { return 1 } 198 if uiq_name_has(buf,off,len,"label" as *u8)==1 { return 1 } 199 if uiq_name_has(buf,off,len,"link" as *u8)==1 { return 1 } 200 if uiq_name_has(buf,off,len,"title" as *u8)==1 { return 1 } 201 return 0 202} 203// surface-role: background/surface-ish substrings. 204func uiq_is_surface(buf: *u8, off: i64, len: i64) -> i64 { 205 if uiq_name_has(buf,off,len,"bg" as *u8)==1 { return 1 } 206 if uiq_name_has(buf,off,len,"surface" as *u8)==1 { return 1 } 207 if uiq_name_has(buf,off,len,"soft" as *u8)==1 { return 1 } 208 if uiq_name_has(buf,off,len,"paper" as *u8)==1 { return 1 } 209 if uiq_name_has(buf,off,len,"card" as *u8)==1 { return 1 } 210 if uiq_name_has(buf,off,len,"panel" as *u8)==1 { return 1 } 211 return 0 212} 213 214// substring search: byte offset of pat in buf[0..n), or -1. 215func uiq_find(buf: *u8, n: i64, pat: *u8) -> i64 { 216 var pl: i64=0; while pat[pl]!=(0 as u8) { pl=pl+1 } 217 if pl==0 { return 0-1 } 218 var i: i64=0 219 while i+pl<=n { 220 var j: i64=0; var ok: i64=1 221 while j<pl { if buf[i+j]!=pat[j] { ok=0; j=pl } else { j=j+1 } } 222 if ok==1 { return i } 223 i=i+1 224 } 225 return 0-1 226} 227func uiq_names_equal(buf: *u8, o1: i64, l1: i64, o2: i64, l2: i64) -> i64 { 228 if l1!=l2 { return 0 } 229 var i: i64=0 230 while i<l1 { if buf[o1+i]!=buf[o2+i] { return 0 } i=i+1 } 231 return 1 232} 233 234// audit ONE resolved theme table (unique names + one rgb per name). flags = emit | (theme<<1); 235// theme 0=default 1=light 2=dark. AA normal-text floor = 4.50 (450). emits a JSON theme object when emit set. 236func uiq_audit_table(buf: *u8, uoff: *i64, ulen: *i64, rgb: *i64, un: i64, flags: i64) -> i64 { 237 let emit: i64 = flags & 1 238 let theme: i64 = flags >> 1 239 var fails: i64=0; var pairs: i64=0; var first: i64=1 240 if emit==1 { 241 uiq_w("{\"theme\":\"" as *u8) 242 if theme==2 { uiq_w("dark" as *u8) } else { if theme==1 { uiq_w("light" as *u8) } else { uiq_w("default" as *u8) } } 243 uiq_w("\",\"failing\":[" as *u8) 244 } 245 var a: i64=0 246 while a<un { 247 if uiq_is_text(buf,uoff[a],ulen[a])==1 { 248 var b: i64=0 249 while b<un { 250 if b!=a { if uiq_is_surface(buf,uoff[b],ulen[b])==1 { 251 let r: i64 = uiq_ratio100(rgb[3*a],rgb[3*a+1],rgb[3*a+2], rgb[3*b],rgb[3*b+1],rgb[3*b+2]) 252 pairs=pairs+1 253 if r<450 { 254 fails=fails+1 255 if emit==1 { 256 if first==0 { uiq_w("," as *u8) } 257 first=0 258 uiq_w("{\"fg\":\"--" as *u8); uiq_wslice(buf,uoff[a],ulen[a]) 259 uiq_w("\",\"bg\":\"--" as *u8); uiq_wslice(buf,uoff[b],ulen[b]) 260 uiq_w("\",\"ratio\":" as *u8); uiq_pratio(r); uiq_w("}" as *u8) 261 } 262 } 263 } } 264 b=b+1 265 } 266 } 267 a=a+1 268 } 269 if emit==1 { uiq_w("],\"pairs\":" as *u8); uiq_pn(pairs); uiq_w(",\"fails\":" as *u8); uiq_pn(fails); uiq_w("}" as *u8) } 270 return fails 271} 272 273// full audit: SCOPE-AWARE. Base :root defines the light/default theme; a prefers-color-scheme:dark block 274// overrides some tokens (others inherit). Light and dark tables are audited SEPARATELY so tokens from 275// different themes are NEVER cross-paired (which would fabricate ratios like dark-fg on light-bg). emit=1 276// prints JSON; returns total failing pairs across themes. 277func uiq_audit_buf(buf: *u8, n: i64, emit: i64) -> i64 { 278 let cap: i64=96 279 let toff: *i64 = sys_mmap(cap*8) as *i64 280 let tlen: *i64 = sys_mmap(cap*8) as *i64 281 let trgb: *i64 = sys_mmap(cap*24) as *i64 282 let nt: i64 = uiq_scan_tokens(buf,n,toff,tlen,trgb,cap) 283 let darkStart: i64 = uiq_find(buf,n,"prefers-color-scheme" as *u8) 284 // resolve unique names -> light rgb + dark rgb (dark inherits base unless overridden after darkStart) 285 let uoff: *i64 = sys_mmap(cap*8) as *i64 286 let ulen: *i64 = sys_mmap(cap*8) as *i64 287 let lrgb: *i64 = sys_mmap(cap*24) as *i64 288 let drgb: *i64 = sys_mmap(cap*24) as *i64 289 let dset: *i64 = sys_mmap(cap*8) as *i64 290 var un: i64=0 291 var a: i64=0 292 while a<nt { 293 var idx: i64=0-1 294 var u: i64=0 295 while u<un { if uiq_names_equal(buf,toff[a],tlen[a],uoff[u],ulen[u])==1 { idx=u; u=un } else { u=u+1 } } 296 var isDark: i64=0 297 if darkStart>=0 { if toff[a]>=darkStart { isDark=1 } } 298 if idx<0 { 299 uoff[un]=toff[a]; ulen[un]=tlen[a] 300 lrgb[3*un]=trgb[3*a]; lrgb[3*un+1]=trgb[3*a+1]; lrgb[3*un+2]=trgb[3*a+2] 301 drgb[3*un]=trgb[3*a]; drgb[3*un+1]=trgb[3*a+1]; drgb[3*un+2]=trgb[3*a+2] 302 dset[un]=isDark 303 un=un+1 304 } else { 305 if isDark==1 { 306 drgb[3*idx]=trgb[3*a]; drgb[3*idx+1]=trgb[3*a+1]; drgb[3*idx+2]=trgb[3*a+2]; dset[idx]=1 307 } else { 308 lrgb[3*idx]=trgb[3*a]; lrgb[3*idx+1]=trgb[3*a+1]; lrgb[3*idx+2]=trgb[3*a+2] 309 if dset[idx]==0 { drgb[3*idx]=trgb[3*a]; drgb[3*idx+1]=trgb[3*a+1]; drgb[3*idx+2]=trgb[3*a+2] } 310 } 311 } 312 a=a+1 313 } 314 var total: i64=0 315 if emit==1 { uiq_w("{\"tokens\":" as *u8); uiq_pn(nt); uiq_w(",\"names\":" as *u8); uiq_pn(un); uiq_w(",\"themes\":[" as *u8) } 316 if darkStart<0 { 317 total = total + uiq_audit_table(buf,uoff,ulen,lrgb,un, emit) 318 } else { 319 total = total + uiq_audit_table(buf,uoff,ulen,lrgb,un, emit | 2) 320 if emit==1 { uiq_w("," as *u8) } 321 total = total + uiq_audit_table(buf,uoff,ulen,drgb,un, emit | 4) 322 } 323 if emit==1 { 324 uiq_w("],\"fails\":" as *u8); uiq_pn(total) 325 uiq_w(",\"verdict\":\"" as *u8) 326 if total==0 { uiq_w("PASS" as *u8) } else { uiq_w("FAIL" as *u8) } 327 uiq_w("\"}\n" as *u8) 328 } 329 return total 330} 331 332// ---------- self-test: proves the math, the parser, AND that the audit catches a REAL fail ---------- 333// returns the number of failed checks (0 = GREEN). Shared by nx_uiq_contrast --kat and the gate. 334func uiq_selftest() -> i64 { 335 var f: i64=0 336 let rgb: *i64 = sys_mmap(32) as *i64 337 // WCAG reference values (the anti-navel-gaze anchor: the W3C's numbers, not ours) 338 if uiq_ratio100(0,0,0,255,255,255)!=K_MAGIC_2100 { f=f+1 } // black/white = 21.00 339 let g6: i64=uiq_ratio100(118,118,118,255,255,255) // #K_MAGIC_767676/white = 4.54 340 if g6<452 { f=f+1 } 341 if g6>456 { f=f+1 } 342 // parser: rgb() (front-door style) 343 if uiq_parse_color("rgb(104,104,118)" as *u8,0,16,rgb)!=1 { f=f+1 } 344 if rgb[0]!=104 { f=f+1 } 345 if rgb[2]!=118 { f=f+1 } 346 // parser: #rrggbb 347 if uiq_parse_color("#2a4d8f" as *u8,0,7,rgb)!=1 { f=f+1 } 348 if rgb[0]!=42 { f=f+1 } 349 if rgb[1]!=77 { f=f+1 } 350 if rgb[2]!=143 { f=f+1 } 351 // parser: #rgb short 352 if uiq_parse_color("#fff" as *u8,0,4,rgb)!=1 { f=f+1 } 353 if rgb[0]!=255 { f=f+1 } 354 // audit PASS fixture: --text #111 on --bg #fff ~ 18.9:1 -> 0 fails 355 let pa: *u8 = ":root{--text:#111;--bg:#fff}" as *u8 356 var pan: i64=0; while pa[pan]!=(0 as u8) { pan=pan+1 } 357 if uiq_audit_buf(pa,pan,0)!=0 { f=f+1 } 358 // audit FAIL fixture: --mut #999 on --bg #fff ~ 2.85:1 -> exactly 1 fail (NON-VACUOUS) 359 let fbx: *u8 = ":root{--mut:#999;--bg:#fff}" as *u8 360 var fbn: i64=0; while fbx[fbn]!=(0 as u8) { fbn=fbn+1 } 361 if uiq_audit_buf(fbx,fbn,0)!=1 { f=f+1 } 362 // NEG-CONTROL: no color tokens -> 0 fails (no false positive) 363 let nc: *u8 = "<h1>hi</h1>" as *u8 364 var ncn: i64=0; while nc[ncn]!=(0 as u8) { ncn=ncn+1 } 365 if uiq_audit_buf(nc,ncn,0)!=0 { f=f+1 } 366 return f 367} 368 369// ---------- APCA (WCAG3 frontier) Lc: perceptual, polarity-aware -- absorbed here so the family has ONE 370// contrast kernel (consolidation: nx_contrast becomes a thin CLI over these; nx_ui_contrast/nx_brand_guard 371// migrate on next touch, D001). Math verbatim from nx_contrast (KAT'd blk-on-wht Lc ~106.0). ---------- 372func uiq_apca_exp(c: i64) -> i64 { return nx_f64_pow(uiq_f64c(c, 255), uiq_f64c(24, 10)) } 373func uiq_apca_y(r: i64, g: i64, b: i64) -> i64 { 374 let yr: i64 = nx_f64_mul(uiq_f64c(K_MAGIC_2126729, K_MAGIC_10000000), uiq_apca_exp(r)) 375 let yg: i64 = nx_f64_mul(uiq_f64c(K_MAGIC_7151522, K_MAGIC_10000000), uiq_apca_exp(g)) 376 let yb: i64 = nx_f64_mul(uiq_f64c(K_MAGIC_721750, K_MAGIC_10000000), uiq_apca_exp(b)) 377 var y: i64 = nx_f64_add(nx_f64_add(yr, yg), yb) 378 let iy: i64 = nx_f64_to_i64(nx_f64_mul(y, nx_i64_to_f64(K_MAGIC_1000000))) 379 if iy < K_MAGIC_22000 { 380 let d: i64 = nx_f64_sub(uiq_f64c(22, 1000), y) 381 y = nx_f64_add(y, nx_f64_pow(d, uiq_f64c(K_MAGIC_1414, 1000))) 382 } 383 return y 384} 385// APCA Lc * 10 (one decimal, signed): + = dark text on light, - = light text on dark. 386func uiq_apca_lc10(fr: i64, fg: i64, fb: i64, br: i64, bg: i64, bb: i64) -> i64 { 387 let ytxt: i64 = uiq_apca_y(fr, fg, fb) 388 let ybg: i64 = uiq_apca_y(br, bg, bb) 389 let itxt: i64 = nx_f64_to_i64(nx_f64_mul(ytxt, nx_i64_to_f64(K_MAGIC_1000000))) 390 let ibg: i64 = nx_f64_to_i64(nx_f64_mul(ybg, nx_i64_to_f64(K_MAGIC_1000000))) 391 var dd: i64 = ibg - itxt 392 if dd < 0 { dd = 0 - dd } 393 if dd < 500 { return 0 } 394 var oc: i64 = nx_i64_to_f64(0) 395 if ibg > itxt { 396 let sn: i64 = nx_f64_mul(nx_f64_sub(nx_f64_pow(ybg, uiq_f64c(56, 100)), nx_f64_pow(ytxt, uiq_f64c(57, 100))), uiq_f64c(114, 100)) 397 let isn: i64 = nx_f64_to_i64(nx_f64_mul(sn, nx_i64_to_f64(K_MAGIC_1000000))) 398 if isn < K_MAGIC_100000 { oc = nx_i64_to_f64(0) } else { oc = nx_f64_sub(sn, uiq_f64c(27, 1000)) } 399 } else { 400 let sr: i64 = nx_f64_mul(nx_f64_sub(nx_f64_pow(ybg, uiq_f64c(65, 100)), nx_f64_pow(ytxt, uiq_f64c(62, 100))), uiq_f64c(114, 100)) 401 let isr: i64 = nx_f64_to_i64(nx_f64_mul(sr, nx_i64_to_f64(K_MAGIC_1000000))) 402 if isr > (0 - K_MAGIC_100000) { oc = nx_i64_to_f64(0) } else { oc = nx_f64_add(sr, uiq_f64c(27, 1000)) } 403 } 404 return nx_f64_to_i64(nx_f64_mul(oc, nx_i64_to_f64(1000))) 405}