code wiki / _hdl_build / nx_font_mockup.nx

nx_font_mockup.nx source

↩ module page · 130 lines · 6305 B

1// nx_font_mockup.nx -- a STANDALONE before/after typography mockup (NOT wired into any browser). Renders 2// the same paragraph three ways into one PNG so the operator can pick the direction by eye: 3// (1) CURRENT -- the 8x8 bitmap font that ships now (monospace, blocky). 4// (2) ATTEMPT -- the vector font crammed into the layout's fixed 9px monospace cell at ~11px (the one 5// that "looked way worse": small + monospace + muddy AA). 6// (3) TARGET -- the SAME vector font rendered PROPORTIONALLY at a real body size (~16px cap) with 7// line spacing (the Chrome-like goal). Proves whether proportional+size fixes it. 8// Uses the sovereign TTF engine (nx_ttf_fontlib) + our own nishi_sans.ttf + nx_png_write. 100% sovereign. 9// expect_exit: 0 license_tier: ORIGINAL 10import "nx_ttf_fontlib.nx" 11import "nx_font8x8.nx" 12import "nx_png_write.nx" 13 14func mk_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 15 16// fill an RGBA framebuffer solid 17func mk_fill(px: *u8, W: i64, H: i64, r: i64, g: i64, b: i64) -> i64 { 18 var i: i64=0 19 while i < W*H { px[i*4]=r as u8; px[i*4+1]=g as u8; px[i*4+2]=b as u8; px[i*4+3]=255 as u8; i=i+1 } 20 return 0 21} 22func mk_rect(px: *u8, W: i64, H: i64, x: i64, y: i64, w: i64, h: i64, r: i64, g: i64, b: i64) -> i64 { 23 var yy: i64=0 24 while yy<h { var xx: i64=0; while xx<w { let fx: i64=x+xx; let fy: i64=y+yy; if fx>=0 { if fx<W { if fy>=0 { if fy<H { let o: i64=(fy*W+fx)*4; px[o]=r as u8; px[o+1]=g as u8; px[o+2]=b as u8; px[o+3]=255 as u8 } } } } xx=xx+1 } yy=yy+1 } 25 return 0 26} 27 28// 8x8 bitmap draw (the CURRENT font), fixed 9*scale advance 29func mk_draw8(px: *u8, W: i64, H: i64, x0: i64, y0: i64, s: *u8, n: i64, scale: i64, r: i64, g: i64, b: i64) -> i64 { 30 let ft8: *u8 = font8x8_table() 31 var i: i64=0 32 while i<n { 33 let ch: i64 = s[i]&0xff 34 let cx: i64 = x0 + i*9*scale 35 if ch>=0x20 { if ch<=0x7E { 36 let go: i64=(ch-0x20)*8 37 var row: i64=0 38 while row<8 { let bits: i64=ft8[go+row]&0xff; var col: i64=0; while col<8 { if ((bits>>col)&1)==1 { mk_rect(px,W,H, cx+col*scale, y0+row*scale, scale, scale, r,g,b) } col=col+1 } row=row+1 } 39 } } 40 i=i+1 41 } 42 return 0 43} 44// 8x8 word-wrapped paragraph, returns the y after the last line 45func mk_para8(px: *u8, W: i64, H: i64, x0: i64, y0: i64, maxw: i64, s: *u8, n: i64, scale: i64, lh: i64, r: i64, g: i64, b: i64) -> i64 { 46 let adv: i64 = 9*scale 47 let cols: i64 = maxw/adv 48 var y: i64 = y0 49 var i: i64 = 0 50 var linestart: i64 = 0 51 var col: i64 = 0 52 while i < n { 53 // find next word end 54 var j: i64=i 55 while j<n { if (s[j]&0xff)==32 { j=n+1 } else { j=j+1 } } 56 var we: i64=i 57 var go: i64=1 58 while go==1 { if we>=n { go=0 } else { if (s[we]&0xff)==32 { go=0 } else { we=we+1 } } } 59 let wl: i64 = we-i 60 if col+wl > cols { y=y+lh; col=0 } // wrap 61 mk_draw8(px,W,H, x0+col*adv, y, ((s as i64)+i) as *u8, wl, scale, r,g,b) 62 col = col+wl+1 // +1 for space 63 i = we+1 64 } 65 return y+lh 66} 67 68// vector proportional word-wrapped paragraph via the TTF engine, returns y after last line 69func mk_para_vec(fh: *i64, fb: *Framebuffer, px: *u8, W: i64, H: i64, x0: i64, y0: i64, maxw: i64, s: *u8, n: i64, ph: i64, mono: i64, lh: i64, r: i64, g: i64, b: i64) -> i64 { 70 var cellw: i64 = 0 71 if mono==1 { cellw = 9 } // monospace-cell mode (the "attempt") 72 var y: i64 = y0 73 var x: i64 = x0 74 var i: i64 = 0 75 let spadv: i64 = tf_char_adv(fh, 32, ph) 76 while i < n { 77 var we: i64=i 78 var go: i64=1 79 while go==1 { if we>=n { go=0 } else { if (s[we]&0xff)==32 { go=0 } else { we=we+1 } } } 80 let wl: i64 = we-i 81 // measure word 82 var ww: i64=0 83 var k: i64=i 84 while k<we { if mono==1 { ww=ww+9 } else { ww=ww+tf_char_adv(fh, s[k]&0xff, ph) } k=k+1 } 85 if x+ww > x0+maxw { y=y+lh; x=x0 } // wrap 86 tf_draw_text(fh, fb, x, y, ((s as i64)+i) as *u8, wl, ph, r, g, b, cellw) 87 x = x + ww 88 if mono==1 { x = x + 9 } else { x = x + spadv } // space 89 i = we+1 90 } 91 return y+lh 92} 93 94func main() -> i64 { 95 sys_write(1, "font-mockup: rendering the same paragraph 3 ways (current 8x8 / attempt / Chrome-like target)\n\x00" as *u8, 92) 96 let W: i64 = 1000 97 let H: i64 = 560 98 let fb: *Framebuffer = sys_mmap(NX_FRAMEBUFFER_BYTES) as *Framebuffer 99 let px: *u8 = sys_mmap(W*H*4+64) 100 nx_framebuffer_init(fb, px, W, H) 101 mk_fill(px, W, H, 255, 255, 255) 102 103 let fh: *i64 = tf_load("web_assets/nishi_sans.ttf\x00" as *u8) 104 if fh == (0 as *i64) { sys_write(2, "mockup: cannot load nishi_sans.ttf\n\x00" as *u8, 35); return 1 } 105 106 let para: *u8 = "A web browser is an application for accessing websites. When a user requests a web page, the browser retrieves its files from a web server and then displays the page on the screen.\x00" as *u8 107 let pn: i64 = mk_slen(para) 108 let lx: i64 = 40 109 let maxw: i64 = 920 110 111 // labels (small 8x8, grey) + a divider per section 112 mk_draw8(px,W,H, lx, 24, "1. CURRENT -- 8x8 bitmap font (what ships now)\x00" as *u8, 46, 1, 120,120,130) 113 mk_para8(px,W,H, lx, 48, maxw, para, pn, 2, 22, 30,30,40) 114 mk_rect(px,W,H, lx, 168, maxw, 1, 220,220,228) 115 116 mk_draw8(px,W,H, lx, 190, "2. ATTEMPT -- vector font, monospace cell, ~11px (the one that looked worse)\x00" as *u8, 75, 1, 120,120,130) 117 mk_para_vec(fh, fb, px, W, H, lx, 214, maxw, para, pn, 15, 1, 22, 30,30,40) 118 mk_rect(px,W,H, lx, 336, maxw, 1, 220,220,228) 119 120 mk_draw8(px,W,H, lx, 358, "3. TARGET -- SAME vector font, PROPORTIONAL, ~16px + line spacing (Chrome-like)\x00" as *u8, 78, 1, 120,120,130) 121 mk_para_vec(fh, fb, px, W, H, lx, 386, maxw, para, pn, 22, 0, 30, 24,24,32) 122 123 // RGBA -> RGB and write PNG 124 let rgb: *u8 = sys_mmap(W*H*3+64) 125 var p: i64=0 126 while p < W*H { rgb[p*3]=px[p*4]; rgb[p*3+1]=px[p*4+1]; rgb[p*3+2]=px[p*4+2]; p=p+1 } 127 nx_png_write_rgb("knowledge/status/font_mockup.png\x00" as *u8, rgb, W, H) 128 sys_write(1, "font-mockup: wrote knowledge/status/font_mockup.png\n\x00" as *u8, 51) 129 return 0 130}