Skip to content

Commit

Permalink
update RFont, add fallbacks, update license year
Browse files Browse the repository at this point in the history
  • Loading branch information
ColleagueRiley committed Jan 2, 2025
1 parent 2f213ff commit 5a6a459
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 51 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
zlib License

Copyright (C) 2021-2023 ColleagueRiley
Copyright (C) 2021-2025 ColleagueRiley

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
Expand Down
155 changes: 116 additions & 39 deletions RFont.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-24 ColleagueRiley ColleagueRiley@gmail.com
* Copyright (c) 2021-25 ColleagueRiley ColleagueRiley@gmail.com
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
Expand Down Expand Up @@ -128,7 +128,7 @@ typedef u32 RFont_texture;
#endif

#ifndef RFONT_MAX_GLYPHS
#define RFONT_MAX_GLYPHS 224
#define RFONT_MAX_GLYPHS 256
#endif

#ifndef RFONT_ATLAS_WIDTH_DEFAULT
Expand All @@ -148,7 +148,7 @@ typedef u32 RFont_texture;
#endif

#ifndef RFONT_INIT_VERTS
#define RFONT_INIT_VERTS 1024 * 600
#define RFONT_INIT_VERTS 20 * RFONT_MAX_GLYPHS
#endif

#ifndef RFONT_TEXTFORMAT_MAX_SIZE
Expand Down Expand Up @@ -179,12 +179,20 @@ typedef struct {
u32 codepoint; /* the character (for checking) */
size_t size; /* the size of the glyph */
i32 x, x2; /* coords of the character on the texture */
RFont_font* font; /* the font that the glyph belongs to */

/* source glyph data */
i32 src;
float w, h, x1, y1, advance;
} RFont_glyph;

/**
* @brief Converts a codepoint to a utf8 string.
* @param codepoint The codepoint to convert to utf8.
* @return The utf8 string.
*/
inline char* RFont_codepoint_to_utf8(u32 codepoint);

/**
* @brief Sets the framebuffer size AND runs the graphics init function.
* @param width The framebuffer width.
Expand Down Expand Up @@ -244,6 +252,9 @@ inline RFont_font* RFont_font_init_data_pro(u8* font_data, b8 auto_free, size_t
*/
inline void RFont_font_free(RFont_font* font);

typedef RFont_glyph (*RFont_glyph_fallback_callback)(RFont_font* font, u32 codepoint, size_t size);
RFont_glyph_fallback_callback RFont_set_glyph_fallback_callback(RFont_glyph_fallback_callback callback);

/**
* @brief Add a character to the font's atlas.
* @param font The font to use.
Expand All @@ -253,6 +264,25 @@ inline void RFont_font_free(RFont_font* font);
*/
inline RFont_glyph RFont_font_add_char(RFont_font* font, char ch, size_t size);

/**
* @brief Add a codepoint to the font's atlas.
* @param font The font to use.
* @param codepoint The codepoint to add to the atlas.
* @param size The size of the character.
* @return The `RFont_glyph` created from the data and added to the atlas.
*/
inline RFont_glyph RFont_font_add_codepoint(RFont_font* font, u32 codepoint, size_t size);

/**
* @brief Add a codepoint to the font's atlas.
* @param font The font to use.
* @param codepoint The codepoint to add to the atlas.
* @param size The size of the character.
* @param fallback If the fallback function should not be called.
* @return The `RFont_glyph` created from the data and added to the atlas.
*/
inline RFont_glyph RFont_font_add_codepointPro(RFont_font* font, u32 codepoint, size_t size, b8 fallback);

#ifndef RFONT_NO_FMT
/**
* @brief Formats a string.
Expand Down Expand Up @@ -481,13 +511,13 @@ struct RFont_font {
RFont_texture atlas; /* atlas texture */
size_t atlasWidth, atlasHeight;
float atlasX; /* the current x position inside the atlas */

float* verts;
float* tcoords;
};

size_t RFont_width = 0, RFont_height = 0;

float* RFont_verts;
float* RFont_tcoords;

RFont_font* font2;

