code wiki / _hdl_build / nx_ttf_fontlib.nx

nx_ttf_fontlib.nx source

↩ module page · 341 lines · 13954 B

1// nx_ttf_fontlib.nx -- the sovereign TrueType FONT ENGINE as a LIBRARY (the browser's text renderer). 2// Canonical home of the TTF read path first proven in the nx_ttf_raster demo (which has a main() and so 3// cannot be imported): sfnt dir -> head/cmap/loca/glyf/hhea/hmtx, cmap format-4, glyf outline decode 4// (flags/short/same/repeat), quadratic Bezier flattening, NONZERO-WINDING scan conversion with SSxSS 5// supersampled ANTI-ALIASED coverage -- plus what the demo lacked: a per-(char,size) COVERAGE CACHE 6// (each glyph rasterizes once per size), REAL per-glyph advances from hmtx (proportional metrics), a 7// mild contrast curve (stem darkening at small sizes), and an RGBA-framebuffer ALPHA-BLEND draw 8// (nx_framebuffer px = R,G,B,A) -- the piece the CSS-on-native arc named as "R4b the browser wire-in". 9// Fonts: our own emitted web_assets/nishi_sans.ttf (96 glyphs, FULL printable ASCII). No FreeType, no GDI. 10// license_tier: ORIGINAL 11import "nx_syscalls.nx" 12import "nx_paint_solid_rect.nx" 13const TF_MAGIC_32768: i64 = 32768 14const TF_MAGIC_65536: i64 = 65536 15const TF_MAGIC_16777216: i64 = 16777216 16const TF_MAGIC_2048: i64 = 2048 17 18func tf_rd16(b: *u8, o: i64) -> i64 { return (b[o] as i64)*256 + (b[o+1] as i64) } 19func tf_rd16s(b: *u8, o: i64) -> i64 { let v: i64=tf_rd16(b,o); if v>=TF_MAGIC_32768 { return v-TF_MAGIC_65536 } return v } 20func tf_rd32(b: *u8, o: i64) -> i64 { return (b[o] as i64)*TF_MAGIC_16777216 + (b[o+1] as i64)*TF_MAGIC_65536 + (b[o+2] as i64)*256 + (b[o+3] as i64) } 21func tf_bit(f: i64, m: i64) -> i64 { if (f/m)%2 == 1 { return 1 } return 0 } 22 23func tf_find_table(b: *u8, t0: i64, t1: i64, t2: i64, t3: i64) -> i64 { 24 let nt: i64=tf_rd16(b,4) 25 var i: i64=0 26 while i<nt { let d: i64=12+i*16 27 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 tf_rd32(b,d+8) } } } } 28 i=i+1 } 29 return 0 - 1 30} 31func tf_cmap_lookup(b: *u8, sub: i64, c: i64) -> i64 { 32 let segX2: i64=tf_rd16(b,sub+6) 33 let segCount: i64=segX2/2 34 var i: i64=0 35 while i<segCount { 36 let endc: i64=tf_rd16(b,sub+14+i*2) 37 if endc>=c { 38 let startc: i64=tf_rd16(b,sub+16+segX2+i*2) 39 if startc<=c { 40 let iro: i64=tf_rd16(b,sub+16+segX2*3+i*2) 41 if iro==0 { let delta: i64=tf_rd16s(b,sub+16+segX2*2+i*2); return (c+delta)%TF_MAGIC_65536 } 42 return 0 43 } 44 return 0 45 } 46 i=i+1 47 } 48 return 0 49} 50// decode glyph gid -> flattened line edges. returns edge count (max MAXE). 51func tf_glyph_edges(b: *u8, glyf: i64, loca: i64, locfmt: i64, gid: i64, ex0: *i64, ey0: *i64, ex1: *i64, ey1: *i64) -> i64 { 52 var g0: i64=0 53 var g1: i64=0 54 if locfmt==0 { g0=tf_rd16(b,loca+gid*2)*2; g1=tf_rd16(b,loca+gid*2+2)*2 } else { g0=tf_rd32(b,loca+gid*4); g1=tf_rd32(b,loca+gid*4+4) } 55 if g1<=g0 { return 0 } 56 let gp: i64=glyf+g0 57 let nc: i64=tf_rd16s(b,gp) 58 if nc<=0 { return 0 } 59 let ep: *i64=sys_mmap(512) as *i64 60 var i: i64=0 61 while i<nc { ep[i]=tf_rd16(b,gp+10+i*2); i=i+1 } 62 let np: i64=ep[nc-1]+1 63 let instr: i64=tf_rd16(b,gp+10+nc*2) 64 var p: i64=gp+10+nc*2+2+instr 65 let FL: *i64=sys_mmap(TF_MAGIC_2048) as *i64 66 let PX: *i64=sys_mmap(TF_MAGIC_2048) as *i64 67 let PY: *i64=sys_mmap(TF_MAGIC_2048) as *i64 68 let ON: *i64=sys_mmap(TF_MAGIC_2048) as *i64 69 var fi: i64=0 70 while fi<np { let f: i64=b[p] as i64; p=p+1; FL[fi]=f; fi=fi+1 71 if tf_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 } } } 72 var x: i64=0 73 i=0 74 while i<np { let f: i64=FL[i] 75 if tf_bit(f,2)==1 { var dx: i64=b[p] as i64; p=p+1; if tf_bit(f,16)==0 { dx=0-dx } x=x+dx } 76 else { if tf_bit(f,16)==0 { x=x+tf_rd16s(b,p); p=p+2 } } 77 PX[i]=x; i=i+1 } 78 var y: i64=0 79 i=0 80 while i<np { let f: i64=FL[i] 81 if tf_bit(f,4)==1 { var dy: i64=b[p] as i64; p=p+1; if tf_bit(f,32)==0 { dy=0-dy } y=y+dy } 82 else { if tf_bit(f,32)==0 { y=y+tf_rd16s(b,p); p=p+2 } } 83 PY[i]=y; ON[i]=tf_bit(f,1); i=i+1 } 84 var ne: i64=0 85 var cs: i64=0 86 var ci: i64=0 87 while ci<nc { 88 let ce: i64=ep[ci] 89 let cnt: i64=ce-cs+1 90 var curx: i64=PX[cs] 91 var cury: i64=PY[cs] 92 var j: i64=1 93 while j<=cnt { 94 let idx: i64=cs + (j%cnt) 95 if ON[idx]==1 { 96 if ne<500 { ex0[ne]=curx; ey0[ne]=cury; ex1[ne]=PX[idx]; ey1[ne]=PY[idx]; ne=ne+1 } 97 curx=PX[idx]; cury=PY[idx]; j=j+1 98 } else { 99 let eidx: i64=cs + ((j+1)%cnt) 100 let cxp: i64=PX[idx] 101 let cyp: i64=PY[idx] 102 let exp: i64=PX[eidx] 103 let eyp: i64=PY[eidx] 104 var t: i64=1 105 var pxp: i64=curx 106 var pyp: i64=cury 107 while t<=6 { 108 let u: i64=6-t 109 let qx: i64=(u*u*curx + 2*u*t*cxp + t*t*exp)/36 110 let qy: i64=(u*u*cury + 2*u*t*cyp + t*t*eyp)/36 111 if ne<500 { ex0[ne]=pxp; ey0[ne]=pyp; ex1[ne]=qx; ey1[ne]=qy; ne=ne+1 } 112 pxp=qx; pyp=qy; t=t+1 113 } 114 curx=exp; cury=eyp; j=j+2 115 } 116 } 117 cs=ce+1; ci=ci+1 118 } 119 return ne 120} 121func tf_inside(gx: i64, gy: i64, ex0: *i64, ey0: *i64, ex1: *i64, ey1: *i64, ne: i64) -> i64 { 122 var w: i64=0 123 var i: i64=0 124 while i<ne { 125 let y0: i64=ey0[i] 126 let y1: i64=ey1[i] 127 var dir: i64=0 128 if y0<=gy { if gy<y1 { dir=1 } } 129 if y1<=gy { if gy<y0 { dir=0-1 } } 130 if dir!=0 { 131 let xc: i64=ex0[i] + (gy-y0)*(ex1[i]-ex0[i])/(y1-y0) 132 if xc>gx { w=w+dir } 133 } 134 i=i+1 135 } 136 if w!=0 { return 1 } 137 return 0 138} 139 140// handle slots: [0]=buf [1]=glyf [2]=loca [3]=locfmt [4]=cmap_sub [5]=hmtx [6]=numHM [7]=cache [8]=nsizes [9..24]=sizes 141const TF_MAXSZ: i64 = 16 142const TF_ASC: i64 = 760 // em top of the pixel box (hhea ascent); box spans [ASC-1000 .. ASC] 143 144// parse a .ttf already in memory (b, len). returns handle (as *i64), or 0 on failure. This is the core; 145// tf_load reads the file then calls this. Surfaces whose keystone lacks sys_read_file (the native-Windows 146// GUI PE) read the bytes their own way (win32 CreateFileA/ReadFile) and call this directly. 147func tf_load_buf(b: *u8, len: i64) -> *i64 { 148 if (b as i64) == 0 { return 0 as *i64 } 149 if len <= 12 { return 0 as *i64 } 150 let glyf: i64 = tf_find_table(b, 103,108,121,102) 151 let loca: i64 = tf_find_table(b, 108,111,99,97) 152 let head: i64 = tf_find_table(b, 104,101,97,100) 153 let cmap: i64 = tf_find_table(b, 99,109,97,112) 154 let hhea: i64 = tf_find_table(b, 104,104,101,97) 155 let hmtx: i64 = tf_find_table(b, 104,109,116,120) 156 if glyf<0 { return 0 as *i64 } 157 if loca<0 { return 0 as *i64 } 158 if head<0 { return 0 as *i64 } 159 if cmap<0 { return 0 as *i64 } 160 if hhea<0 { return 0 as *i64 } 161 if hmtx<0 { return 0 as *i64 } 162 let fh: *i64 = sys_mmap(8*32) as *i64 163 fh[0] = b as i64 164 fh[1] = glyf 165 fh[2] = loca 166 fh[3] = tf_rd16s(b, head+50) 167 fh[4] = cmap + tf_rd32(b, cmap+8) 168 fh[5] = hmtx 169 fh[6] = tf_rd16(b, hhea+34) 170 fh[7] = (sys_mmap(TF_MAXSZ*96*4*8) as *i64) as i64 171 fh[8] = 0 172 return fh 173} 174 175// load + parse a .ttf from a file path (needs sys_read_file -- WSL/Linux/NishiOS). returns 0 on failure. 176func tf_load(path: *u8) -> *i64 { 177 let lp: *i64 = sys_mmap(16) as *i64 178 lp[0] = 0 - 1 179 let b: *u8 = sys_read_file(path, lp) 180 if lp[0] <= 12 { return 0 as *i64 } 181 let glyf: i64 = tf_find_table(b, 103,108,121,102) 182 let loca: i64 = tf_find_table(b, 108,111,99,97) 183 let head: i64 = tf_find_table(b, 104,101,97,100) 184 let cmap: i64 = tf_find_table(b, 99,109,97,112) 185 let hhea: i64 = tf_find_table(b, 104,104,101,97) 186 let hmtx: i64 = tf_find_table(b, 104,109,116,120) 187 if glyf<0 { return 0 as *i64 } 188 if loca<0 { return 0 as *i64 } 189 if head<0 { return 0 as *i64 } 190 if cmap<0 { return 0 as *i64 } 191 if hhea<0 { return 0 as *i64 } 192 if hmtx<0 { return 0 as *i64 } 193 let fh: *i64 = sys_mmap(8*32) as *i64 194 fh[0] = b as i64 195 fh[1] = glyf 196 fh[2] = loca 197 fh[3] = tf_rd16s(b, head+50) 198 fh[4] = cmap + tf_rd32(b, cmap+8) 199 fh[5] = hmtx 200 fh[6] = tf_rd16(b, hhea+34) 201 fh[7] = (sys_mmap(TF_MAXSZ*96*4*8) as *i64) as i64 202 fh[8] = 0 203 return fh 204} 205 206// get (rasterizing + caching on miss) the coverage record for char ch at pixel height ph. 207// rec: [0]=cov ptr (bytes, cw x chh) [1]=cw [2]=chh [3]=advance px. returns 0 as *i64 if no glyph. 208func tf_glyph(fh: *i64, ch: i64, ph: i64) -> *i64 { 209 if ph <= 2 { return 0 as *i64 } 210 if ch < 32 { return 0 as *i64 } 211 if ch > 126 { return 0 as *i64 } 212 // size slot 213 var slot: i64 = 0 - 1 214 var i: i64 = 0 215 while i < fh[8] { if fh[9+i]==ph { slot=i; i=fh[8] } else { i=i+1 } } 216 if slot < 0 { 217 if fh[8] < TF_MAXSZ { slot=fh[8]; fh[9+slot]=ph; fh[8]=fh[8]+1 } 218 else { slot=TF_MAXSZ-1; fh[9+slot]=ph } 219 } 220 let cache: *i64 = fh[7] as *i64 221 let rec: *i64 = ((cache as i64) + ((slot*96 + (ch-32))*4)*8) as *i64 222 if rec[0] != 0 { return rec } 223 // rasterize 224 let b: *u8 = fh[0] as *u8 225 var gid: i64 = tf_cmap_lookup(b, fh[4], ch) 226 let numHM: i64 = fh[6] 227 var hmi: i64 = gid 228 if hmi >= numHM { hmi = numHM-1 } 229 let aw_em: i64 = tf_rd16(b, fh[5] + hmi*4) 230 var advpx: i64 = (aw_em*ph)/1000 231 if advpx < 2 { advpx = 2 } 232 let ex0: *i64 = sys_mmap(8*512) as *i64 233 let ey0: *i64 = sys_mmap(8*512) as *i64 234 let ex1: *i64 = sys_mmap(8*512) as *i64 235 let ey1: *i64 = sys_mmap(8*512) as *i64 236 let ne: i64 = tf_glyph_edges(b, fh[1], fh[2], fh[3], gid, ex0, ey0, ex1, ey1) 237 let cw: i64 = advpx + 2 238 let chh: i64 = ph + 1 239 let cov: *u8 = sys_mmap(cw*chh + 16) 240 let SS: i64 = 4 241 if ne > 0 { 242 var py: i64 = 0 243 while py < chh { 244 var px: i64 = 0 245 while px < cw { 246 var cnt: i64 = 0 247 var sy: i64 = 0 248 while sy < SS { 249 var sx: i64 = 0 250 while sx < SS { 251 let gx: i64 = ((px*SS+sx)*1000)/(ph*SS) 252 let gy: i64 = TF_ASC - ((py*SS+sy)*1000)/(ph*SS) 253 cnt = cnt + tf_inside(gx, gy, ex0, ey0, ex1, ey1, ne) 254 sx = sx + 1 255 } 256 sy = sy + 1 257 } 258 var a: i64 = (cnt*255)/(SS*SS) 259 if a > 0 { a = a + (a*(255-a))/170; if a > 255 { a = 255 } } // contrast boost (stem darkening) 260 cov[py*cw+px] = a as u8 261 px = px + 1 262 } 263 py = py + 1 264 } 265 } 266 rec[0] = cov as i64 267 rec[1] = cw 268 rec[2] = chh 269 rec[3] = advpx 270 return rec 271} 272 273// draw a text run. fh = font handle; (x,y) = TOP-LEFT of the line's glyph box; ph = pixel height of the em 274// box; cellw>0 = fixed advance per char (the browser layout's cell); cellw==0 = proportional advances. 275// returns total width advanced. 276func tf_draw_text(fh: *i64, fb: *Framebuffer, x: i64, y: i64, s: *u8, n: i64, ph: i64, cr: i64, cg: i64, cb: i64, cellw: i64) -> i64 { 277 let fw: i64 = fb.width 278 let fhh: i64 = fb.height 279 let pxb: *u8 = fb.pixels 280 var pen: i64 = x 281 var i: i64 = 0 282 while i < n { 283 let ch: i64 = s[i] & 0xff 284 var adv: i64 = cellw 285 if ch >= 33 { if ch <= 126 { 286 let rec: *i64 = tf_glyph(fh, ch, ph) 287 if rec != (0 as *i64) { 288 let cov: *u8 = rec[0] as *u8 289 let cw: i64 = rec[1] 290 let chh: i64 = rec[2] 291 let natural: i64 = rec[3] 292 if cellw == 0 { adv = natural } 293 // draw the FULL coverage width cw (the glyph ink can extend past its own advance -- e.g. an 294 // 'i'/'l' stem near the right edge). Squeeze only when the ink is wider than the cell. 295 var dw: i64 = cw 296 var squeeze: i64 = 0 297 if cellw > 0 { if cw > cellw { dw = cellw; squeeze = 1 } } 298 var xoff: i64 = 0 299 if cellw > 0 { if cw < cellw { xoff = (cellw-cw)/2 } } 300 var gy: i64 = 0 301 while gy < chh { 302 let fy: i64 = y + gy 303 if fy >= 0 { if fy < fhh { 304 var gx: i64 = 0 305 while gx < dw { 306 var sxc: i64 = gx 307 if squeeze == 1 { sxc = (gx*cw)/dw } 308 if sxc >= cw { sxc = cw-1 } 309 let a: i64 = cov[gy*cw+sxc] as i64 310 if a > 0 { 311 let fx: i64 = pen + xoff + gx 312 if fx >= 0 { if fx < fw { 313 let o: i64 = (fy*fw+fx)*4 314 let ia: i64 = 255-a 315 pxb[o] = (((pxb[o] as i64)*ia + cr*a)/255) as u8 316 pxb[o+1] = (((pxb[o+1] as i64)*ia + cg*a)/255) as u8 317 pxb[o+2] = (((pxb[o+2] as i64)*ia + cb*a)/255) as u8 318 pxb[o+3] = 255 as u8 319 } } 320 } 321 gx = gx + 1 322 } 323 } } 324 gy = gy + 1 325 } 326 } 327 } } 328 if cellw == 0 { if ch == 32 { adv = (380*ph)/1000 } } 329 pen = pen + adv 330 i = i + 1 331 } 332 return pen - x 333} 334 335// proportional advance of one char at ph (for measurement) 336func tf_char_adv(fh: *i64, ch: i64, ph: i64) -> i64 { 337 if ch == 32 { return (380*ph)/1000 } 338 let rec: *i64 = tf_glyph(fh, ch, ph) 339 if rec == (0 as *i64) { return (600*ph)/1000 } 340 return rec[3] 341}