code wiki / (root) / nx_ttf_raster.nx

nx_ttf_raster.nx source

↩ module page · 257 lines · 10569 B

1// nx_ttf_raster.nx -- SOVEREIGN TrueType RASTERIZER (the font engine for the Nishi browser + OS). Parses a real 2// .ttf we emitted (sfnt dir -> head/cmap/loca/glyf), maps chars via cmap format-4, decodes glyf outlines (contours, 3// on/off-curve flags, coord deltas incl. short/same/repeat), FLATTENS quadratic Beziers, and SCAN-CONVERTS the 4// filled outline (nonzero winding, per-pixel ray test) to a pixel grid -- printed as ASCII so we can SEE our own 5// font render through our OWN engine (round-trip: nx_font_ttf_emit -> .ttf -> nx_ttf_raster -> pixels). No FreeType, 6// no external anything. Renders "NISHI". license_tier: ORIGINAL expect_exit: 0 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_4096: i64 = 4096 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 rd16(b: *u8, o: i64) -> i64 { return (b[o] as i64)*256 + (b[o+1] as i64) } 16func rd16s(b: *u8, o: i64) -> i64 { let v: i64=rd16(b,o); if v>=K_MAGIC_32768 { return v-K_MAGIC_65536 } return v } 17func 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) } 18func bit(f: i64, m: i64) -> i64 { if (f/m)%2 == 1 { return 1 } return 0 } 19 20func find_table(b: *u8, t0: i64, t1: i64, t2: i64, t3: i64) -> i64 { 21 let nt: i64=rd16(b,4) 22 var i: i64=0 23 while i<nt { let d: i64=12+i*16 24 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) } } } } 25 i=i+1 } 26 return 0-1 27} 28// cmap format-4 lookup (idRangeOffset==0 path). sub = subtable offset. 29func cmap_lookup(b: *u8, sub: i64, c: i64) -> i64 { 30 let segX2: i64=rd16(b,sub+6) 31 let segCount: i64=segX2/2 32 var i: i64=0 33 while i<segCount { 34 let endc: i64=rd16(b,sub+14+i*2) 35 if endc>=c { 36 let startc: i64=rd16(b,sub+16+segX2+i*2) 37 if startc<=c { 38 let iro: i64=rd16(b,sub+16+segX2*3+i*2) 39 if iro==0 { let delta: i64=rd16s(b,sub+16+segX2*2+i*2); return (c+delta)%K_MAGIC_65536 } 40 return 0 41 } 42 return 0 43 } 44 i=i+1 45 } 46 return 0 47} 48 49// decode glyph gid into flattened edges (ex0..ey1). returns edge count. 50func glyph_edges(b: *u8, glyf: i64, loca: i64, locfmt: i64, gid: i64, ex0: *i64, ey0: *i64, ex1: *i64, ey1: *i64, 51 ep: *i64, PX: *i64, PY: *i64, ON: *i64) -> i64 { 52 var g0: i64=0; var g1: i64=0 53 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) } 54 if g1<=g0 { return 0 } 55 let gp: i64=glyf+g0 56 let nc: i64=rd16s(b,gp) 57 if nc<=0 { return 0 } 58 var i: i64=0 59 while i<nc { ep[i]=rd16(b,gp+10+i*2); i=i+1 } 60 let np: i64=ep[nc-1]+1 61 let instr: i64=rd16(b,gp+10+nc*2) 62 var p: i64=gp+10+nc*2+2+instr 63 // flags (with repeat) 64 let FL: *i64=sys_mmap(K_MAGIC_2048) as *i64 65 var fi: i64=0 66 while fi<np { let f: i64=b[p] as i64; p=p+1; FL[fi]=f; fi=fi+1 67 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 } } } 68 // x coords 69 var x: i64=0; i=0 70 while i<np { let f: i64=FL[i] 71 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 } 72 else { if bit(f,16)==0 { x=x+rd16s(b,p); p=p+2 } } 73 PX[i]=x; i=i+1 } 74 // y coords 75 var y: i64=0; i=0 76 while i<np { let f: i64=FL[i] 77 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 } 78 else { if bit(f,32)==0 { y=y+rd16s(b,p); p=p+2 } } 79 PY[i]=y; ON[i]=bit(f,1); i=i+1 } 80 // flatten contours -> edges 81 var ne: i64=0 82 var cs: i64=0 83 var ci: i64=0 84 while ci<nc { 85 let ce: i64=ep[ci] 86 let cnt: i64=ce-cs+1 87 // assume starts on-curve (our fonts do). walk circularly. 88 var curx: i64=PX[cs]; var cury: i64=PY[cs] 89 var j: i64=1 90 while j<=cnt { 91 let idx: i64=cs + (j%cnt) 92 if ON[idx]==1 { 93 ex0[ne]=curx; ey0[ne]=cury; ex1[ne]=PX[idx]; ey1[ne]=PY[idx]; ne=ne+1 94 curx=PX[idx]; cury=PY[idx]; j=j+1 95 } else { 96 let eidx: i64=cs + ((j+1)%cnt) 97 let cxp: i64=PX[idx]; let cyp: i64=PY[idx]; let exp: i64=PX[eidx]; let eyp: i64=PY[eidx] 98 var t: i64=1 99 var pxp: i64=curx; var pyp: i64=cury 100 while t<=6 { 101 let u: i64=6-t 102 let qx: i64=(u*u*curx + 2*u*t*cxp + t*t*exp)/36 103 let qy: i64=(u*u*cury + 2*u*t*cyp + t*t*eyp)/36 104 ex0[ne]=pxp; ey0[ne]=pyp; ex1[ne]=qx; ey1[ne]=qy; ne=ne+1 105 pxp=qx; pyp=qy; t=t+1 106 } 107 curx=exp; cury=eyp; j=j+2 108 } 109 } 110 cs=ce+1; ci=ci+1 111 } 112 return ne 113} 114// point-in-outline via nonzero winding (ray to +x). 115func inside(gx: i64, gy: i64, ex0: *i64, ey0: *i64, ex1: *i64, ey1: *i64, ne: i64) -> i64 { 116 var w: i64=0 117 var i: i64=0 118 while i<ne { 119 let y0: i64=ey0[i]; let y1: i64=ey1[i] 120 var dir: i64=0 121 if y0<=gy { if gy<y1 { dir=1 } } 122 if y1<=gy { if gy<y0 { dir=0-1 } } 123 if dir!=0 { 124 let xc: i64=ex0[i] + (gy-y0)*(ex1[i]-ex0[i])/(y1-y0) 125 if xc>gx { w=w+dir } 126 } 127 i=i+1 128 } 129 if w!=0 { return 1 } 130 return 0 131} 132 133// Bayer 4x4 ordered-dither matrix (0..15) -- turns grayscale coverage into 1-bit that reads as gray on e-ink 134func bayer(x: i64, y: i64) -> i64 { 135 let idx: i64 = (y%4)*4 + (x%4) 136 if idx==0 { return 0 } if idx==1 { return 8 } if idx==2 { return 2 } if idx==3 { return 10 } 137 if idx==4 { return 12 } if idx==5 { return 4 } if idx==6 { return 14 } if idx==7 { return 6 } 138 if idx==8 { return 3 } if idx==9 { return 11 } if idx==10 { return 1 } if idx==11 { return 9 } 139 if idx==12 { return 15 } if idx==13 { return 7 } if idx==14 { return 13 } 140 return 5 141} 142 143func main() -> i64 { 144 let len_p: *i64 = sys_mmap(8) as *i64 145 let b: *u8 = sys_read_file_x86_64("web_assets/nishi_sans.ttf" as *u8, len_p) 146 if b == (0 as *u8) { sw("cannot read nishi_sans.ttf\n" as *u8); return 5 } 147 let glyf: i64=find_table(b, 103,108,121,102) // glyf 148 let loca: i64=find_table(b, 108,111,99,97) // loca 149 let head: i64=find_table(b, 104,101,97,100) // head 150 let cmap: i64=find_table(b, 99,109,97,112) // cmap 151 if glyf<0 { sw("no glyf\n" as *u8); return 6 } 152 let locfmt: i64=rd16s(b,head+50) 153 // cmap: pick first subtable 154 let suboff: i64=rd32(b,cmap+8) 155 let sub: i64=cmap+suboff 156 157 sw("=== nx_ttf_raster -- OUR font, rendered by OUR engine (nishi_latin.ttf -> pixels) ===\n" as *u8) 158 159 let word: *i64=sys_mmap(64) as *i64 160 word[0]=78; word[1]=105; word[2]=115; word[3]=104; word[4]=105 // N i s h i (grayscale AA) 161 let NW: i64=5 162 let MAXE: i64=400 163 let ex0: *i64=sys_mmap(NW*MAXE*8) as *i64 164 let ey0: *i64=sys_mmap(NW*MAXE*8) as *i64 165 let ex1: *i64=sys_mmap(NW*MAXE*8) as *i64 166 let ey1: *i64=sys_mmap(NW*MAXE*8) as *i64 167 let nedg: *i64=sys_mmap(64) as *i64 168 let ep: *i64=sys_mmap(128) as *i64 169 let PX: *i64=sys_mmap(K_MAGIC_2048) as *i64 170 let PY: *i64=sys_mmap(K_MAGIC_2048) as *i64 171 let ON: *i64=sys_mmap(K_MAGIC_2048) as *i64 172 173 var s: i64=0 174 while s<NW { 175 let gid: i64=cmap_lookup(b, sub, word[s]) 176 let base: i64=s*MAXE 177 let ne: i64=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, ep, PX, PY, ON) 178 nedg[s]=ne 179 s=s+1 180 } 181 182 let GH: i64=30 183 let GW: i64=22 184 let SS: i64=3 185 let ramp: *u8=" .:-=+*#%@" as *u8 186 let line: *u8=sys_mmap(K_MAGIC_4096) 187 var row: i64=0 188 while row<GH { 189 var col: i64=0 190 var lp: i64=0 191 s=0 192 while s<NW { 193 let base: i64=s*MAXE 194 col=0 195 while col<GW { 196 // GRAYSCALE ANTI-ALIASING: SSxSS supersample -> coverage -> gray ramp (box filter) 197 var cnt: i64=0 198 var sy: i64=0 199 while sy<SS { 200 var sx: i64=0 201 while sx<SS { 202 let gx: i64 = col*820/GW + sx*820/(GW*SS) 203 let gy: i64 = 740 - (row*800/GH + sy*800/(GH*SS)) 204 cnt = cnt + 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]) 205 sx=sx+1 206 } 207 sy=sy+1 208 } 209 let idx: i64 = cnt*10/(SS*SS+1) 210 line[lp]=ramp[idx] 211 lp=lp+1 212 col=col+1 213 } 214 line[lp]=32 as u8; lp=lp+1 215 s=s+1 216 } 217 line[lp]=10 as u8; lp=lp+1 218 sys_write(1, line, lp) 219 row=row+1 220 } 221 sw("=== same glyphs -> 1-bit E-INK via Bayer 4x4 ordered dither (reads as smooth gray on e-paper) ===\n" as *u8) 222 row=0 223 while row<GH { 224 var lp2: i64=0 225 s=0 226 while s<NW { 227 let base: i64=s*MAXE 228 var col: i64=0 229 while col<GW { 230 var cnt: i64=0 231 var sy: i64=0 232 while sy<SS { 233 var sx: i64=0 234 while sx<SS { 235 let gx: i64 = col*820/GW + sx*820/(GW*SS) 236 let gy: i64 = 740 - (row*800/GH + sy*800/(GH*SS)) 237 cnt = cnt + 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]) 238 sx=sx+1 239 } 240 sy=sy+1 241 } 242 let cov16: i64 = cnt*16/(SS*SS) 243 let gxpix: i64 = s*(GW+1) + col 244 if cov16 > bayer(gxpix, row) { line[lp2]=35 as u8 } else { line[lp2]=32 as u8 } 245 lp2=lp2+1 246 col=col+1 247 } 248 line[lp2]=32 as u8; lp2=lp2+1 249 s=s+1 250 } 251 line[lp2]=10 as u8; lp2=lp2+1 252 sys_write(1, line, lp2) 253 row=row+1 254 } 255 sw("=== sovereign engine: outlines -> grayscale AA (LCD) AND 1-bit dither (e-ink) from ONE coverage source ===\n" as *u8) 256 return 0 257}