nx_font8x8.nx source
↩ module page · 170 lines · 9943 B
1// nx_font8x8.nx -- sovereign 8x8 ASCII bitmap font (printable 0x20..0x7E). Pure glyph DATA (like an
2// AES S-box), not 3rd-party code: 8 bytes/glyph, byte=row top->bottom, bit 0 (LSB)=leftmost column.
3// font8x8_table() returns a 96*8 byte table indexed by (char-0x20)*8.
4import "nx_syscalls.nx"
5
6func fg(t: *u8, i: i64, b0: i64, b1: i64, b2: i64, b3: i64, b4: i64, b5: i64, b6: i64, b7: i64) -> i64 {
7 let o: i64 = i * 8
8 t[o+0]=b0 as u8; t[o+1]=b1 as u8; t[o+2]=b2 as u8; t[o+3]=b3 as u8
9 t[o+4]=b4 as u8; t[o+5]=b5 as u8; t[o+6]=b6 as u8; t[o+7]=b7 as u8
10 return 0
11}
12
13func font8x8_table() -> *u8 {
14 let t: *u8 = sys_mmap(96 * 8)
15 fg(t,0, 0,0,0,0,0,0,0,0) // space
16 fg(t,1, 0x18,0x3C,0x3C,0x18,0x18,0x00,0x18,0x00) // !
17 fg(t,2, 0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x00) // "
18 fg(t,3, 0x36,0x36,0x7F,0x36,0x7F,0x36,0x36,0x00) // #
19 fg(t,4, 0x0C,0x3E,0x03,0x1E,0x30,0x1F,0x0C,0x00) // $
20 fg(t,5, 0x00,0x63,0x33,0x18,0x0C,0x66,0x63,0x00) // %
21 fg(t,6, 0x1C,0x36,0x1C,0x6E,0x3B,0x33,0x6E,0x00) // &
22 fg(t,7, 0x06,0x06,0x03,0x00,0x00,0x00,0x00,0x00) // '
23 fg(t,8, 0x18,0x0C,0x06,0x06,0x06,0x0C,0x18,0x00) // (
24 fg(t,9, 0x06,0x0C,0x18,0x18,0x18,0x0C,0x06,0x00) // )
25 fg(t,10, 0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00) // *
26 fg(t,11, 0x00,0x0C,0x0C,0x3F,0x0C,0x0C,0x00,0x00) // +
27 fg(t,12, 0x00,0x00,0x00,0x00,0x00,0x0C,0x0C,0x06) // ,
28 fg(t,13, 0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00) // -
29 fg(t,14, 0x00,0x00,0x00,0x00,0x00,0x0C,0x0C,0x00) // .
30 fg(t,15, 0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x00) // /
31 fg(t,16, 0x3E,0x63,0x73,0x7B,0x6F,0x67,0x3E,0x00) // 0
32 fg(t,17, 0x0C,0x0E,0x0C,0x0C,0x0C,0x0C,0x3F,0x00) // 1
33 fg(t,18, 0x1E,0x33,0x30,0x1C,0x06,0x33,0x3F,0x00) // 2
34 fg(t,19, 0x1E,0x33,0x30,0x1C,0x30,0x33,0x1E,0x00) // 3
35 fg(t,20, 0x38,0x3C,0x36,0x33,0x7F,0x30,0x78,0x00) // 4
36 fg(t,21, 0x3F,0x03,0x1F,0x30,0x30,0x33,0x1E,0x00) // 5
37 fg(t,22, 0x1C,0x06,0x03,0x1F,0x33,0x33,0x1E,0x00) // 6
38 fg(t,23, 0x3F,0x33,0x30,0x18,0x0C,0x0C,0x0C,0x00) // 7
39 fg(t,24, 0x1E,0x33,0x33,0x1E,0x33,0x33,0x1E,0x00) // 8
40 fg(t,25, 0x1E,0x33,0x33,0x3E,0x30,0x18,0x0E,0x00) // 9
41 fg(t,26, 0x00,0x0C,0x0C,0x00,0x00,0x0C,0x0C,0x00) // :
42 fg(t,27, 0x00,0x0C,0x0C,0x00,0x00,0x0C,0x0C,0x06) // ;
43 fg(t,28, 0x18,0x0C,0x06,0x03,0x06,0x0C,0x18,0x00) // <
44 fg(t,29, 0x00,0x00,0x3F,0x00,0x00,0x3F,0x00,0x00) // =
45 fg(t,30, 0x06,0x0C,0x18,0x30,0x18,0x0C,0x06,0x00) // >
46 fg(t,31, 0x1E,0x33,0x30,0x18,0x0C,0x00,0x0C,0x00) // ?
47 fg(t,32, 0x3E,0x63,0x7B,0x7B,0x7B,0x03,0x1E,0x00) // @
48 fg(t,33, 0x0C,0x1E,0x33,0x33,0x3F,0x33,0x33,0x00) // A
49 fg(t,34, 0x3F,0x66,0x66,0x3E,0x66,0x66,0x3F,0x00) // B
50 fg(t,35, 0x3C,0x66,0x03,0x03,0x03,0x66,0x3C,0x00) // C
51 fg(t,36, 0x1F,0x36,0x66,0x66,0x66,0x36,0x1F,0x00) // D
52 fg(t,37, 0x7F,0x46,0x16,0x1E,0x16,0x46,0x7F,0x00) // E
53 fg(t,38, 0x7F,0x46,0x16,0x1E,0x16,0x06,0x0F,0x00) // F
54 fg(t,39, 0x3C,0x66,0x03,0x03,0x73,0x66,0x7C,0x00) // G
55 fg(t,40, 0x33,0x33,0x33,0x3F,0x33,0x33,0x33,0x00) // H
56 fg(t,41, 0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00) // I
57 fg(t,42, 0x78,0x30,0x30,0x30,0x33,0x33,0x1E,0x00) // J
58 fg(t,43, 0x67,0x66,0x36,0x1E,0x36,0x66,0x67,0x00) // K
59 fg(t,44, 0x0F,0x06,0x06,0x06,0x46,0x66,0x7F,0x00) // L
60 fg(t,45, 0x63,0x77,0x7F,0x7F,0x6B,0x63,0x63,0x00) // M
61 fg(t,46, 0x63,0x67,0x6F,0x7B,0x73,0x63,0x63,0x00) // N
62 fg(t,47, 0x1C,0x36,0x63,0x63,0x63,0x36,0x1C,0x00) // O
63 fg(t,48, 0x3F,0x66,0x66,0x3E,0x06,0x06,0x0F,0x00) // P
64 fg(t,49, 0x1E,0x33,0x33,0x33,0x3B,0x1E,0x38,0x00) // Q
65 fg(t,50, 0x3F,0x66,0x66,0x3E,0x36,0x66,0x67,0x00) // R
66 fg(t,51, 0x1E,0x33,0x07,0x0E,0x38,0x33,0x1E,0x00) // S
67 fg(t,52, 0x3F,0x2D,0x0C,0x0C,0x0C,0x0C,0x1E,0x00) // T
68 fg(t,53, 0x33,0x33,0x33,0x33,0x33,0x33,0x3F,0x00) // U
69 fg(t,54, 0x33,0x33,0x33,0x33,0x33,0x1E,0x0C,0x00) // V
70 fg(t,55, 0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00) // W
71 fg(t,56, 0x63,0x63,0x36,0x1C,0x1C,0x36,0x63,0x00) // X
72 fg(t,57, 0x33,0x33,0x33,0x1E,0x0C,0x0C,0x1E,0x00) // Y
73 fg(t,58, 0x7F,0x63,0x31,0x18,0x4C,0x66,0x7F,0x00) // Z
74 fg(t,59, 0x1E,0x06,0x06,0x06,0x06,0x06,0x1E,0x00) // [
75 fg(t,60, 0x03,0x06,0x0C,0x18,0x30,0x60,0x40,0x00) // backslash
76 fg(t,61, 0x1E,0x18,0x18,0x18,0x18,0x18,0x1E,0x00) // ]
77 fg(t,62, 0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00) // ^
78 fg(t,63, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF) // _
79 fg(t,64, 0x0C,0x0C,0x18,0x00,0x00,0x00,0x00,0x00) // `
80 fg(t,65, 0x00,0x00,0x1E,0x30,0x3E,0x33,0x6E,0x00) // a
81 fg(t,66, 0x07,0x06,0x06,0x3E,0x66,0x66,0x3B,0x00) // b
82 fg(t,67, 0x00,0x00,0x1E,0x33,0x03,0x33,0x1E,0x00) // c
83 fg(t,68, 0x38,0x30,0x30,0x3E,0x33,0x33,0x6E,0x00) // d
84 fg(t,69, 0x00,0x00,0x1E,0x33,0x3F,0x03,0x1E,0x00) // e
85 fg(t,70, 0x1C,0x36,0x06,0x0F,0x06,0x06,0x0F,0x00) // f
86 fg(t,71, 0x00,0x00,0x6E,0x33,0x33,0x3E,0x30,0x1F) // g
87 fg(t,72, 0x07,0x06,0x36,0x6E,0x66,0x66,0x67,0x00) // h
88 fg(t,73, 0x0C,0x00,0x0F,0x0C,0x0C,0x0C,0x3F,0x00) // i (serifed wide: fills the cell -> no false word-gap at 2x)
89 fg(t,74, 0x30,0x00,0x30,0x30,0x30,0x33,0x33,0x1E) // j
90 fg(t,75, 0x07,0x06,0x66,0x36,0x1E,0x36,0x67,0x00) // k
91 fg(t,76, 0x0E,0x0C,0x0C,0x0C,0x0C,0x0C,0x3F,0x00) // l (wide foot serif, matches i)
92 fg(t,77, 0x00,0x00,0x7F,0x6B,0x6B,0x6B,0x6B,0x00) // m (top bar + THREE distinct legs -- the old two solid rows fused into 'n' at 2x)
93 fg(t,78, 0x00,0x00,0x1F,0x33,0x33,0x33,0x33,0x00) // n
94 fg(t,79, 0x00,0x00,0x1E,0x33,0x33,0x33,0x1E,0x00) // o
95 fg(t,80, 0x00,0x00,0x3B,0x66,0x66,0x3E,0x06,0x0F) // p
96 fg(t,81, 0x00,0x00,0x6E,0x33,0x33,0x3E,0x30,0x78) // q
97 fg(t,82, 0x00,0x00,0x3E,0x66,0x06,0x06,0x0F,0x00) // r (shoulder at TOP only + clean stem -- the old low right ink read as 'p')
98 fg(t,83, 0x00,0x00,0x3E,0x03,0x1E,0x30,0x1F,0x00) // s
99 fg(t,84, 0x08,0x0C,0x3E,0x0C,0x0C,0x2C,0x18,0x00) // t
100 fg(t,85, 0x00,0x00,0x33,0x33,0x33,0x33,0x6E,0x00) // u
101 fg(t,86, 0x00,0x00,0x33,0x33,0x33,0x1E,0x0C,0x00) // v
102 fg(t,87, 0x00,0x00,0x63,0x6B,0x7F,0x7F,0x36,0x00) // w
103 fg(t,88, 0x00,0x00,0x63,0x36,0x1C,0x36,0x63,0x00) // x
104 fg(t,89, 0x00,0x00,0x33,0x33,0x33,0x3E,0x30,0x1F) // y
105 fg(t,90, 0x00,0x00,0x3F,0x19,0x0C,0x26,0x3F,0x00) // z
106 fg(t,91, 0x38,0x0C,0x0C,0x07,0x0C,0x0C,0x38,0x00) // {
107 fg(t,92, 0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00) // |
108 fg(t,93, 0x07,0x0C,0x0C,0x38,0x0C,0x0C,0x07,0x00) // }
109 fg(t,94, 0x6E,0x3B,0x00,0x00,0x00,0x00,0x00,0x00) // ~
110 fg(t,95, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00) // (0x7F) blank
111 return t
112}
113
114// ---- PROPORTIONAL METRICS (the font's single source of truth -- layout MEASURES and paint DRAWS with
115// the SAME numbers, so reserved boxes exactly match painted text; no monospace-cell hacks). ----
116
117// ink width of a glyph in 1x px: the rightmost set column + 1 (0 for space/blank).
118func font8x8_ink_w(t: *u8, ch: i64) -> i64 {
119 if ch <= 0x20 { return 0 }
120 if ch > 0x7E { return 0 }
121 let go: i64 = (ch - 0x20) * 8
122 var w: i64 = 0
123 var row: i64 = 0
124 while row < 8 {
125 let bits: i64 = t[go + row] & 0xff
126 var col: i64 = 7
127 var stop: i64 = 0
128 while col >= 0 { if stop==0 { if ((bits >> col) & 1) == 1 { if col+1 > w { w = col+1 } stop=1 } } col = col - 1 }
129 row = row + 1
130 }
131 return w
132}
133// advance of a char in 1x px: ink + 2px letter-spacing, clamped [4..9]; space = 6 (3x the letter gap so
134// words separate cleanly -- OCR-judge-validated). Non-ASCII draws nothing but advances like a space.
135func font8x8_adv(t: *u8, ch: i64) -> i64 {
136 if ch == 0x20 { return 6 }
137 if ch < 0x20 { return 6 }
138 if ch > 0x7E { return 6 }
139 var aw: i64 = font8x8_ink_w(t, ch) + 2
140 if aw > 9 { aw = 9 }
141 if aw < 4 { aw = 4 }
142 return aw
143}
144// measured width of text[start..end) in 1x px.
145func font8x8_text_w(t: *u8, text: *u8, start: i64, end: i64) -> i64 {
146 var w: i64 = 0
147 var i: i64 = start
148 while i < end { w = w + font8x8_adv(t, text[i] & 0xff); i = i + 1 }
149 return w
150}
151// MEASURED word-wrap break (the proportional twin of tw_next_break): first index e > start such that
152// text[start..e) fits in avail_px, breaking AFTER the last space where possible; a single long word
153// hard-breaks at the last char that fits. Returns len when the rest fits.
154func font8x8_next_break(t: *u8, text: *u8, len: i64, avail_px: i64, start: i64) -> i64 {
155 if start >= len { return len }
156 var w: i64 = 0
157 var i: i64 = start
158 var brk: i64 = 0 - 1
159 while i < len {
160 w = w + font8x8_adv(t, text[i] & 0xff)
161 if w > avail_px {
162 if brk > start { return brk + 1 } // break after the last space
163 if i > start { return i } // hard break inside a long word
164 return start + 1 // avail narrower than one glyph: force progress
165 }
166 if (text[i] & 0xff) == 32 { brk = i }
167 i = i + 1
168 }
169 return len
170}