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}