diff options
author | Bad Diode <bd@badd10de.dev> | 2021-05-23 17:37:16 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-05-23 17:37:16 +0200 |
commit | 0a3831824a4e93825c5b939f406545eead622fbe (patch) | |
tree | bd5fdcd4619f43c660a2677680f73a7faf50397e | |
parent | 27472e8acf860d981425a751a4e92343ccdf387a (diff) | |
download | uxngba-0a3831824a4e93825c5b939f406545eead622fbe.tar.gz uxngba-0a3831824a4e93825c5b939f406545eead622fbe.zip |
Simplify and speed up the text engine
-rw-r--r-- | src/main.c | 9 | ||||
-rw-r--r-- | src/text.h | 235 | ||||
-rw-r--r-- | src/uxn/devices/ppu.c | 29 |
3 files changed, 35 insertions, 238 deletions
@@ -2,7 +2,6 @@ | |||
2 | 2 | ||
3 | #include "common.h" | 3 | #include "common.h" |
4 | #include "bitmap.h" | 4 | #include "bitmap.h" |
5 | #include "bd-font.c" | ||
6 | #include "filesystem.c" | 5 | #include "filesystem.c" |
7 | 6 | ||
8 | #include "rom.c" | 7 | #include "rom.c" |
@@ -281,13 +280,7 @@ int main(void) { | |||
281 | init_uxn(&u); | 280 | init_uxn(&u); |
282 | 281 | ||
283 | // Initialize text engine. | 282 | // Initialize text engine. |
284 | txt_init_hybrid( | 283 | txt_init(1, ppu.fg); |
285 | TXT_MODE_HYBRID, | ||
286 | (Font){ | ||
287 | .data = bd_font, | ||
288 | .char_height = 8, | ||
289 | .char_width = 8, | ||
290 | }, ppu.fg); | ||
291 | txt_position(0,0); | 284 | txt_position(0,0); |
292 | 285 | ||
293 | // Main loop. | 286 | // Main loop. |
@@ -7,31 +7,7 @@ | |||
7 | #include "common.h" | 7 | #include "common.h" |
8 | #include "bitmap.h" | 8 | #include "bitmap.h" |
9 | #include "posprintf.h" | 9 | #include "posprintf.h" |
10 | 10 | #include "ppu.h" | |
11 | |||
12 | typedef enum { | ||
13 | TXT_MODE_TILED_BG, | ||
14 | TXT_MODE_HYBRID, | ||
15 | TXT_MODE_MODE3, | ||
16 | } TextMode; | ||
17 | |||
18 | typedef struct Font { | ||
19 | // A pointer to an area of memory containing font data. | ||
20 | // TODO: Should we unpack each char everytime or unpack everything into RAM? | ||
21 | // Maybe this should be optional? | ||
22 | u16 *data; | ||
23 | // The char_map stores the index to the character position within the font | ||
24 | // array depending on the ascii number we want to render. This allows | ||
25 | // the usage reduced font sets, for example just uppercase letters and | ||
26 | // numbers. | ||
27 | u8 *char_map; | ||
28 | // Width and height of each font character. Only monospaced fonts are | ||
29 | // currently supported. | ||
30 | u8 char_width; | ||
31 | u8 char_height; | ||
32 | // The color of this font. | ||
33 | Color color; | ||
34 | } Font; | ||
35 | 11 | ||
36 | typedef struct TextEngine { | 12 | typedef struct TextEngine { |
37 | // Currently working on tiled backgrounds only. The X and Y positions | 13 | // Currently working on tiled backgrounds only. The X and Y positions |
@@ -40,85 +16,15 @@ typedef struct TextEngine { | |||
40 | size_t cursor_x; | 16 | size_t cursor_x; |
41 | size_t cursor_y; | 17 | size_t cursor_y; |
42 | // Pointer to the memory being used for writing to the screen. | 18 | // Pointer to the memory being used for writing to the screen. |
43 | u16 *memory; | 19 | u32 *memory; |
44 | // TODO: Support other modes and monospaced fonts should be simple but we | ||
45 | // need to keep track of a couple of other variables, which may not be | ||
46 | // available for all modes. For example, tile modes can't have a font width | ||
47 | // smaller than the tile size, although bigger fonts (For example fonts | ||
48 | // composed of multiple tiles) should still be possible. This may not be | ||
49 | // a good time investment, but can be done in the future if needed. | ||
50 | // The mode controls how the text is rendered and how the memory is managed. | ||
51 | TextMode mode; | ||
52 | // The font used to render the text. | 20 | // The font used to render the text. |
53 | Font font; | 21 | u8 color; |
54 | } TextEngine; | 22 | } TextEngine; |
55 | 23 | ||
56 | static TextEngine text_engine = {0}; | 24 | static TextEngine text_engine = {0}; |
57 | 25 | ||
58 | static u8 default_char_map[256] = {0}; | ||
59 | |||
60 | void | ||
61 | txt_putc_tile(char c) { | ||
62 | if (c == '\0') { | ||
63 | return; | ||
64 | } | ||
65 | if (c == '\n') { | ||
66 | text_engine.cursor_x = 0; | ||
67 | text_engine.cursor_y++; | ||
68 | } else { | ||
69 | text_engine.memory[text_engine.cursor_x + 32 * text_engine.cursor_y] = c; | ||
70 | text_engine.cursor_x++; | ||
71 | if (text_engine.cursor_x >= 30) { | ||
72 | text_engine.cursor_x = 0; | ||
73 | text_engine.cursor_y++; | ||
74 | } | ||
75 | } | ||
76 | if (text_engine.cursor_y >= 20) { | ||
77 | text_engine.cursor_y = 0; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | void | 26 | void |
82 | txt_putc_m3(char c) { | 27 | txt_putc(char c) { |
83 | if (c == '\0') { | ||
84 | return; | ||
85 | } | ||
86 | if (c == '\n') { | ||
87 | text_engine.cursor_x = 0; | ||
88 | text_engine.cursor_y += text_engine.font.char_height; | ||
89 | } else { | ||
90 | u8 idx = text_engine.font.char_map[(int)c] * 2; | ||
91 | u32 *packed_char = text_engine.font.data; | ||
92 | packed_char += idx; | ||
93 | Tile tile = {0}; | ||
94 | unpack_tiles(packed_char, &tile, 1); | ||
95 | int x = text_engine.cursor_x; | ||
96 | int y = text_engine.cursor_y; | ||
97 | for (size_t i = 0; i < text_engine.font.char_height; ++i) { | ||
98 | for (size_t j = 0; j < text_engine.font.char_width; ++j) { | ||
99 | if ((tile.row[i] >> 4 * j) & 0x1) { | ||
100 | // put_pixel_m4(x + j, y + i, 1, backbuffer); | ||
101 | // TODO: Clean this up please. | ||
102 | put_pixel_m3(x + j, | ||
103 | y + i, | ||
104 | text_engine.font.color, | ||
105 | FRAMEBUFFER); | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | text_engine.cursor_x += text_engine.font.char_width; | ||
110 | if (text_engine.cursor_x >= SCREEN_WIDTH) { | ||
111 | text_engine.cursor_x = 0; | ||
112 | text_engine.cursor_y += text_engine.font.char_height; | ||
113 | } | ||
114 | } | ||
115 | if (text_engine.cursor_y >= SCREEN_HEIGHT) { | ||
116 | text_engine.cursor_y = 0; | ||
117 | } | ||
118 | } | ||
119 | |||
120 | void | ||
121 | txt_putc_hybrid(char c) { | ||
122 | if (c == '\0') { | 28 | if (c == '\0') { |
123 | return; | 29 | return; |
124 | } | 30 | } |
@@ -126,16 +32,9 @@ txt_putc_hybrid(char c) { | |||
126 | text_engine.cursor_x = 0; | 32 | text_engine.cursor_x = 0; |
127 | text_engine.cursor_y++; | 33 | text_engine.cursor_y++; |
128 | } else { | 34 | } else { |
129 | u8 idx = text_engine.font.char_map[(int)c] * 2; | ||
130 | u32 *packed_char = text_engine.font.data; | ||
131 | packed_char += idx; | ||
132 | Tile tile = {0}; | ||
133 | unpack_tiles(packed_char, &tile, 1); | ||
134 | int x = text_engine.cursor_x; | 35 | int x = text_engine.cursor_x; |
135 | int y = text_engine.cursor_y; | 36 | int y = text_engine.cursor_y; |
136 | Tile *buf = text_engine.memory; | 37 | putfontchar(text_engine.memory, x, y, c, text_engine.color); |
137 | buf[x + y * 32] = tile; | ||
138 | dirty_tiles[y] |= 1 << x; | ||
139 | text_engine.cursor_x += 1; | 38 | text_engine.cursor_x += 1; |
140 | if (text_engine.cursor_x >= 30) { | 39 | if (text_engine.cursor_x >= 30) { |
141 | text_engine.cursor_x = 0; | 40 | text_engine.cursor_x = 0; |
@@ -147,21 +46,6 @@ txt_putc_hybrid(char c) { | |||
147 | } | 46 | } |
148 | } | 47 | } |
149 | 48 | ||
150 | void | ||
151 | txt_putc(char c) { | ||
152 | switch (text_engine.mode) { | ||
153 | case TXT_MODE_TILED_BG: { | ||
154 | txt_putc_tile(c); | ||
155 | } break; | ||
156 | case TXT_MODE_MODE3: { | ||
157 | txt_putc_m3(c); | ||
158 | } break; | ||
159 | case TXT_MODE_HYBRID: { | ||
160 | txt_putc_hybrid(c); | ||
161 | } break; | ||
162 | } | ||
163 | } | ||
164 | |||
165 | static inline void | 49 | static inline void |
166 | txt_puts(char *msg) { | 50 | txt_puts(char *msg) { |
167 | while (*msg) { | 51 | while (*msg) { |
@@ -170,89 +54,13 @@ txt_puts(char *msg) { | |||
170 | } | 54 | } |
171 | 55 | ||
172 | void | 56 | void |
173 | txt_init_tile(size_t bg, Font font, size_t cb_idx) { | 57 | txt_init(u8 color, u32 *buf) { |
174 | // The screenblock for the tile map should start after the tile memory | ||
175 | // (MEM_VRAM + 0x2000 for 256 characters). Since each screenblock is | ||
176 | // composed of 1024 screenblock entries of u16 (2 bytes), we need an | ||
177 | // screenblock index offset of 8192 / (1024 * 2) = 4 screen blocks. | ||
178 | size_t sb_idx = cb_idx * 8 + 4; | ||
179 | |||
180 | // Set the background parameters for the text layer. | ||
181 | BG_CTRL(bg) = BG_CHARBLOCK(cb_idx) | BG_SCREENBLOCK(sb_idx) | BG_PRIORITY(3); | ||
182 | |||
183 | // Load font data in video memory. Each character is unpacked into a tile of | ||
184 | // 8 32bit values (4bpp), meaning that for the full ASCII set of 256 | ||
185 | // characters, we need 8192 bytes of VRAM (8 * 4 * 256). | ||
186 | text_engine.font = font; | ||
187 | unpack_tiles(font.data, &TILE_MEM[cb_idx], 256); | ||
188 | |||
189 | // Initialize default values. | ||
190 | if (font.color == 0) { | ||
191 | font.color = COLOR_WHITE; | ||
192 | } | ||
193 | |||
194 | // Load palette color. | ||
195 | PAL_BUFFER_BG[1] = font.color; | ||
196 | |||
197 | // Update text_engine variables. | ||
198 | text_engine.memory = SCREENBLOCK_MEM[sb_idx]; | ||
199 | } | ||
200 | |||
201 | void | ||
202 | txt_init_bitmap(TextMode mode, Font font) { | ||
203 | // If font_map is NULL, initialize the standard 0-255 character map. | ||
204 | if (font.char_map == NULL) { | ||
205 | for (size_t i = 0; i < 256; ++i) { | ||
206 | default_char_map[i] = i; | ||
207 | } | ||
208 | font.char_map = &default_char_map; | ||
209 | } | ||
210 | |||
211 | // Initialize default values if set to zero. | ||
212 | if (font.char_width == 0) { | ||
213 | font.char_width = 8; | ||
214 | } | ||
215 | if (font.char_height == 0) { | ||
216 | font.char_height = 8; | ||
217 | } | ||
218 | if (font.color == 0) { | ||
219 | font.color = COLOR_WHITE; | ||
220 | } | ||
221 | |||
222 | // Prepare text engine. | 58 | // Prepare text engine. |
223 | text_engine.font = font; | 59 | text_engine.color = color; |
224 | text_engine.mode = mode; | ||
225 | } | ||
226 | |||
227 | void | ||
228 | txt_init_hybrid(TextMode mode, Font font, u32 *buf) { | ||
229 | // If font_map is NULL, initialize the standard 0-255 character map. | ||
230 | if (font.char_map == NULL) { | ||
231 | for (size_t i = 0; i < 256; ++i) { | ||
232 | default_char_map[i] = i; | ||
233 | } | ||
234 | font.char_map = &default_char_map; | ||
235 | } | ||
236 | |||
237 | // Initialize default values if set to zero. | ||
238 | if (font.char_width == 0) { | ||
239 | font.char_width = 8; | ||
240 | } | ||
241 | if (font.char_height == 0) { | ||
242 | font.char_height = 8; | ||
243 | } | ||
244 | if (font.color == 0) { | ||
245 | font.color = COLOR_WHITE; | ||
246 | } | ||
247 | |||
248 | // Prepare text engine. | ||
249 | text_engine.font = font; | ||
250 | text_engine.mode = mode; | ||
251 | text_engine.memory = buf; | 60 | text_engine.memory = buf; |
252 | } | 61 | } |
253 | 62 | ||
254 | // Print text to the screen with formatting. | 63 | // Print text to the screen with formatting. |
255 | |||
256 | #define txt_printf(msg, ...) \ | 64 | #define txt_printf(msg, ...) \ |
257 | { \ | 65 | { \ |
258 | char buf[256] = {0}; \ | 66 | char buf[256] = {0}; \ |
@@ -261,36 +69,16 @@ txt_init_hybrid(TextMode mode, Font font, u32 *buf) { | |||
261 | } | 69 | } |
262 | 70 | ||
263 | void | 71 | void |
264 | txt_clear_line_tiled(void) { | 72 | txt_clear_line(void) { |
265 | for (size_t i = 0; i < 30; ++i) { | 73 | for (size_t i = 0; i < 30; ++i) { |
266 | text_engine.memory[i + 32 * text_engine.cursor_y] = ' '; | 74 | int x = text_engine.cursor_x; |
75 | int y = text_engine.cursor_y; | ||
76 | putfontchar(text_engine.memory, x, y, ' ', text_engine.color); | ||
267 | } | 77 | } |
268 | text_engine.cursor_x = 0; | 78 | text_engine.cursor_x = 0; |
269 | } | 79 | } |
270 | 80 | ||
271 | void | 81 | void |
272 | txt_clear_line_bitmap(void) { | ||
273 | // DEBUG: should be on the text struct. | ||
274 | Tile *buf = &TILE_MEM[0]; | ||
275 | for (size_t i = 0; i < 30; ++i) { | ||
276 | buf[i + text_engine.cursor_y * 30] = (Tile){0}; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | void | ||
281 | txt_clear_line(void) { | ||
282 | switch (text_engine.mode) { | ||
283 | case TXT_MODE_TILED_BG: { | ||
284 | txt_clear_line_tiled(); | ||
285 | } break; | ||
286 | case TXT_MODE_MODE3: | ||
287 | case TXT_MODE_HYBRID: { | ||
288 | txt_clear_line_bitmap(); | ||
289 | } break; | ||
290 | } | ||
291 | } | ||
292 | |||
293 | void | ||
294 | txt_clear_screen(void) { | 82 | txt_clear_screen(void) { |
295 | for (size_t j = 0; j < 20; ++j) { | 83 | for (size_t j = 0; j < 20; ++j) { |
296 | text_engine.cursor_y = j; | 84 | text_engine.cursor_y = j; |
@@ -307,4 +95,3 @@ txt_position(size_t tile_x, size_t tile_y) { | |||
307 | } | 95 | } |
308 | 96 | ||
309 | #endif // GBAEXP_TILES_H | 97 | #endif // GBAEXP_TILES_H |
310 | |||
diff --git a/src/uxn/devices/ppu.c b/src/uxn/devices/ppu.c index 02267f0..64f3fb4 100644 --- a/src/uxn/devices/ppu.c +++ b/src/uxn/devices/ppu.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include "ppu.h" | 1 | #include "ppu.h" |
2 | #include "bd-font.c" | ||
2 | 3 | ||
3 | /* | 4 | /* |
4 | Copyright (c) 2021 Devine Lu Linvega | 5 | Copyright (c) 2021 Devine Lu Linvega |
@@ -14,11 +15,12 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
14 | WITH REGARD TO THIS SOFTWARE. | 15 | WITH REGARD TO THIS SOFTWARE. |
15 | */ | 16 | */ |
16 | 17 | ||
17 | #define FG_FRONT ((u32*)(MEM_VRAM)) | 18 | #define FG_FRONT ((u32*)(MEM_VRAM)) |
18 | #define BG_FRONT ((u32*)(MEM_VRAM + KB(20))) | 19 | #define BG_FRONT ((u32*)(MEM_VRAM + KB(20))) |
19 | #define FG_BACK ((u32*)(MEM_VRAM + KB(44))) | 20 | #define FG_BACK ((u32*)(MEM_VRAM + KB(44))) |
20 | #define BG_BACK ((u32*)(MEM_VRAM + KB(64))) | 21 | #define BG_BACK ((u32*)(MEM_VRAM + KB(64))) |
21 | #define TILE_MAP ((u32*)(MEM_VRAM + KB(40))) | 22 | #define TILE_MAP ((u32*)(MEM_VRAM + KB(40))) |
23 | #define FONT_DATA ((u32*)(MEM_VRAM + KB(84))) | ||
22 | 24 | ||
23 | static u32 unpack_icon_lut[256] = { | 25 | static u32 unpack_icon_lut[256] = { |
24 | 0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100, | 26 | 0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100, |
@@ -213,6 +215,18 @@ puticn(u32 *layer, u16 x, u16 y, u8 *sprite, u8 color, u8 flipx, u8 flipy) { | |||
213 | 215 | ||
214 | IWRAM_CODE | 216 | IWRAM_CODE |
215 | void | 217 | void |
218 | putfontchar(u32 *layer, u16 tile_x, u16 tile_y, u8 ch, u8 color) { | ||
219 | u32 pos = (tile_x + tile_y * 32) * 8; | ||
220 | u32 *tile_data = &layer[pos]; | ||
221 | u32 *font_data = &FONT_DATA[8 * ch]; | ||
222 | for (size_t i = 0; i < 8; ++i) { | ||
223 | tile_data[i] = font_data[i] * color; | ||
224 | } | ||
225 | dirty_tiles[tile_y] |= 1 << tile_x; | ||
226 | } | ||
227 | |||
228 | IWRAM_CODE | ||
229 | void | ||
216 | putchr(u32 *layer, u16 x, u16 y, u8 *sprite, u8 color, | 230 | putchr(u32 *layer, u16 x, u16 y, u8 *sprite, u8 color, |
217 | u8 flipx, u8 flipy) { | 231 | u8 flipx, u8 flipy) { |
218 | u8 sprline1, sprline2; | 232 | u8 sprline1, sprline2; |
@@ -316,7 +330,7 @@ initppu(Ppu *p, u8 hor, u8 ver, u8 pad) { | |||
316 | PAL_BUFFER_BG[2] = COLOR_RED; | 330 | PAL_BUFFER_BG[2] = COLOR_RED; |
317 | PAL_BUFFER_BG[3] = COLOR_BLUE; | 331 | PAL_BUFFER_BG[3] = COLOR_BLUE; |
318 | 332 | ||
319 | // Initialize memory map. | 333 | // Initialize background memory map. |
320 | u16 *mem_map_fg = SCREENBLOCK_MEM[sb_fg]; | 334 | u16 *mem_map_fg = SCREENBLOCK_MEM[sb_fg]; |
321 | u16 *mem_map_bg = SCREENBLOCK_MEM[sb_bg]; | 335 | u16 *mem_map_bg = SCREENBLOCK_MEM[sb_bg]; |
322 | size_t k = 0; | 336 | size_t k = 0; |
@@ -325,5 +339,8 @@ initppu(Ppu *p, u8 hor, u8 ver, u8 pad) { | |||
325 | mem_map_bg[i] = k + 32 * 4; | 339 | mem_map_bg[i] = k + 32 * 4; |
326 | } | 340 | } |
327 | 341 | ||
342 | // Load font data into VRAM. | ||
343 | unpack_tiles(&bd_font, FONT_DATA, 256); | ||
344 | |||
328 | return 1; | 345 | return 1; |
329 | } | 346 | } |