code wiki / (root) / nx_ttf_subpixel.nx

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}