void RFont_update_framebuffer(size_t width, size_t height) {
Expand All @@ -497,15 +527,12 @@ void RFont_update_framebuffer(size_t width, size_t height) {
}

void RFont_init(size_t width, size_t height) {
RFont_update_framebuffer(width, height);

#ifndef RFONT_NO_GRAPHICS
/* init any rendering stuff that needs to be initalized (eg. vbo objects) */
RFont_render_init();
#endif
RFont_update_framebuffer(width, height);

RFont_verts = (float*)RFONT_MALLOC(sizeof(float) * RFONT_INIT_VERTS);
RFont_tcoords = (float*)RFONT_MALLOC(sizeof(float) * RFONT_INIT_VERTS);
#ifndef RFONT_NO_GRAPHICS
/* init any rendering stuff that needs to be initalized (eg. vbo objects) */
RFont_render_init();
#endif
}

#ifndef RFONT_NO_STDIO
Expand Down Expand Up @@ -537,6 +564,9 @@ RFont_font* RFont_font_init_data(u8* font_data, b8 auto_free) {
RFont_font* RFont_font_init_data_pro(u8* font_data, b8 auto_free, size_t atlasWidth, size_t atlasHeight) {
RFont_font* font = (RFont_font*)RFONT_MALLOC(sizeof(RFont_font));

font->verts = (float*)RFONT_MALLOC(sizeof(float) * RFONT_INIT_VERTS);
font->tcoords = (float*)RFONT_MALLOC(sizeof(float) * RFONT_INIT_VERTS);

font->atlasWidth = atlasWidth;
font->atlasHeight = atlasHeight;

Expand Down Expand Up @@ -567,12 +597,14 @@ void RFont_font_free(RFont_font* font) {
if (font->free_font_memory)
RFONT_FREE(font->info.data);

RFONT_FREE(font->verts);
RFONT_FREE(font->tcoords);

RFONT_FREE (font);
}

void RFont_close(void) {
RFONT_FREE(RFont_verts);
RFONT_FREE(RFont_tcoords);

}


Expand Down Expand Up @@ -628,13 +660,28 @@ void RFont_font_add_string_len(RFont_font* font, const char* string, size_t strL
RFont_font_add_char(font, *str, sizes[i]);
}

RFont_glyph_fallback_callback RFont_glyph_fallback = NULL;
RFont_glyph_fallback_callback RFont_set_glyph_fallback_callback(RFont_glyph_fallback_callback callback) {
RFont_glyph_fallback_callback old = RFont_glyph_fallback;
RFont_glyph_fallback = callback;
return old;
}

RFont_glyph RFont_font_add_char(RFont_font* font, char ch, size_t size) {
static u32 utf8state = 0, codepoint = 0;

if (RFont_decode_utf8(&utf8state, &codepoint, (u8)ch) != RFONT_UTF8_ACCEPT)
return (RFont_glyph){0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if (RFont_decode_utf8(&utf8state, &codepoint, (u8)ch) != RFONT_UTF8_ACCEPT) {
return (RFont_glyph){0, 0, 0, 0, (RFont_font*)0, 0, 0, 0, 0, 0, 0};
}

return RFont_font_add_codepoint(font, codepoint, size);
}

RFont_glyph RFont_font_add_codepoint(RFont_font* font, u32 codepoint, size_t size) {
return RFont_font_add_codepointPro(font, codepoint, size, 1);
}

RFont_glyph RFont_font_add_codepointPro(RFont_font* font, u32 codepoint, size_t size, b8 fallback) {
u32 i;
for (i = 0; i < font->glyph_len; i++)
if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == size)
Expand All @@ -643,25 +690,20 @@ RFont_glyph RFont_font_add_char(RFont_font* font, char ch, size_t size) {
RFont_glyph* glyph = &font->glyphs[i];

glyph->src = stbtt_FindGlyphIndex(&font->info, codepoint);

if (glyph->src == 0 && font2 != NULL && font2->info.data != font->info.data) {
stbtt_fontinfo saveInfo = font->info;

RFont_font* fakeFont = font;
fakeFont->info = font2->info;

RFont_glyph g = RFont_font_add_char(fakeFont, 't', size);

fakeFont->info = saveInfo;

return g;

if (glyph->src == 0 && fallback && RFont_glyph_fallback) {
RFont_glyph fallback = RFont_glyph_fallback(font, codepoint, size);
if (fallback.codepoint != 0 && fallback.size != 0) {
return fallback;
}
}

font->glyph_len++;

i32 x0, y0, x1, y1, w = 0, h = 0;
if (stbtt_GetGlyphBox(&font->info, glyph->src, &x0, &y0, &x1, &y1) == 0)
return (RFont_glyph){0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if (stbtt_GetGlyphBox(&font->info, glyph->src, &x0, &y0, &x1, &y1) == 0) {
return (RFont_glyph){0, 0, 0, 0, (RFont_font*)0, 0, 0, 0, 0, 0, 0};
}

float scale = ((float)size) / font->fheight;

Expand All @@ -676,6 +718,7 @@ RFont_glyph RFont_font_add_char(RFont_font* font, char ch, size_t size) {
glyph->x2 = font->atlasX + glyph->w;
glyph->x1 = floorf(x0 * scale);
glyph->y1 = floor(-y1 * scale);
glyph->font = font;

#ifndef RFONT_NO_GRAPHICS

Expand Down Expand Up @@ -756,9 +799,36 @@ RFont_area RFont_draw_text_spacing(RFont_font* font, const char* text, float x,
return RFont_draw_text_len(font, text, 0, x, y, size, spacing);
}

char* RFont_codepoint_to_utf8(u32 codepoint) {
static char utf8[5];
if (codepoint <= 0x7F) {
utf8[0] = codepoint;
utf8[1] = 0;
} else if (codepoint <= 0x7FF) {
utf8[0] = 0xC0 | (codepoint >> 6);
utf8[1] = 0x80 | (codepoint & 0x3F);
utf8[2] = 0;
} else if (codepoint <= 0xFFFF) {
utf8[0] = 0xE0 | (codepoint >> 12);
utf8[1] = 0x80 | ((codepoint >> 6) & 0x3F);
utf8[2] = 0x80 | (codepoint & 0x3F);
utf8[3] = 0;
} else if (codepoint <= 0x10FFFF) {
utf8[0] = 0xF0 | (codepoint >> 18);
utf8[1] = 0x80 | ((codepoint >> 12) & 0x3F);
utf8[2] = 0x80 | ((codepoint >> 6) & 0x3F);
utf8[3] = 0x80 | (codepoint & 0x3F);
utf8[4] = 0;
} else {
utf8[0] = 0;
}

return utf8;
}

RFont_area RFont_draw_text_len(RFont_font* font, const char* text, size_t len, float x, float y, u32 size, float spacing) {
float* verts = RFont_verts;
float* tcoords = RFont_tcoords;
float* verts = font->verts;
float* tcoords = font->tcoords;

float startX = x;
float startY = y;
Expand All @@ -772,8 +842,9 @@ RFont_area RFont_draw_text_len(RFont_font* font, const char* text, size_t len, f

float scale = (((float)size) / font->fheight);
float space_adv = (scale * font->space_adv) / 2;

y -= (-font->descent * scale);

float descent_offset = (-font->descent * scale);
y -= descent_offset;

for (str = (char*)text; (len == 0 || (size_t)(str - text) < len) && *str; str++) {
if (*str == '\n') {
Expand All @@ -792,6 +863,12 @@ RFont_area RFont_draw_text_len(RFont_font* font, const char* text, size_t len, f
if (glyph.codepoint == 0 && glyph.size == 0)
continue;

if (glyph.font != font) {
RFont_draw_text_len(glyph.font, RFont_codepoint_to_utf8(glyph.codepoint), 4, x, y - size + descent_offset, size, spacing);
x += glyph.advance + spacing;
continue;
}

float realX = x + glyph.x1;
float realY = y + glyph.y1;

Expand Down Expand Up @@ -845,14 +922,14 @@ RFont_area RFont_draw_text_len(RFont_font* font, const char* text, size_t len, f
tcoords[tIndex + 10] = RFONT_GET_TEXPOSX(glyph.x2, font->atlasWidth);
tcoords[tIndex + 11] = RFONT_GET_TEXPOSY(glyph.h, font->atlasHeight);

x += glyph.advance + spacing;
i += 18;
tIndex += 12;

x += glyph.advance + spacing;
}

#ifndef RFONT_NO_GRAPHICS
RFont_render_text(font->atlas, verts, tcoords, i / 3);
if (i)
RFont_render_text(font->atlas, verts, tcoords, i / 3);
#endif

return (RFont_area){(u32)(x - startX), (u32)(y - startY) + (-font->descent * scale)};
Expand Down
42 changes: 31 additions & 11 deletions example/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,33 @@

#include "ext/rglLoad.h"
#endif

#include "RFont.h"


#include <stdbool.h>

RFont_font* english;
RFont_font* japanese;

RFont_glyph glyphFallback(RFont_font* font, u32 codepoint, size_t size) {
RFONT_UNUSED(font); RFONT_UNUSED(size);
RFont_glyph g;

if (font == japanese)
g = RFont_font_add_codepointPro(english, codepoint, size, 0);
else
g = RFont_font_add_codepointPro(japanese, codepoint, size, 0);

if (g.codepoint != 0 && g.size != 0) {
printf("Codepoint found in fallback font\n");
return g;
} else {
printf("Codepoint %s not found in fallback font either\n", RFont_codepoint_to_utf8(codepoint));
}

return (RFont_glyph){0, 0, 0, 0, (RFont_font*)0, 0, 0, 0, 0, 0, 0};
}

int main(int argc, char **argv) {
#if !defined(RFONT_RENDER_LEGACY)
RGFW_setGLVersion(RGFW_GL_CORE, 3, 3);
Expand All @@ -54,10 +75,10 @@ int main(int argc, char **argv) {
glClear(GL_COLOR_BUFFER_BIT);

RFont_init(win->r.w, win->r.h);
RFont_font* font = RFont_font_init("DejaVuSans.ttf");
RFont_font* japanese = RFont_font_init("DroidSansJapanese.ttf");

english = RFont_font_init("DejaVuSans.ttf");
japanese = RFont_font_init("DroidSansJapanese.ttf");

RFont_set_glyph_fallback_callback(glyphFallback);
glViewport(0, 0, win->r.w, win->r.h);

while (RGFW_window_shouldClose(win) == 0) {
Expand All @@ -72,15 +93,14 @@ int main(int argc, char **argv) {
glClear(GL_COLOR_BUFFER_BIT);

RFont_set_color(0.0f, 1.0f, 0, 1.0f);

RFont_draw_text(font, "abcdefghijklmnopqrstuvwxyz\n1234567890@.<>,/?\\|[{]}", 0, 0, 60);
RFont_draw_text_spacing(font, "`~!#$%^&*()_-=+", 0, 120, 60, 1.0f);
RFont_draw_text(english, "abcdefghijklmnopqrstuvwxyz\n1234567890@.<>,/?\\|[{]}", 0, 0, 60);
RFont_draw_text_spacing(english, "`~!#$%^&*()_-=+", 0, 120, 60, 1.0f);
RFont_set_color(1.0f, 0.0f, 0, 1.0f);
RFont_draw_text(font, "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nSomething about a fast lazy dog.", 0, 210, 20);
RFont_draw_text(english, "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nSomething about a fast lazy .", 0, 210, 20);
RFont_set_color(0.0f, 1.0f, 0, 1.0f);
RFont_draw_text(font, "RFont_draw_text(); ⌫§", 0, 240, 60);
RFont_draw_text(english, "RFont_draw_text(); ⌫§", 0, 240, 60);
RFont_set_color(1.0f, 0.0f, 0, 1.0f);
RFont_draw_text(japanese, "テキスト例", 0, 300, 60);
RFont_draw_text(japanese, "テキスト例hola", 0, 300, 60);

#if defined(RFONT_RENDER_RGL)
rglRenderBatch(); // Update and draw internal render batch
Expand All @@ -91,7 +111,7 @@ int main(int argc, char **argv) {
RFont_update_framebuffer(win->r.w, win->r.h);
}

RFont_font_free(font);
RFont_font_free(english);
RFont_font_free(japanese);

#if defined(RFONT_RENDER_RGL) && !defined(RFONT_RENDER_LEGACY)
Expand Down

0 comments on commit 5a6a459

Please sign in to comment.