code wiki / (root) / nx_ttf_sdf.nx

nx_ttf_sdf.nx source

↩ module page · 202 lines · 10034 B

1// nx_ttf_sdf.nx -- L2 rung: SIGNED DISTANCE FIELD generator (resolution-independent glyph rendering, the non-neural 2// "beats a bitmap when scaling" technique). Parses our .ttf, decodes a glyph outline, computes a TINY signed 3// distance field (16x16 = 256 texels: each stores signed distance to nearest outline edge, - inside / + outside), 4// then RECONSTRUCTS the glyph CRISP at much larger output by bilinear-sampling that tiny field + soft threshold. 5// The point: 256 bytes -> a sharp glyph at ANY scale (a 16x16 BITMAP would be blocky; the SDF stays crisp). This 6// is what a GPU shader does with one small texture. Renders 'S'. 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_1000000000: i64 = 1000000000 13const K_MAGIC_8192: i64 = 8192 14 15func sw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 16func 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 } 17func rd16(b: *u8, o: i64) -> i64 { return (b[o] as i64)*256 + (b[o+1] as i64) } 18func rd16s(b: *u8, o: i64) -> i64 { let v: i64=rd16(b,o); if v>=K_MAGIC_32768 { return v-K_MAGIC_65536 } return v } 19func 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) } 20func bit(f: i64, m: i64) -> i64 { if (f/m)%2 == 1 { return 1 } return 0 } 21func isqrt(n: i64) -> i64 { if n<=0 { return 0 } var x: i64=n; var y: i64=(x+1)/2; while y<x { x=y; y=(x + n/x)/2 } return x } 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 { 32 let endc: i64=rd16(b,sub+14+i*2) 33 if endc>=c { 34 let startc: i64=rd16(b,sub+16+segX2+i*2) 35 if startc<=c { let iro: i64=rd16(b,sub+16+segX2*3+i*2) 36 if iro==0 { let delta: i64=rd16s(b,sub+16+segX2*2+i*2); return (c+delta)%K_MAGIC_65536 } 37 return 0 } 38 return 0 } 39 i=i+1 } 40 return 0 41} 42func glyph_edges(b: *u8, glyf: i64, loca: i64, locfmt: i64, gid: i64, ex0: *i64, ey0: *i64, ex1: *i64, ey1: *i64) -> i64 { 43 var g0: i64=0; var g1: i64=0 44 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) } 45 if g1<=g0 { return 0 } 46 let gp: i64=glyf+g0; let nc: i64=rd16s(b,gp) 47 if nc<=0 { return 0 } 48 let ep: *i64=sys_mmap(256) as *i64 49 var i: i64=0 50 while i<nc { ep[i]=rd16(b,gp+10+i*2); i=i+1 } 51 let np: i64=ep[nc-1]+1 52 let instr: i64=rd16(b,gp+10+nc*2) 53 var p: i64=gp+10+nc*2+2+instr 54 let FL: *i64=sys_mmap(K_MAGIC_2048) as *i64 55 let PX: *i64=sys_mmap(K_MAGIC_2048) as *i64 56 let PY: *i64=sys_mmap(K_MAGIC_2048) as *i64 57 let ON: *i64=sys_mmap(K_MAGIC_2048) as *i64 58 var fi: i64=0 59 while fi<np { let f: i64=b[p] as i64; p=p+1; FL[fi]=f; fi=fi+1 60 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 } } } 61 var x: i64=0; i=0 62 while i<np { let f: i64=FL[i] 63 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 } 64 else { if bit(f,16)==0 { x=x+rd16s(b,p); p=p+2 } } 65 PX[i]=x; i=i+1 } 66 var y: i64=0; i=0 67 while i<np { let f: i64=FL[i] 68 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 } 69 else { if bit(f,32)==0 { y=y+rd16s(b,p); p=p+2 } } 70 PY[i]=y; ON[i]=bit(f,1); i=i+1 } 71 var ne: i64=0; var cs: i64=0; var ci: i64=0 72 while ci<nc { 73 let ce: i64=ep[ci]; let cnt: i64=ce-cs+1 74 var curx: i64=PX[cs]; var cury: i64=PY[cs]; var j: i64=1 75 while j<=cnt { 76 let idx: i64=cs + (j%cnt) 77 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 } 78 else { 79 let eidx: i64=cs + ((j+1)%cnt) 80 let cxp: i64=PX[idx]; let cyp: i64=PY[idx]; let exp: i64=PX[eidx]; let eyp: i64=PY[eidx] 81 var t: i64=1; var pxp: i64=curx; var pyp: i64=cury 82 while t<=6 { let u: i64=6-t 83 let qx: i64=(u*u*curx + 2*u*t*cxp + t*t*exp)/36 84 let qy: i64=(u*u*cury + 2*u*t*cyp + t*t*eyp)/36 85 ex0[ne]=pxp; ey0[ne]=pyp; ex1[ne]=qx; ey1[ne]=qy; ne=ne+1; pxp=qx; pyp=qy; t=t+1 } 86 curx=exp; cury=eyp; j=j+2 } 87 } 88 cs=ce+1; ci=ci+1 89 } 90 return ne 91} 92func inside(gx: i64, gy: i64, ex0: *i64, ey0: *i64, ex1: *i64, ey1: *i64, ne: i64) -> i64 { 93 var w: i64=0; var i: i64=0 94 while i<ne { let y0: i64=ey0[i]; let y1: i64=ey1[i]; var dir: i64=0 95 if y0<=gy { if gy<y1 { dir=1 } } 96 if y1<=gy { if gy<y0 { dir=0-1 } } 97 if dir!=0 { let xc: i64=ex0[i] + (gy-y0)*(ex1[i]-ex0[i])/(y1-y0); if xc>gx { w=w+dir } } 98 i=i+1 } 99 if w!=0 { return 1 } 100 return 0 101} 102func seg_dist2(px: i64, py: i64, x0: i64, y0: i64, x1: i64, y1: i64) -> i64 { 103 let dx: i64=x1-x0; let dy: i64=y1-y0; let d2: i64=dx*dx+dy*dy 104 if d2==0 { let ax: i64=px-x0; let ay: i64=py-y0; return ax*ax+ay*ay } 105 var t: i64=(px-x0)*dx + (py-y0)*dy 106 if t<0 { t=0 } if t>d2 { t=d2 } 107 let projx: i64=x0 + t*dx/d2; let projy: i64=y0 + t*dy/d2 108 let ax: i64=px-projx; let ay: i64=py-projy 109 return ax*ax+ay*ay 110} 111// signed distance at (px,py): +outside / -inside 112func sdist(px: i64, py: i64, ex0: *i64, ey0: *i64, ex1: *i64, ey1: *i64, ne: i64) -> i64 { 113 var mind2: i64=K_MAGIC_1000000000; var i: i64=0 114 while i<ne { let d2: i64=seg_dist2(px,py, ex0[i],ey0[i],ex1[i],ey1[i]); if d2<mind2 { mind2=d2 } i=i+1 } 115 let d: i64=isqrt(mind2) 116 if inside(px,py,ex0,ey0,ex1,ey1,ne)==1 { return 0-d } 117 return d 118} 119 120func main() -> i64 { 121 let len_p: *i64 = sys_mmap(8) as *i64 122 let b: *u8 = sys_read_file_x86_64("web_assets/nishi_sans.ttf" as *u8, len_p) 123 if b == (0 as *u8) { sw("cannot read nishi_sans.ttf\n" as *u8); return 5 } 124 let glyf: i64=find_table(b, 103,108,121,102) 125 let loca: i64=find_table(b, 108,111,99,97) 126 let head: i64=find_table(b, 104,101,97,100) 127 let cmap: i64=find_table(b, 99,109,97,112) 128 let locfmt: i64=rd16s(b,head+50) 129 let sub: i64=cmap+rd32(b,cmap+8) 130 131 let ex0: *i64=sys_mmap(K_MAGIC_8192) as *i64 132 let ey0: *i64=sys_mmap(K_MAGIC_8192) as *i64 133 let ex1: *i64=sys_mmap(K_MAGIC_8192) as *i64 134 let ey1: *i64=sys_mmap(K_MAGIC_8192) as *i64 135 let gid: i64=cmap_lookup(b, sub, 83) // 'S' 136 let ne: i64=glyph_edges(b, glyf, loca, locfmt, gid, ex0, ey0, ex1, ey1) 137 138 // glyph bounds from edges 139 var bx0: i64=ex0[0]; var bx1: i64=ex0[0]; var by0: i64=ey0[0]; var by1: i64=ey0[0] 140 var i: i64=0 141 while i<ne { 142 if ex0[i]<bx0 { bx0=ex0[i] } if ex1[i]<bx0 { bx0=ex1[i] } 143 if ex0[i]>bx1 { bx1=ex0[i] } if ex1[i]>bx1 { bx1=ex1[i] } 144 if ey0[i]<by0 { by0=ey0[i] } if ey1[i]<by0 { by0=ey1[i] } 145 if ey0[i]>by1 { by1=ey0[i] } if ey1[i]>by1 { by1=ey1[i] } 146 i=i+1 147 } 148 let pad: i64=90 149 bx0=bx0-pad; bx1=bx1+pad; by0=by0-pad; by1=by1+pad 150 let bw: i64=bx1-bx0; let bh: i64=by1-by0 151 152 // BUILD the tiny SDF (GS x GS) 153 let GS: i64=16 154 let sd: *i64=sys_mmap(GS*GS*8) as *i64 155 var gj: i64=0 156 while gj<GS { 157 var gi: i64=0 158 while gi<GS { 159 let px: i64=bx0 + (gi*2+1)*bw/(GS*2) 160 let py: i64=by0 + (gj*2+1)*bh/(GS*2) 161 sd[gj*GS+gi]=sdist(px,py, ex0,ey0,ex1,ey1, ne) 162 gi=gi+1 163 } 164 gj=gj+1 165 } 166 sw("=== nx_ttf_sdf -- TINY " as *u8); sn(GS); sw("x" as *u8); sn(GS); sw(" signed distance field (" as *u8); sn(GS*GS); sw(" texels) reconstructed CRISP at large size ===\n" as *u8) 167 168 // RECONSTRUCT at high res by bilinear-sampling the tiny SDF + soft threshold 169 let OW: i64=52; let OH: i64=30 170 let ramp: *u8=" .:-=+*#%@" as *u8 171 let ew: i64=bw/OW + 1 // ~1 output-pixel edge softness 172 let line: *u8=sys_mmap(256) 173 var oy: i64=0 174 while oy<OH { 175 var ox: i64=0; var lp: i64=0 176 while ox<OW { 177 let gx: i64=bx0 + (ox*2+1)*bw/(OW*2) 178 let gy: i64=by1 - (oy*2+1)*bh/(OH*2) 179 // map to grid fixed-point (*256), minus half texel 180 var uf: i64=(gx-bx0)*GS*256/bw - 128 181 var vf: i64=(gy-by0)*GS*256/bh - 128 182 if uf<0 { uf=0 } if vf<0 { vf=0 } 183 var i0: i64=uf/256; var j0: i64=vf/256 184 if i0>GS-2 { i0=GS-2 } if j0>GS-2 { j0=GS-2 } 185 let fx: i64=uf-i0*256; let fy: i64=vf-j0*256 186 let s00: i64=sd[j0*GS+i0]; let s10: i64=sd[j0*GS+i0+1]; let s01: i64=sd[(j0+1)*GS+i0]; let s11: i64=sd[(j0+1)*GS+i0+1] 187 let a: i64=s00*(256-fx)/256 + s10*fx/256 188 let c: i64=s01*(256-fx)/256 + s11*fx/256 189 let sdv: i64=a*(256-fy)/256 + c*fy/256 190 // coverage from signed distance: inside(-) -> solid 191 var lvl: i64=(ew - sdv)*10/(2*ew) 192 if lvl<0 { lvl=0 } if lvl>9 { lvl=9 } 193 line[lp]=ramp[lvl]; lp=lp+1 194 ox=ox+1 195 } 196 line[lp]=10 as u8; lp=lp+1 197 sys_write(1, line, lp) 198 oy=oy+1 199 } 200 sw("=== " as *u8); sn(GS*GS); sw(" SDF bytes -> " as *u8); sn(OW*OH); sw("-pixel crisp glyph. A " as *u8); sn(GS); sw("x" as *u8); sn(GS); sw(" BITMAP would be blocky; the SDF stays sharp at any scale (GPU/HiDPI rung). ===\n" as *u8) 201 return 0 202}