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}