nx_nishi_font_core.nx source
↩ module page · 214 lines · 11637 B
1// nx_nishi_font_core.nx -- the PURE, IMPORT-FREE core of the sovereign Nishi stroke font. Contains the
2// glyph stroke designs + the supersampled-mask rasterizer + a BASE-RELATIVE i64-framebuffer blit that takes
3// a CALLER-PROVIDED scratch mask (no sys_mmap) so it runs unchanged in our browser WASM (linear mem, base=0).
4// NO imports -> compiles to clean WAT with no un-lowerable syscalls. The u8/PNG path (nf_render_glyph) +
5// demo + gate live in nx_nishi_font.nx, which imports this. Monoline sans-serif, 4x supersample box AA, all
6// integer (no float). Grid: x,y in EM units, EM height 28; cap-top y=4, baseline y=22. Glyph set: N I S H B
7// R O W E + 0-9 + space. Every glyph DESIGNED here (no glyph copied from any font -> no license). license_tier: ORIGINAL
8
9const NF_SS: i64 = 4 // supersample factor
10const NF_EM: i64 = 28 // EM height in design units
11const NF_ADV_U: i64 = 20 // advance width in design units
12
13// unit coordinate -> supersampled-pixel coordinate at cell height CH(px)
14func nf_u(u: i64, ch: i64) -> i64 { return u * ch * NF_SS / NF_EM }
15
16// stamp a filled disc of radius r (ss-pixels) into the 1-byte mask, clamped.
17func nf_stamp(mask: *u8, mw: i64, mh: i64, cx: i64, cy: i64, r: i64) -> i64 {
18 let r2: i64 = r*r
19 var yy: i64 = cy - r
20 while yy <= cy + r {
21 if yy >= 0 { if yy < mh {
22 var xx: i64 = cx - r
23 while xx <= cx + r {
24 if xx >= 0 { if xx < mw {
25 let ddx: i64 = xx - cx
26 let ddy: i64 = yy - cy
27 if ddx*ddx + ddy*ddy <= r2 { mask[yy*mw + xx] = 1 as u8 }
28 } }
29 xx = xx + 1
30 }
31 } }
32 yy = yy + 1
33 }
34 return 0
35}
36// thick line (ss-pixel coords) via DDA + disc stamps.
37func nf_seg(mask: *u8, mw: i64, mh: i64, x0: i64, y0: i64, x1: i64, y1: i64, r: i64) -> i64 {
38 let dx: i64 = x1 - x0
39 let dy: i64 = y1 - y0
40 var adx: i64 = dx; if adx < 0 { adx = 0 - adx }
41 var ady: i64 = dy; if ady < 0 { ady = 0 - ady }
42 var steps: i64 = adx; if ady > steps { steps = ady }
43 if steps == 0 { nf_stamp(mask, mw, mh, x0, y0, r); return 0 }
44 var i: i64 = 0
45 while i <= steps {
46 let x: i64 = x0 + dx*i/steps
47 let y: i64 = y0 + dy*i/steps
48 nf_stamp(mask, mw, mh, x, y, r)
49 i = i + 1
50 }
51 return 0
52}
53// draw unit-segment (ux0,uy0)-(ux1,uy1) into the mask at cell height ch with stroke radius r.
54func nf_us(mask: *u8, mw: i64, mh: i64, ux0: i64, uy0: i64, ux1: i64, uy1: i64, ch: i64, r: i64) -> i64 {
55 nf_seg(mask, mw, mh, nf_u(ux0,ch), nf_u(uy0,ch), nf_u(ux1,ch), nf_u(uy1,ch), r)
56 return 0
57}
58
59// draw glyph `gid` (ASCII) strokes into the mask. Designed monoline letterforms (cap-top 4, baseline 22).
60func nf_glyph(mask: *u8, mw: i64, mh: i64, gid: i64, ch: i64, r: i64) -> i64 {
61 if gid == 78 { // N
62 nf_us(mask,mw,mh, 3,22, 3,4, ch,r); nf_us(mask,mw,mh, 3,4, 15,22, ch,r); nf_us(mask,mw,mh, 15,22, 15,4, ch,r)
63 }
64 if gid == 73 { // I
65 nf_us(mask,mw,mh, 9,4, 9,22, ch,r)
66 }
67 if gid == 72 { // H
68 nf_us(mask,mw,mh, 3,4, 3,22, ch,r); nf_us(mask,mw,mh, 15,4, 15,22, ch,r); nf_us(mask,mw,mh, 3,13, 15,13, ch,r)
69 }
70 if gid == 83 { // S
71 nf_us(mask,mw,mh, 15,7, 11,4, ch,r); nf_us(mask,mw,mh, 11,4, 7,4, ch,r); nf_us(mask,mw,mh, 7,4, 3,7, ch,r)
72 nf_us(mask,mw,mh, 3,7, 3,10, ch,r); nf_us(mask,mw,mh, 3,10, 7,13, ch,r); nf_us(mask,mw,mh, 7,13, 11,13, ch,r)
73 nf_us(mask,mw,mh, 11,13, 15,16, ch,r); nf_us(mask,mw,mh, 15,16, 15,19, ch,r); nf_us(mask,mw,mh, 15,19, 11,22, ch,r)
74 nf_us(mask,mw,mh, 11,22, 7,22, ch,r); nf_us(mask,mw,mh, 7,22, 3,19, ch,r)
75 }
76 if gid == 66 { // B
77 nf_us(mask,mw,mh, 3,4, 3,22, ch,r); nf_us(mask,mw,mh, 3,4, 11,4, ch,r); nf_us(mask,mw,mh, 11,4, 15,7, ch,r)
78 nf_us(mask,mw,mh, 15,7, 11,13, ch,r); nf_us(mask,mw,mh, 11,13, 3,13, ch,r); nf_us(mask,mw,mh, 3,13, 12,13, ch,r)
79 nf_us(mask,mw,mh, 12,13, 15,17, ch,r); nf_us(mask,mw,mh, 15,17, 11,22, ch,r); nf_us(mask,mw,mh, 11,22, 3,22, ch,r)
80 }
81 if gid == 82 { // R
82 nf_us(mask,mw,mh, 3,4, 3,22, ch,r); nf_us(mask,mw,mh, 3,4, 11,4, ch,r); nf_us(mask,mw,mh, 11,4, 15,7, ch,r)
83 nf_us(mask,mw,mh, 15,7, 11,13, ch,r); nf_us(mask,mw,mh, 11,13, 3,13, ch,r); nf_us(mask,mw,mh, 9,13, 15,22, ch,r)
84 }
85 if gid == 79 { // O
86 nf_us(mask,mw,mh, 9,4, 13,5, ch,r); nf_us(mask,mw,mh, 13,5, 15,9, ch,r); nf_us(mask,mw,mh, 15,9, 15,17, ch,r)
87 nf_us(mask,mw,mh, 15,17, 13,21, ch,r); nf_us(mask,mw,mh, 13,21, 9,22, ch,r); nf_us(mask,mw,mh, 9,22, 5,21, ch,r)
88 nf_us(mask,mw,mh, 5,21, 3,17, ch,r); nf_us(mask,mw,mh, 3,17, 3,9, ch,r); nf_us(mask,mw,mh, 3,9, 5,5, ch,r); nf_us(mask,mw,mh, 5,5, 9,4, ch,r)
89 }
90 if gid == 87 { // W
91 nf_us(mask,mw,mh, 3,4, 6,22, ch,r); nf_us(mask,mw,mh, 6,22, 9,10, ch,r); nf_us(mask,mw,mh, 9,10, 12,22, ch,r); nf_us(mask,mw,mh, 12,22, 15,4, ch,r)
92 }
93 if gid == 69 { // E
94 nf_us(mask,mw,mh, 3,4, 3,22, ch,r); nf_us(mask,mw,mh, 3,4, 15,4, ch,r); nf_us(mask,mw,mh, 3,13, 12,13, ch,r); nf_us(mask,mw,mh, 3,22, 15,22, ch,r)
95 }
96 if gid == 48 { // 0 (rounded, like O)
97 nf_us(mask,mw,mh, 9,4, 13,5, ch,r); nf_us(mask,mw,mh, 13,5, 15,9, ch,r); nf_us(mask,mw,mh, 15,9, 15,17, ch,r)
98 nf_us(mask,mw,mh, 15,17, 13,21, ch,r); nf_us(mask,mw,mh, 13,21, 9,22, ch,r); nf_us(mask,mw,mh, 9,22, 5,21, ch,r)
99 nf_us(mask,mw,mh, 5,21, 3,17, ch,r); nf_us(mask,mw,mh, 3,17, 3,9, ch,r); nf_us(mask,mw,mh, 3,9, 5,5, ch,r); nf_us(mask,mw,mh, 5,5, 9,4, ch,r)
100 }
101 if gid == 49 { // 1
102 nf_us(mask,mw,mh, 6,7, 9,4, ch,r); nf_us(mask,mw,mh, 9,4, 9,22, ch,r); nf_us(mask,mw,mh, 5,22, 13,22, ch,r)
103 }
104 if gid == 50 { // 2
105 nf_us(mask,mw,mh, 3,8, 5,5, ch,r); nf_us(mask,mw,mh, 5,5, 11,5, ch,r); nf_us(mask,mw,mh, 11,5, 14,8, ch,r)
106 nf_us(mask,mw,mh, 14,8, 14,11, ch,r); nf_us(mask,mw,mh, 14,11, 3,22, ch,r); nf_us(mask,mw,mh, 3,22, 15,22, ch,r)
107 }
108 if gid == 51 { // 3
109 nf_us(mask,mw,mh, 4,6, 8,4, ch,r); nf_us(mask,mw,mh, 8,4, 12,4, ch,r); nf_us(mask,mw,mh, 12,4, 15,8, ch,r)
110 nf_us(mask,mw,mh, 15,8, 11,12, ch,r); nf_us(mask,mw,mh, 11,12, 8,12, ch,r); nf_us(mask,mw,mh, 8,12, 12,13, ch,r)
111 nf_us(mask,mw,mh, 12,13, 15,16, ch,r); nf_us(mask,mw,mh, 15,16, 15,19, ch,r); nf_us(mask,mw,mh, 15,19, 12,22, ch,r)
112 nf_us(mask,mw,mh, 12,22, 8,22, ch,r); nf_us(mask,mw,mh, 8,22, 4,20, ch,r)
113 }
114 if gid == 52 { // 4
115 nf_us(mask,mw,mh, 11,4, 3,16, ch,r); nf_us(mask,mw,mh, 3,16, 15,16, ch,r); nf_us(mask,mw,mh, 12,4, 12,22, ch,r)
116 }
117 if gid == 53 { // 5
118 nf_us(mask,mw,mh, 14,4, 5,4, ch,r); nf_us(mask,mw,mh, 5,4, 5,11, ch,r); nf_us(mask,mw,mh, 5,11, 11,11, ch,r)
119 nf_us(mask,mw,mh, 11,11, 15,15, ch,r); nf_us(mask,mw,mh, 15,15, 15,18, ch,r); nf_us(mask,mw,mh, 15,18, 11,22, ch,r)
120 nf_us(mask,mw,mh, 11,22, 6,22, ch,r); nf_us(mask,mw,mh, 6,22, 3,19, ch,r)
121 }
122 if gid == 54 { // 6
123 nf_us(mask,mw,mh, 13,6, 9,4, ch,r); nf_us(mask,mw,mh, 9,4, 5,8, ch,r); nf_us(mask,mw,mh, 5,8, 3,14, ch,r)
124 nf_us(mask,mw,mh, 3,14, 3,18, ch,r); nf_us(mask,mw,mh, 3,18, 6,22, ch,r); nf_us(mask,mw,mh, 6,22, 10,22, ch,r)
125 nf_us(mask,mw,mh, 10,22, 14,19, ch,r); nf_us(mask,mw,mh, 14,19, 14,15, ch,r); nf_us(mask,mw,mh, 14,15, 10,12, ch,r)
126 nf_us(mask,mw,mh, 10,12, 6,13, ch,r); nf_us(mask,mw,mh, 6,13, 3,16, ch,r)
127 }
128 if gid == 55 { // 7
129 nf_us(mask,mw,mh, 3,4, 15,4, ch,r); nf_us(mask,mw,mh, 15,4, 7,22, ch,r)
130 }
131 if gid == 56 { // 8
132 nf_us(mask,mw,mh, 9,4, 6,5, ch,r); nf_us(mask,mw,mh, 6,5, 5,8, ch,r); nf_us(mask,mw,mh, 5,8, 7,11, ch,r)
133 nf_us(mask,mw,mh, 7,11, 9,12, ch,r); nf_us(mask,mw,mh, 9,12, 11,11, ch,r); nf_us(mask,mw,mh, 11,11, 13,8, ch,r)
134 nf_us(mask,mw,mh, 13,8, 12,5, ch,r); nf_us(mask,mw,mh, 12,5, 9,4, ch,r)
135 nf_us(mask,mw,mh, 9,12, 6,13, ch,r); nf_us(mask,mw,mh, 6,13, 4,17, ch,r); nf_us(mask,mw,mh, 4,17, 6,21, ch,r)
136 nf_us(mask,mw,mh, 6,21, 9,22, ch,r); nf_us(mask,mw,mh, 9,22, 12,21, ch,r); nf_us(mask,mw,mh, 12,21, 14,17, ch,r)
137 nf_us(mask,mw,mh, 14,17, 12,13, ch,r); nf_us(mask,mw,mh, 12,13, 9,12, ch,r)
138 }
139 if gid == 57 { // 9
140 nf_us(mask,mw,mh, 9,4, 12,5, ch,r); nf_us(mask,mw,mh, 12,5, 13,8, ch,r); nf_us(mask,mw,mh, 13,8, 12,11, ch,r)
141 nf_us(mask,mw,mh, 12,11, 9,12, ch,r); nf_us(mask,mw,mh, 9,12, 6,11, ch,r); nf_us(mask,mw,mh, 6,11, 5,8, ch,r)
142 nf_us(mask,mw,mh, 5,8, 6,5, ch,r); nf_us(mask,mw,mh, 6,5, 9,4, ch,r)
143 nf_us(mask,mw,mh, 13,8, 13,16, ch,r); nf_us(mask,mw,mh, 13,16, 10,22, ch,r); nf_us(mask,mw,mh, 10,22, 6,21, ch,r)
144 }
145 return 0
146}
147
148// advance width in pixels at cell height ch.
149func nf_adv_px(ch: i64) -> i64 { return ch * NF_ADV_U / NF_EM }
150
151// ---- WASM-SAFE i64 framebuffer blit (caller provides scratch mask; NO sys_mmap) ----
152// Blit glyph `gid` at (ox,oy) cell-height ch into i64 packed-RGB framebuffer fb (fbw x fbh), light `ink`
153// alpha-blended over the existing pixel (integer coverage). `mask` must be >= nf_adv_px(ch)*NF_SS * ch*NF_SS
154// bytes of scratch (e.g. a fixed region in our linear memory). Returns advance px.
155func nf_blit_glyph_mem(fb: *i64, fbw: i64, fbh: i64, mask: *u8, ox: i64, oy: i64, ch: i64, gid: i64, ink: i64) -> i64 {
156 let adv: i64 = ch * NF_ADV_U / NF_EM
157 let mw: i64 = adv * NF_SS
158 let mh: i64 = ch * NF_SS
159 var r: i64 = ch * NF_SS / 16
160 if r < 1 { r = 1 }
161 let mn: i64 = mw * mh
162 var z: i64 = 0
163 while z < mn { mask[z] = 0 as u8; z = z + 1 } // clear caller scratch
164 nf_glyph(mask, mw, mh, gid, ch, r)
165 let ss2: i64 = NF_SS * NF_SS
166 let ir: i64 = ink & 255
167 let ig: i64 = (ink >> 8) & 255
168 let ib: i64 = (ink >> 16) & 255
169 var oy2: i64 = 0
170 while oy2 < ch {
171 var ox2: i64 = 0
172 while ox2 < adv {
173 var cov: i64 = 0
174 var by: i64 = oy2 * NF_SS
175 let byend: i64 = by + NF_SS
176 while by < byend {
177 var bx: i64 = ox2 * NF_SS
178 let bxend: i64 = bx + NF_SS
179 while bx < bxend { if (mask[by*mw + bx]&0xff) == 1 { cov = cov + 1 } bx = bx + 1 }
180 by = by + 1
181 }
182 if cov > 0 {
183 let px: i64 = oy + oy2
184 let pxx: i64 = ox + ox2
185 if px >= 0 { if px < fbh { if pxx >= 0 { if pxx < fbw {
186 let idx: i64 = px*fbw + pxx
187 let bg: i64 = fb[idx]
188 let br0: i64 = bg & 255
189 let bg0: i64 = (bg >> 8) & 255
190 let bb0: i64 = (bg >> 16) & 255
191 let nr: i64 = (ir*cov + br0*(ss2-cov)) / ss2
192 let ng: i64 = (ig*cov + bg0*(ss2-cov)) / ss2
193 let nb: i64 = (ib*cov + bb0*(ss2-cov)) / ss2
194 fb[idx] = (nr & 255) | ((ng & 255) << 8) | ((nb & 255) << 16)
195 } } } }
196 }
197 ox2 = ox2 + 1
198 }
199 oy2 = oy2 + 1
200 }
201 return adv
202}
203// draw NUL-free string s[0..slen) at (ox,oy) cell-height ch into i64 fb with light ink; returns x advance.
204func nf_draw_text_mem(fb: *i64, fbw: i64, fbh: i64, mask: *u8, ox: i64, oy: i64, ch: i64, s: *u8, slen: i64, ink: i64) -> i64 {
205 var x: i64 = ox
206 var i: i64 = 0
207 while i < slen {
208 let c: i64 = s[i] & 0xff
209 let adv: i64 = nf_blit_glyph_mem(fb, fbw, fbh, mask, x, oy, ch, c, ink)
210 x = x + adv
211 i = i + 1
212 }
213 return x - ox
214}