nx_text_render.nx source
↩ module page · 119 lines · 3874 B
1// nx_text_render.nx -- text rendering onto 1bpp framebuffer.
2//
3// Composes nx_font_1bpp + nx_fb_1bpp. Renders a single character
4// or a fixed-length string at (x, y) onto the framebuffer using
5// the supplied font.
6//
7// Per substrate-tier discipline: substrate-side rendering with no
8// allocation; all bytes from caller's arena.
9//
10// Composes:
11// nx_fb_1bpp -- target framebuffer
12// nx_font_1bpp -- glyph source
13
14import "nx_syscalls.nx"
15import "nx_tier.nx"
16import "nx_fb_1bpp.nx"
17import "nx_font_1bpp.nx"
18
19const NX_TR_OK: nx_int = 0
20const NX_TR_ERR_CLIPPED_ALL: nx_int = 1
21const NX_TR_ERR_BAD_FONT: nx_int = 2
22
23// ===== nx_text_render_char =========================================
24//
25// Render one character at (x, y) on the framebuffer. Pixels with
26// bit=1 in the glyph are set to ON. Out-of-bounds pixels are
27// silently clipped.
28//
29// Returns the x-advance (caller increments x by this for next char).
30// 0 if the char is not in the font.
31
32func nx_text_render_char(fb: *NxFb1bpp, font: *NxFont,
33 x: nx_size, y: nx_size, ch: nx_int) -> nx_size {
34 if (fb as i64) == 0 { return 0 }
35 if (font as i64) == 0 { return 0 }
36 if nx_font_has_char(font, ch) == 0 { return font.glyph_width }
37 var col: nx_size = 0
38 while col < font.glyph_width {
39 var row: nx_size = 0
40 while row < font.glyph_height {
41 let p: nx_int = nx_font_glyph_pixel(font, ch, col, row)
42 if p == 1 {
43 nx_fb_1bpp_set_pixel(fb, x + col, y + row, 1)
44 }
45 row = row + 1
46 }
47 col = col + 1
48 }
49 return font.glyph_width
50}
51
52// ===== nx_text_render_string =======================================
53//
54// Render a string of bytes at (x, y). str_ptr/str_len comes from
55// caller. Spaces auto-advance by glyph_width (default monospace).
56// Returns total x-advance (sum of per-char advances).
57
58func nx_text_render_string(fb: *NxFb1bpp, font: *NxFont,
59 x: nx_size, y: nx_size,
60 str_ptr: *u8, str_len: nx_size) -> nx_size {
61 if (str_ptr as i64) == 0 { return 0 }
62 var total_adv: nx_size = 0
63 var i: nx_size = 0
64 while i < str_len {
65 let ch: nx_int = (str_ptr[i] as i64) & 255
66 let adv: nx_size = nx_text_render_char(fb, font, x + total_adv, y, ch)
67 total_adv = total_adv + adv
68 i = i + 1
69 }
70 return total_adv
71}
72
73// ===== nx_text_string_width ========================================
74//
75// How wide will this string be when rendered? Used for centering /
76// right-alignment / overflow checking.
77
78func nx_text_string_width(font: *NxFont, str_len: nx_size) -> nx_size {
79 if (font as i64) == 0 { return 0 }
80 return str_len * font.glyph_width
81}
82
83// ===== nx_text_render_int_q14 ======================================
84//
85// Render an integer (display as decimal). Useful for showing
86// frame count, score, position. Buffer must be at least 12 chars.
87
88func nx_text_render_int(fb: *NxFb1bpp, font: *NxFont,
89 x: nx_size, y: nx_size, value: nx_int) -> nx_size {
90 let buf: *u8 = (sys_mmap(16)) as *u8
91 var v: nx_int = value
92 var negative: nx_int = 0
93 if v < 0 {
94 negative = 1
95 v = 0 - v
96 }
97 // Write digits backward
98 var pos: nx_size = 15
99 if v == 0 {
100 buf[pos] = 48 as u8 // '0'
101 pos = pos - 1
102 } else {
103 while v > 0 {
104 let d: nx_int = v - (v / 10) * 10
105 buf[pos] = (48 + d) as u8
106 pos = pos - 1
107 v = v / 10
108 }
109 }
110 if negative == 1 {
111 buf[pos] = 45 as u8 // '-'
112 pos = pos - 1
113 }
114 // start at pos+1
115 let start: nx_size = pos + 1
116 let len: nx_size = 16 - start
117 let str_start: *u8 = (buf as i64 + start as i64) as *u8
118 return nx_text_render_string(fb, font, x, y, str_start, len)
119}