From c176078df2155fd11022a45fb969213269e49f4c Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sun, 22 Jun 2025 18:29:05 -0500 Subject: [PATCH] Custom font rendering. --- src/dusk/ui/font.h | 12 +--- src/dusk/ui/uitextbox.c | 10 +-- src/dusk/ui/uitextbox.h | 17 +++-- src/duskraylib/display/draw/drawui.c | 93 +++++++++++++++++++--------- src/duskraylib/display/draw/drawui.h | 34 +++++++++- src/duskraylib/display/render.c | 2 + src/duskraylib/display/render.h | 3 +- tools/fontcompile/fontcompile.py | 15 ++++- 8 files changed, 128 insertions(+), 58 deletions(-) diff --git a/src/dusk/ui/font.h b/src/dusk/ui/font.h index f7b7d1e..418f1ed 100644 --- a/src/dusk/ui/font.h +++ b/src/dusk/ui/font.h @@ -6,14 +6,4 @@ */ #pragma once -#include "ui/fontdata.h" - -#define FONT_HEIGHT 8 -#define FONT_WIDTH FONT_HEIGHT -#define FONT_LINE_SPACING 1 - -#define FONT_LINE_HEIGHT ( \ - FONT_HEIGHT + FONT_LINE_SPACING \ -) - -// \ No newline at end of file +#include "ui/fontdata.h" \ No newline at end of file diff --git a/src/dusk/ui/uitextbox.c b/src/dusk/ui/uitextbox.c index 89829c1..9900d64 100644 --- a/src/dusk/ui/uitextbox.c +++ b/src/dusk/ui/uitextbox.c @@ -20,21 +20,17 @@ void uiTextboxUpdate() { if(UI_TEXTBOX.visible == false) return; if(UI_TEXTBOX.charsRevealed < UI_TEXTBOX.pageChars[UI_TEXTBOX.page]) { - if((RENDER_FRAME % 4) == 0) { - UI_TEXTBOX.charsRevealed++; - } else if(inputIsDown(INPUT_BIND_ACTION)) { + UI_TEXTBOX.charsRevealed++; + if(inputIsDown(INPUT_BIND_ACTION)) { UI_TEXTBOX.charsRevealed++; } } else { if(inputPressed(INPUT_BIND_ACTION)) { if(UI_TEXTBOX.page < UI_TEXTBOX.pageCount - 1) { - uitextbox_t *textbox = &UI_TEXTBOX; - printf("next page?\n"); UI_TEXTBOX.page++; UI_TEXTBOX.charsRevealed = 0; } else { - printf("Closing textbox\n"); // Close the textbox UI_TEXTBOX.visible = false; UI_TEXTBOX.page = 0; @@ -45,8 +41,6 @@ void uiTextboxUpdate() { } void uiTextboxSetText(const char_t *text) { - uitextbox_t *textbox = &UI_TEXTBOX; - assertNotNull(text, "Text pointer cannot be NULL, call uiTextboxClose()"); memoryZero(UI_TEXTBOX.text, sizeof(UI_TEXTBOX.text)); diff --git a/src/dusk/ui/uitextbox.h b/src/dusk/ui/uitextbox.h index 1a2563d..40ad790 100644 --- a/src/dusk/ui/uitextbox.h +++ b/src/dusk/ui/uitextbox.h @@ -12,13 +12,13 @@ #define UI_TEXTBOX_LINES_PER_PAGE 4 #define UI_TEXTBOX_WIDTH RENDER_WIDTH #define UI_TEXTBOX_HEIGHT_INNER ( \ - FONT_LINE_HEIGHT * UI_TEXTBOX_LINES_PER_PAGE \ + FONT_TILE_HEIGHT * UI_TEXTBOX_LINES_PER_PAGE \ ) #define UI_TEXTBOX_BORDER_WIDTH 4 -#define UI_TEXTBOX_BORDER_HEIGHT 4 -#define UI_TEXTBOX_PADDING_X 4 -#define UI_TEXTBOX_PADDING_Y 4 +#define UI_TEXTBOX_BORDER_HEIGHT UI_TEXTBOX_BORDER_WIDTH +#define UI_TEXTBOX_PADDING_X 2 +#define UI_TEXTBOX_PADDING_Y UI_TEXTBOX_PADDING_X #define UI_TEXTBOX_WIDTH_INNER ( \ UI_TEXTBOX_WIDTH - (UI_TEXTBOX_BORDER_WIDTH * 2) - \ (UI_TEXTBOX_PADDING_X * 2) \ @@ -28,7 +28,7 @@ (UI_TEXTBOX_PADDING_Y * 2) \ ) -#define UI_TEXTBOX_CHARS_PER_LINE (UI_TEXTBOX_WIDTH_INNER / FONT_WIDTH) +#define UI_TEXTBOX_CHARS_PER_LINE (UI_TEXTBOX_WIDTH_INNER / FONT_TILE_WIDTH) #define UI_TEXTBOX_CHARS_PER_PAGE ( \ UI_TEXTBOX_CHARS_PER_LINE * UI_TEXTBOX_LINES_PER_PAGE \ ) @@ -40,6 +40,8 @@ UI_TEXTBOX_CHARS_PER_PAGE * UI_TEXTBOX_PAGE_COUNT_MAX \ ) +#define UI_TEXTBOX_REVEAL_RATE 2 + typedef struct { char_t text[UI_TEXTBOX_CHARS_MAX]; uint8_t lineLengths[UI_TEXTBOX_LINE_COUNT]; @@ -65,6 +67,11 @@ void uiTextboxInit(void); */ void uiTextboxUpdate(void); +/** + * Sets the text for the UI textbox. + * + * @param text The text to display in the textbox. + */ void uiTextboxSetText( const char_t *text ); \ No newline at end of file diff --git a/src/duskraylib/display/draw/drawui.c b/src/duskraylib/display/draw/drawui.c index 5acbb3f..9c4f06f 100644 --- a/src/duskraylib/display/draw/drawui.c +++ b/src/duskraylib/display/draw/drawui.c @@ -9,6 +9,7 @@ #include "ui/uitextbox.h" #include "util/memory.h" #include "assert/assert.h" +#include "display/render.h" void drawUIInit(void) { } @@ -20,22 +21,21 @@ void drawUI() { void drawUITextbox() { if(!UI_TEXTBOX.visible) return; - uitextbox_t *textbox = &UI_TEXTBOX; - // Semi-transparent dark blue Color background = (Color){ 0, 0, 128, 128 }; + + uint32_t x = 0; + uint32_t y = RENDER_HEIGHT - UI_TEXTBOX_HEIGHT; + DrawRectangle( - 0, RENDER_HEIGHT - UI_TEXTBOX_HEIGHT, + x, y, UI_TEXTBOX_WIDTH, UI_TEXTBOX_HEIGHT, background ); - + BeginBlendMode(BLEND_ALPHA); - if(UI_TEXTBOX.charsRevealed > 0) { - char_t buffer[UI_TEXTBOX_CHARS_PER_LINE + 1];// +1 for null term uint8_t charsRendered = 0; - uitextbox_t *textbox = &UI_TEXTBOX; // For each line for(uint8_t i = 0; i < UI_TEXTBOX_LINES_PER_PAGE; i++) { @@ -55,33 +55,66 @@ void drawUITextbox() { // Update how many rendered charsRendered += lineChars; - // Copy string from VN Textbox... - memoryCopy( - buffer, - UI_TEXTBOX.text + (UI_TEXTBOX.page * UI_TEXTBOX_CHARS_PER_PAGE) + - (i * UI_TEXTBOX_CHARS_PER_LINE), - lineChars - ); - // Null term string - buffer[lineChars] = '\0'; // Null-terminate the string - - // Render the text - DrawText( - buffer, - UI_TEXTBOX_PADDING_X + UI_TEXTBOX_BORDER_WIDTH, - ( - (RENDER_HEIGHT - UI_TEXTBOX_HEIGHT) + - UI_TEXTBOX_PADDING_Y + UI_TEXTBOX_BORDER_HEIGHT + - (i * FONT_LINE_HEIGHT) - ), - FONT_HEIGHT, - WHITE - ); + for(uint8_t j = 0; j < lineChars; j++) { + drawUIChar( + UI_TEXTBOX.text[ + (i * UI_TEXTBOX_CHARS_PER_LINE) + j + + (UI_TEXTBOX.page * UI_TEXTBOX_CHARS_PER_PAGE) + ], + x + UI_TEXTBOX_PADDING_X + UI_TEXTBOX_BORDER_WIDTH + ( + j * FONT_TILE_WIDTH + ), + y + UI_TEXTBOX_PADDING_Y + UI_TEXTBOX_BORDER_HEIGHT + ( + i * FONT_TILE_HEIGHT + ), + WHITE + ); + } // Check if we're done rendering text if(UI_TEXTBOX.charsRevealed - charsRendered == 0) break; } } - EndBlendMode(); +} + +void drawUIText( + const uint8_t *text, + const uint16_t length, + const int32_t x, + const int32_t y, + const Color color +) { + assertNotNull(text, "Text to draw cannot be NULL"); + if(length == 0) return; + + for(uint16_t i = 0; i < length; i++) { + drawUIChar(text[i], x + (i * FONT_TILE_WIDTH), y, color); + } +} + +void drawUIChar( + const char_t c, + const int32_t x, + const int32_t y, + const Color color +) { + if(c < ' ' || c > '~') return; // Only print printable ASCII characters + + int32_t charIndex = TILE_INDEXES[c - FONT_CHAR_START]; + int32_t charX = (charIndex % FONT_COLUMN_COUNT) * FONT_TILE_WIDTH; + int32_t charY = (charIndex / FONT_COLUMN_COUNT) * FONT_TILE_HEIGHT; + + // FLip Y coordinate for raylib texture coordinates + DrawTextureRec( + RENDER_FONT_TEXTURE, + (Rectangle) { + (float) charX, + (float) charY, // Flip Y by adding height + (float) FONT_TILE_WIDTH, + (float) FONT_TILE_HEIGHT // Negative height to flip the texture + }, + (Vector2) { x, y }, // Add FONT_HEIGHT to flip Y + color + ); } \ No newline at end of file diff --git a/src/duskraylib/display/draw/drawui.h b/src/duskraylib/display/draw/drawui.h index 1e282cb..e893866 100644 --- a/src/duskraylib/display/draw/drawui.h +++ b/src/duskraylib/display/draw/drawui.h @@ -21,4 +21,36 @@ void drawUI(void); /** * Draws the UI textbox to the screen. */ -void drawUITextbox(void); \ No newline at end of file +void drawUITextbox(void); + +/** + * Draws text to the screen at the specified position with the given color. + * + * @param text The text to draw. + * @param length The length of the text to draw. + * @param x The x-coordinate to draw the text at. + * @param y The y-coordinate to draw the text at. + * @param color The color of the text. + */ +void drawUIText( + const uint8_t *text, + const uint16_t length, + const int32_t x, + const int32_t y, + const Color color +); + +/** + * Draws a single character to the screen at the specified position with the given color. + * + * @param c The character to draw. + * @param x The x-coordinate to draw the character at. + * @param y The y-coordinate to draw the character at. + * @param color The color of the character. + */ +void drawUIChar( + const char_t c, + const int32_t x, + const int32_t y, + const Color color +); \ No newline at end of file diff --git a/src/duskraylib/display/render.c b/src/duskraylib/display/render.c index a706910..07449be 100644 --- a/src/duskraylib/display/render.c +++ b/src/duskraylib/display/render.c @@ -14,6 +14,7 @@ RenderTexture2D RENDER_SCREEN_TEXTURE; Texture2D RENDER_TILEMAP_TEXTURE; Texture2D RENDER_ENTITIES_TEXTURE; +Texture2D RENDER_FONT_TEXTURE; void renderInit(void) { InitWindow( @@ -31,6 +32,7 @@ void renderInit(void) { RENDER_SCREEN_TEXTURE = LoadRenderTexture(RENDER_WIDTH, RENDER_HEIGHT); RENDER_TILEMAP_TEXTURE = LoadTexture("../data/tilemap.png"); RENDER_ENTITIES_TEXTURE = LoadTexture("../data/entities.png"); + RENDER_FONT_TEXTURE = LoadTexture("../data/minogram_6x10.png"); drawOverworldInit(); drawUIInit(); diff --git a/src/duskraylib/display/render.h b/src/duskraylib/display/render.h index 2e2753a..00a2e81 100644 --- a/src/duskraylib/display/render.h +++ b/src/duskraylib/display/render.h @@ -11,4 +11,5 @@ extern RenderTexture2D RENDER_SCREEN_TEXTURE; extern Texture2D RENDER_TILEMAP_TEXTURE; -extern Texture2D RENDER_ENTITIES_TEXTURE; \ No newline at end of file +extern Texture2D RENDER_ENTITIES_TEXTURE; +extern Texture2D RENDER_FONT_TEXTURE; \ No newline at end of file diff --git a/tools/fontcompile/fontcompile.py b/tools/fontcompile/fontcompile.py index 482e3b7..f36e9ab 100644 --- a/tools/fontcompile/fontcompile.py +++ b/tools/fontcompile/fontcompile.py @@ -119,7 +119,17 @@ with open(outputFile, 'w') as f: f.write(f"#define FONT_TILE_HEIGHT {root.attrib['tileheight']}\n") f.write(f"#define FONT_TILE_COUNT {len(outputTileIndexes)}\n") f.write(f"#define FONT_CHAR_START {CHAR_START}\n") - f.write(f"#define FONT_CHAR_END {CHAR_END}\n\n") + f.write(f"#define FONT_CHAR_END {CHAR_END}\n") + f.write(f"#define FONT_CHAR_COUNT {CHARS_TOTAL}\n") + f.write(f"#define FONT_COLUMN_COUNT {img.width // int(root.attrib['tilewidth'])}\n") + f.write(f"#define FONT_ROW_COUNT {img.height // int(root.attrib['tileheight'])}\n\n") + + f.write("static const uint8_t TILE_INDEXES[FONT_CHAR_COUNT] = {\n") + f.write(" ") + for i in range(len(outputTileIndexes)): + tileIndex = outputTileIndexes[i] + f.write(f"{tileIndex}, ") + f.write("\n};\n\n") f.write("static const uint8_t TILE_PIXEL_DATA[FONT_TILE_COUNT][FONT_TILE_WIDTH * FONT_TILE_HEIGHT] = {\n") for i in range(len(outputTileIndexes)): @@ -137,6 +147,7 @@ with open(outputFile, 'w') as f: for x in range(int(root.attrib['tilewidth'])): pixel = tile.getpixel((x, y)) f.write(f"0x{1 if pixel[3] > 0 else 0:02X}, ") + f.write("},\n\n") + - f.write("},\n") f.write("};\n\n") \ No newline at end of file