code wiki / (root) / nx_nishi_font_core.nx

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}