diff --git a/Makefile b/Makefile index e5d2264..cd627a9 100644 --- a/Makefile +++ b/Makefile @@ -3,13 +3,14 @@ LIBS := `pkg-config --libs sdl3` INC := `pkg-config --cflags sdl3` -I./include SRC := src/engine/engine.c \ src/engine/engine_init.c \ - src/engine/render.c + src/engine/render.c \ + src/engine/text.c OUT := build/main SNAKE_SRC := src/examples/snake.c $(SRC) SNAKE := build/snake build: - $(CC) -o $(OUT) $(SRC) $(INC) $(LIBS) + $(CC) -o $(OUT) src/main.c $(SRC) $(INC) $(LIBS) run: $(OUT) diff --git a/include/engine.h b/include/engine.h index efe2808..ff323cc 100644 --- a/include/engine.h +++ b/include/engine.h @@ -11,6 +11,8 @@ #define DEFAULT_WINDOW_NAME "Dangerzone" #define WINDOW_NAME_LENGTH 128 #define TARGET_FRAME 1.0 / 60.0 +#define FONT_ROWS 7 +#define FONT_COLS 5 typedef struct { char window_name[WINDOW_NAME_LENGTH]; @@ -91,5 +93,19 @@ put_char( char c, uint32_t x, uint32_t y, - uint8_t size + uint32_t size, + uint32_t fg_color, + uint32_t bg_color +); + +void +put_string( + const char *text, + uint32_t x, + uint32_t y, + uint32_t size, + uint32_t spacing, + uint32_t line_spacing, + uint32_t fg_color, + uint32_t bg_color ); \ No newline at end of file diff --git a/src/engine/render.c b/src/engine/render.c index 221bbe6..4711e49 100644 --- a/src/engine/render.c +++ b/src/engine/render.c @@ -1,5 +1,6 @@ #include "engine.h" #include "error.h" +#include // alter a pixel at an imaginary x & y coordinates void diff --git a/src/engine/text.c b/src/engine/text.c new file mode 100644 index 0000000..b17df86 --- /dev/null +++ b/src/engine/text.c @@ -0,0 +1,141 @@ +// rendering characters in the framebuffer + +#include "engine.h" +#include + +/* + * each character has 8 rows + * only the lower 4 bits of each byte are used + */ +static const uint8_t font[128][FONT_ROWS] = { + [' '] = { 0, 0, 0, 0, 0, 0, 0 }, + + ['A'] = { 0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11 }, + ['B'] = { 0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E }, + ['C'] = { 0x0F, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0F }, + ['D'] = { 0x1E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E }, + ['E'] = { 0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F }, + ['F'] = { 0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10 }, + ['G'] = { 0x0F, 0x10, 0x10, 0x17, 0x11, 0x11, 0x0F }, + ['H'] = { 0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11 }, + ['I'] = { 0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1F }, + ['J'] = { 0x01, 0x01, 0x01, 0x01, 0x11, 0x11, 0x0E }, + ['K'] = { 0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11 }, + ['L'] = { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F }, + ['M'] = { 0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11 }, + ['N'] = { 0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x11 }, + ['O'] = { 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E }, + ['P'] = { 0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10 }, + ['Q'] = { 0x0E, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0D }, + ['R'] = { 0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11 }, + ['S'] = { 0x0F, 0x10, 0x10, 0x0E, 0x01, 0x01, 0x1E }, + ['T'] = { 0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }, + ['U'] = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E }, + ['V'] = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04 }, + ['W'] = { 0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A }, + ['X'] = { 0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11 }, + ['Y'] = { 0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04 }, + ['Z'] = { 0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F }, + + ['0'] = { 0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E }, + ['1'] = { 0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E }, + ['2'] = { 0x0E, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1F }, + ['3'] = { 0x1E, 0x01, 0x01, 0x0E, 0x01, 0x01, 0x1E }, + ['4'] = { 0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02 }, + ['5'] = { 0x1F, 0x10, 0x10, 0x1E, 0x01, 0x01, 0x1E }, + ['6'] = { 0x0E, 0x10, 0x10, 0x1E, 0x11, 0x11, 0x0E }, + ['7'] = { 0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08 }, + ['8'] = { 0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E }, + ['9'] = { 0x0E, 0x11, 0x11, 0x0F, 0x01, 0x01, 0x0E }, + + ['.'] = { 0, 0, 0, 0, 0, 0x0C, 0x0C }, + [','] = { 0, 0, 0, 0, 0, 0x0C, 0x08 }, + [':'] = { 0, 0x0C, 0x0C, 0, 0, 0x0C, 0x0C }, + [';'] = { 0, 0x0C, 0x0C, 0, 0, 0x0C, 0x08 }, + ['!'] = { 0x04, 0x04, 0x04, 0x04, 0x04, 0, 0x04 }, + ['?'] = { 0x0E, 0x11, 0x01, 0x02, 0x04, 0, 0x04 }, + ['-'] = { 0, 0, 0, 0x1F, 0, 0, 0 }, + ['_'] = { 0, 0, 0, 0, 0, 0, 0x1F }, + ['+'] = { 0, 0x04, 0x04, 0x1F, 0x04, 0x04, 0 }, + ['='] = { 0, 0x1F, 0, 0, 0x1F, 0, 0 }, + ['/'] = { 0x01, 0x01, 0x02, 0x04, 0x08, 0x10, 0x10 }, + ['\\'] = { 0x10, 0x10, 0x08, 0x04, 0x02, 0x01, 0x01 }, + ['('] = { 0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02 }, + [')'] = { 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08 }, + ['['] = { 0x0E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0E }, + [']'] = { 0x0E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0E }, + ['<'] = { 0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02 }, + ['>'] = { 0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08 }, + ['*'] = { 0, 0x15, 0x0E, 0x1F, 0x0E, 0x15, 0 }, + ['#'] = { 0x0A, 0x0A, 0x1F, 0x0A, 0x1F, 0x0A, 0x0A }, + ['%'] = { 0x19, 0x19, 0x02, 0x04, 0x08, 0x13, 0x13 }, + ['@'] = { 0x0E, 0x11, 0x17, 0x15, 0x17, 0x10, 0x0F } +}; + +/** + * put a char 'c' on 'x' and 'y' in the framebuffer + * with a foreground color of 'color' + */ +void +put_char( + char c, + uint32_t x, + uint32_t y, + uint32_t size, + uint32_t fg_color, + uint32_t bg_color +) { + // convert lowercase letters to uppercase + if (c >= 'a' && c <= 'z') { + c -= 'a' - 'A'; + } + + // avoid indexing outside font[128] + if ((unsigned char)c >= 128) { + c = '?'; + } + + for (uint32_t row = 0; row < FONT_ROWS; row++) { + uint8_t bits = font[(unsigned char)c][row]; + + for (uint32_t col = 0; col < FONT_COLS; col++) { + uint32_t color = + bits & (1u << (FONT_COLS - 1 - col)) + ? fg_color : bg_color; + + put_rect( + x + col * size, + y + row * size, + size, + size, + color + ); + } + } +} + +void +put_string( + const char *text, + uint32_t x, + uint32_t y, + uint32_t size, + uint32_t spacing, + uint32_t line_spacing, + uint32_t fg_color, + uint32_t bg_color +) { + uint32_t start_x = x; + + while (*text) { + if (*text == '\n') { + x = start_x; + y += (FONT_ROWS + line_spacing) * size; + } else { + put_char(*text, x, y, size, fg_color, bg_color); + x += (FONT_COLS + spacing) * size; + } + + text++; + } +} diff --git a/src/examples/snake.c b/src/examples/snake.c index 46f6c0f..3b88146 100644 --- a/src/examples/snake.c +++ b/src/examples/snake.c @@ -88,25 +88,6 @@ client_poll_event() } } -/** - * draws a rectangle with its left-top corner at (x,y) with - * a width of 'w' and height of 'h' filled with color 'color' - */ -void -put_rect( - unsigned int x, - unsigned int y, - unsigned int w, - unsigned int h, - uint32_t color -) { - for (int i = x; i < x + w; i++) { - for (int j = y; j < y + h; j++) { - put_pixel(i, j, color); - } - } -} - // a wrapper function for put_rect using a v2 struct void put_rect_v2( diff --git a/src/main.c b/src/main.c index 335912c..719025e 100644 --- a/src/main.c +++ b/src/main.c @@ -18,7 +18,7 @@ client_poll_event() default: break; } - } + } } /** @@ -31,6 +31,11 @@ client_update_framebuffer() { // single green demo pixel game_state->framebuffer[0] = 0x00FF00; + + put_rect(0, 0, 1100, 200, 0xFFFF00); + put_string("Hello, traveller. You have come\nto the right place!", + 20, 20, 2, 2, 3, 0x000000, 0xFFFF00 + ); } int