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}