code wiki / (root) / nx_text_render.nx

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}