nx_ttf_subpixel.nx source
↩ module page · 199 lines · 10712 B
1// nx_ttf_subpixel.nx -- SUBPIXEL / ClearType rendering (L1-2 LCD crispness rung). On an RGB-stripe LCD each pixel
2// is 3 side-by-side R,G,B subpixels; rendering at 3x horizontal resolution and driving each subpixel independently
3// TRIPLES effective horizontal sharpness. Parses our .ttf, computes 3x-horizontal coverage with vertical AA, applies
4// the standard [1,2,3,2,1]/9 ClearType filter per subpixel -> a REAL 24-bit BMP (web_assets/nishi_subpixel.bmp) you
5// can open. Dark text on white; slight R/B color fringe at edges = the ClearType look (crisper at native size on an
6// RGB LCD). Works directly with our coverage -- NO outline-merge/model needed. 100% sovereign. expect_exit: 0 license_tier: ORIGINAL
7import "nx_syscalls_x86_64.nx"
8const K_MAGIC_32768: i64 = 32768
9const K_MAGIC_65536: i64 = 65536
10const K_MAGIC_16777216: i64 = 16777216
11const K_MAGIC_2048: i64 = 2048
12const K_MAGIC_2835: i64 = 2835
13
14func sw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
15func sn(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1} sys_write(1,bb,k); return 0 }
16func rd16(b: *u8, o: i64) -> i64 { return (b[o] as i64)*256 + (b[o+1] as i64) }
17func rd16s(b: *u8, o: i64) -> i64 { let v: i64=rd16(b,o); if v>=K_MAGIC_32768 { return v-K_MAGIC_65536 } return v }
18func rd32(b: *u8, o: i64) -> i64 { return (b[o] as i64)*K_MAGIC_16777216 + (b[o+1] as i64)*K_MAGIC_65536 + (b[o+2] as i64)*256 + (b[o+3] as i64) }
19func bit(f: i64, m: i64) -> i64 { if (f/m)%2 == 1 { return 1 } return 0 }
20func wl16(b: *u8, o: i64, v: i64) -> i64 { b[o]=(v%256) as u8; b[o+1]=((v/256)%256) as u8; return o+2 }
21func wl32(b: *u8, o: i64, v: i64) -> i64 { b[o]=(v%256) as u8; b[o+1]=((v/256)%256) as u8; b[o+2]=((v/K_MAGIC_65536)%256) as u8; b[o+3]=((v/K_MAGIC_16777216)%256) as u8; return o+4 }
22func find_table(b: *u8, t0: i64, t1: i64, t2: i64, t3: i64) -> i64 {
23 let nt: i64=rd16(b,4); var i: i64=0
24 while i<nt { let d: i64=12+i*16
25 if (b[d] as i64)==t0 { if (b[d+1] as i64)==t1 { if (b[d+2] as i64)==t2 { if (b[d+3] as i64)==t3 { return rd32(b,d+8) } } } }
26 i=i+1 }
27 return 0-1
28}
29func cmap_lookup(b: *u8, sub: i64, c: i64) -> i64 {
30 let segX2: i64=rd16(b,sub+6); let segCount: i64=segX2/2; var i: i64=0
31 while i<segCount { let endc: i64=rd16(b,sub+14+i*2)
32 if endc>=c { let startc: i64=rd16(b,sub+16+segX2+i*2)
33 if startc<=c { let iro: i64=rd16(b,sub+16+segX2*3+i*2)
34 if iro==0 { let delta: i64=rd16s(b,sub+16+segX2*2+i*2); return (c+delta)%K_MAGIC_65536 }
35 return 0 }
36 return 0 }
37 i=i+1 }
38 return 0
39}
40func glyph_edges(b: *u8, glyf: i64, loca: i64, locfmt: i64, gid: i64, ex0: *i64, ey0: *i64, ex1: *i64, ey1: *i64) -> i64 {
41 var g0: i64=0; var g1: i64=0
42 if locfmt==0 { g0=rd16(b,loca+gid*2)*2; g1=rd16(b,loca+gid*2+2)*2 } else { g0=rd32(b,loca+gid*4); g1=rd32(b,loca+gid*4+4) }
43 if g1<=g0 { return 0 }
44 let gp: i64=glyf+g0; let nc: i64=rd16s(b,gp)
45 if nc<=0 { return 0 }
46 let ep: *i64=sys_mmap(256) as *i64
47 var i: i64=0
48 while i<nc { ep[i]=rd16(b,gp+10+i*2); i=i+1 }
49 let np: i64=ep[nc-1]+1
50 let instr: i64=rd16(b,gp+10+nc*2)
51 var p: i64=gp+10+nc*2+2+instr
52 let FL: *i64=sys_mmap(K_MAGIC_2048) as *i64; let PX: *i64=sys_mmap(K_MAGIC_2048) as *i64; let PY: *i64=sys_mmap(K_MAGIC_2048) as *i64; let ON: *i64=sys_mmap(K_MAGIC_2048) as *i64
53 var fi: i64=0
54 while fi<np { let f: i64=b[p] as i64; p=p+1; FL[fi]=f; fi=fi+1
55 if bit(f,8)==1 { var rep: i64=b[p] as i64; p=p+1; while rep>0 { FL[fi]=f; fi=fi+1; rep=rep-1 } } }
56 var x: i64=0; i=0
57 while i<np { let f: i64=FL[i]
58 if bit(f,2)==1 { var dx: i64=b[p] as i64; p=p+1; if bit(f,16)==0 { dx=0-dx } x=x+dx }
59 else { if bit(f,16)==0 { x=x+rd16s(b,p); p=p+2 } }
60 PX[i]=x; i=i+1 }
61 var y: i64=0; i=0
62 while i<np { let f: i64=FL[i]
63 if bit(f,4)==1 { var dy: i64=b[p] as i64; p=p+1; if bit(f,32)==0 { dy=0-dy } y=y+dy }
64 else { if bit(f,32)==0 { y=y+rd16s(b,p); p=p+2 } }
65 PY[i]=y; ON[i]=bit(f,1); i=i+1 }
66 var ne: i64=0; var cs: i64=0; var ci: i64=0
67 while ci<nc { let ce: i64=ep[ci]; let cnt: i64=ce-cs+1
68 var curx: i64=PX[cs]; var cury: i64=PY[cs]; var j: i64=1
69 while j<=cnt { let idx: i64=cs + (j%cnt)
70 if ON[idx]==1 { ex0[ne]=curx; ey0[ne]=cury; ex1[ne]=PX[idx]; ey1[ne]=PY[idx]; ne=ne+1; curx=PX[idx]; cury=PY[idx]; j=j+1 }
71 else { let eidx: i64=cs + ((j+1)%cnt); let cxp: i64=PX[idx]; let cyp: i64=PY[idx]; let exp: i64=PX[eidx]; let eyp: i64=PY[eidx]
72 var t: i64=1; var pxp: i64=curx; var pyp: i64=cury
73 while t<=6 { let u: i64=6-t
74 let qx: i64=(u*u*curx + 2*u*t*cxp + t*t*exp)/36; let qy: i64=(u*u*cury + 2*u*t*cyp + t*t*eyp)/36
75 ex0[ne]=pxp; ey0[ne]=pyp; ex1[ne]=qx; ey1[ne]=qy; ne=ne+1; pxp=qx; pyp=qy; t=t+1 }
76 curx=exp; cury=eyp; j=j+2 } }
77 cs=ce+1; ci=ci+1 }
78 return ne
79}
80func inside(gx: i64, gy: i64, ex0: *i64, ey0: *i64, ex1: *i64, ey1: *i64, ne: i64) -> i64 {
81 var w: i64=0; var i: i64=0
82 while i<ne { let y0: i64=ey0[i]; let y1: i64=ey1[i]; var dir: i64=0
83 if y0<=gy { if gy<y1 { dir=1 } }
84 if y1<=gy { if gy<y0 { dir=0-1 } }
85 if dir!=0 { let xc: i64=ex0[i] + (gy-y0)*(ex1[i]-ex0[i])/(y1-y0); if xc>gx { w=w+dir } }
86 i=i+1 }
87 if w!=0 { return 1 }
88 return 0
89}
90
91func main() -> i64 {
92 let len_p: *i64 = sys_mmap(8) as *i64
93 let b: *u8 = sys_read_file_x86_64("web_assets/nishi_sans.ttf" as *u8, len_p)
94 if b == (0 as *u8) { sw("cannot read nishi_sans.ttf\n" as *u8); return 5 }
95 let glyf: i64=find_table(b, 103,108,121,102); let loca: i64=find_table(b, 108,111,99,97)
96 let head: i64=find_table(b, 104,101,97,100); let cmap: i64=find_table(b, 99,109,97,112)
97 let locfmt: i64=rd16s(b,head+50); let sub: i64=cmap+rd32(b,cmap+8)
98
99 // decode "Nishi"
100 let word: *i64=sys_mmap(64) as *i64
101 word[0]=78; word[1]=105; word[2]=115; word[3]=104; word[4]=105
102 let NW: i64=5; let MAXE: i64=400
103 let ex0: *i64=sys_mmap(NW*MAXE*8) as *i64; let ey0: *i64=sys_mmap(NW*MAXE*8) as *i64
104 let ex1: *i64=sys_mmap(NW*MAXE*8) as *i64; let ey1: *i64=sys_mmap(NW*MAXE*8) as *i64
105 let nedg: *i64=sys_mmap(64) as *i64
106 var s: i64=0
107 while s<NW {
108 let gid: i64=cmap_lookup(b, sub, word[s]); let base: i64=s*MAXE
109 nedg[s]=glyph_edges(b, glyf, loca, locfmt, gid, ((ex0 as i64)+base*8) as *i64, ((ey0 as i64)+base*8) as *i64, ((ex1 as i64)+base*8) as *i64, ((ey1 as i64)+base*8) as *i64)
110 s=s+1
111 }
112
113 let CW: i64=40; let H: i64=60; let W: i64=CW*NW; let SSv: i64=3
114 let TC: i64=3*CW // sub-columns per glyph cell
115 let X0: i64=0-40; let X1: i64=680; let YT: i64=760; let YB: i64=0-200
116 let rgb: *u8=sys_mmap(W*H*3) // top-down RGB
117
118 // per glyph: build 3x-horizontal coverage, filter to subpixels, write into rgb
119 s=0
120 while s<NW {
121 let base: i64=s*MAXE
122 let cov3: *i64=sys_mmap(H*TC*8)
123 var py: i64=0
124 while py<H {
125 var sc: i64=0
126 while sc<TC {
127 let gx: i64=X0 + sc*(X1-X0)/TC
128 var c: i64=0; var sv: i64=0
129 while sv<SSv { let gy: i64=YT - (py*SSv+sv)*(YT-YB)/(H*SSv)
130 c=c + inside(gx,gy, ((ex0 as i64)+base*8) as *i64, ((ey0 as i64)+base*8) as *i64, ((ex1 as i64)+base*8) as *i64, ((ey1 as i64)+base*8) as *i64, nedg[s]); sv=sv+1 }
131 cov3[py*TC+sc]=c
132 sc=sc+1
133 }
134 py=py+1
135 }
136 // filter to R,G,B per output pixel: taps [1,2,3,2,1]/9
137 py=0
138 while py<H {
139 var lx: i64=0
140 while lx<CW {
141 var ch: i64=0
142 while ch<3 {
143 let cc: i64=lx*3 + ch
144 var acc: i64=0
145 var kk: i64=0-2
146 while kk<=2 {
147 var ic: i64=cc+kk
148 if ic<0 { ic=0 } if ic>TC-1 { ic=TC-1 }
149 var wgt: i64=3
150 if kk==0-1 { wgt=2 } if kk==1 { wgt=2 } if kk==0-2 { wgt=1 } if kk==2 { wgt=1 }
151 acc=acc + cov3[py*TC+ic]*wgt
152 kk=kk+1
153 }
154 let ink: i64=acc*255/(9*SSv) // 0..255 ink
155 var val: i64=255-ink; if val<0 { val=0 } if val>255 { val=255 }
156 rgb[(py*W + s*CW + lx)*3 + ch]=val as u8
157 ch=ch+1
158 }
159 lx=lx+1
160 }
161 py=py+1
162 }
163 s=s+1
164 }
165
166 // ---- write 24-bit BMP (bottom-up, BGR, W*3 already mult of 4 for W=200) ----
167 let rowSize: i64=W*3
168 let imgSize: i64=rowSize*H
169 let fileSize: i64=54+imgSize
170 let bmp: *u8=sys_mmap(fileSize+8)
171 bmp[0]=66 as u8; bmp[1]=77 as u8
172 var o: i64=2
173 o=wl32(bmp,o,fileSize); o=wl32(bmp,o,0); o=wl32(bmp,o,54)
174 o=wl32(bmp,o,40); o=wl32(bmp,o,W); o=wl32(bmp,o,H); o=wl16(bmp,o,1); o=wl16(bmp,o,24)
175 o=wl32(bmp,o,0); o=wl32(bmp,o,imgSize); o=wl32(bmp,o,K_MAGIC_2835); o=wl32(bmp,o,K_MAGIC_2835); o=wl32(bmp,o,0); o=wl32(bmp,o,0)
176 var fy: i64=0
177 while fy<H {
178 let py: i64=H-1-fy
179 var px: i64=0
180 while px<W {
181 let r: i64=rgb[(py*W+px)*3+0] as i64; let g: i64=rgb[(py*W+px)*3+1] as i64; let bl: i64=rgb[(py*W+px)*3+2] as i64
182 bmp[o]=bl as u8; bmp[o+1]=g as u8; bmp[o+2]=r as u8; o=o+3
183 px=px+1
184 }
185 fy=fy+1
186 }
187 let fd: i64=sys_openat_wr("web_assets/nishi_subpixel.bmp" as *u8, 420)
188 if fd<0 { sw("SAVE-FAIL\n" as *u8); return 6 }
189 sys_write(fd, bmp, fileSize); sys_close(fd)
190 sw("nx_ttf_subpixel: wrote web_assets/nishi_subpixel.bmp (" as *u8); sn(W); sw("x" as *u8); sn(H); sw(", 24-bit) -- ClearType-style RGB subpixel text\n" as *u8)
191
192 // inline proof: a mid row's R,G,B differ per pixel (3x horizontal resolution, not one gray value)
193 let midpy: i64=H/2
194 sw(" proof (row " as *u8); sn(midpy); sw(", pixels 6..11 R/G/B -- channels differ = real subpixel detail):\n" as *u8)
195 var px: i64=6
196 while px<12 { sw(" px" as *u8); sn(px); sw("=" as *u8); sn(rgb[(midpy*W+px)*3+0] as i64); sw("/" as *u8); sn(rgb[(midpy*W+px)*3+1] as i64); sw("/" as *u8); sn(rgb[(midpy*W+px)*3+2] as i64); px=px+1 }
197 sw("\n same coverage source as grayscale AA + e-ink dither -> LCD gets subpixel sharpness. Open the .bmp to SEE it.\n" as *u8)
198 return 0
199}