Skip to content

Commit

Permalink
refactor!: clean up font caching system
Browse files Browse the repository at this point in the history
Reworked font caching system to load all data for a set font size at once. `font->letters` is now a 2D array, with the first value being the size, and the second value being a `char` value. `xpd_font_cache` is no longer called twice for every character drawn.

BREAKING CHANGE: While this shouldn't affect users, the internal functions `xpd_font_get_metrics` and `xpd_font_get_texture` have been removed. Various types were also changed, with `xpd_font_cache_t` being removed altogether.
  • Loading branch information
slimit75 committed Apr 9, 2024
1 parent 95cdb35 commit 19f2742
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 84 deletions.
90 changes: 25 additions & 65 deletions src/fonts.c
Original file line number Diff line number Diff line change
@@ -1,122 +1,82 @@
#include "xpdraw/fonts.h"

#include <assert.h>
#include <stdbool.h>
#include <XPLMUtilities.h>

bool fonts_init = false;
char str[255];
int fonts_init = 0;
FT_Library ft;

void xpd_font_load(xpd_font_face_t *font, const char *path) {
if (fonts_init == false) {
if (fonts_init == 0) {
FT_Init_FreeType(&ft);
fonts_init = true;
fonts_init = 1;
}

FT_New_Face(ft, path, 0, &font->ftFace);
font->letters_idx = -1;
}

void xpd_font_cache(xpd_font_face_t *font, int size, char letter) {
// TODO: Does this need to be called as often as it currently is?
bool not_loaded = true;

void xpd_font_cache(xpd_font_face_t *font, int size) {
// Throw a fatal error if the font isn't properly loaded
if (font->ftFace == NULL) {
assert("font->ftFace == NULL");
}

for (int i = 0; (i < XPD_CHAR_MAX) && (i <= font->letters_idx); i++) {
if (font->letters[i].size == size && font->letters[i].letter == letter) {
not_loaded = false;
break;
}
}

if (not_loaded == true) {
FT_Set_Pixel_Sizes(font->ftFace, 0, (int)(size * 1.5));
FT_Load_Char(font->ftFace, letter, FT_LOAD_RENDER);

font->letters_idx++;
font->letters[font->letters_idx].size = size;
font->letters[font->letters_idx].letter = letter;
font->letters[font->letters_idx].data.metrics = font->ftFace->glyph->metrics;

xpd_load_buffer(&font->letters[font->letters_idx].data.bitmap, font->ftFace->glyph->bitmap.buffer,
font->ftFace->glyph->bitmap.width, font->ftFace->glyph->bitmap.rows, GL_ALPHA);
}
}

FT_Glyph_Metrics xpd_font_get_metrics(xpd_font_face_t *font, int size, char letter) {
xpd_font_cache(font, size, letter); // Automatically cache char if it isn't already cached

for (int i = 0; i < CHAR_MAX; i++) {
if (font->letters[i].size == size && font->letters[i].letter == letter) {
return font->letters[i].data.metrics;
}
}
if (font->letters[size][7].letter == 0u) {
// Tell FreeType what font size we want
FT_Set_Pixel_Sizes(font->ftFace, 0, (int) (size * 1.5));

sprintf(str, "xpdraw: Unable to load metrics for font family %s at letter %c & size %i!\n",
font->ftFace->family_name, letter, size);
XPLMDebugString(str);
return xpd_metrics_empty;
}
// Load data for each available character
for (int i = CHAR_MIN; i <= CHAR_MAX; i++) {
FT_Load_Char(font->ftFace, i, FT_LOAD_RENDER);

xpd_texture_t xpd_font_get_texture(xpd_font_face_t *font, int size, char letter) {
xpd_font_cache(font, size, letter); // Automatically cache char if it isn't already cached
font->letters[size][i].letter = i;
font->letters[size][i].metrics = font->ftFace->glyph->metrics;

for (int i = 0; i < CHAR_MAX; i++) {
if (font->letters[i].size == size && font->letters[i].letter == letter) {
return font->letters[i].data.bitmap;
xpd_load_buffer(&font->letters[size][i].bitmap, font->ftFace->glyph->bitmap.buffer,
font->ftFace->glyph->bitmap.width, font->ftFace->glyph->bitmap.rows, GL_ALPHA);
}
}

sprintf(str, "xpdraw: Unable to load texture for font family %s at letter %c & size %i!\n",
font->ftFace->family_name, letter, size);
XPLMDebugString(str);
return xpd_texture_empty;
}

int xpd_text_length(xpd_font_face_t *font, const char *text, const int size) {
int width = 0;

// Calculate the length of the string before drawing it
for (int i = 0; i < strlen(text); i++) {
FT_Glyph_Metrics text_metrics = xpd_font_get_metrics(font, size, text[i]);
FT_Glyph_Metrics text_metrics = font->letters[size][text[i]].metrics;
if (i == strlen(text) - 1) {
width += (int)((text_metrics.width + text_metrics.horiBearingX) / 64);
}
else {
width += (int)(text_metrics.horiAdvance / 64);
width += (int) ((text_metrics.width + text_metrics.horiBearingX) / 64);
} else {
width += (int) (text_metrics.horiAdvance / 64);
}
}

return width;
}

void xpd_text_draw(xpd_font_face_t *font, const char *text, int x, int y, int size, xpd_text_align_t align,
xpd_color_t textColor) {
xpd_color_t textColor) {
xpd_font_cache(font, size);
glColor4f(textColor.red, textColor.green, textColor.blue, textColor.alpha);

// Handle text alignment
if (align == xpdAlignCenter) {
x -= xpd_text_length(font, text, size) / 2;
}
else if (align == xpdAlignRight) {
} else if (align == xpdAlignRight) {
x -= xpd_text_length(font, text, size);
}

// Draw each character
for (int i = 0; i < strlen(text); i++) {
FT_Glyph_Metrics text_metrics = xpd_font_get_metrics(font, size, text[i]);
FT_Glyph_Metrics text_metrics = font->letters[size][text[i]].metrics;

// Calculate offset from the passed y value
int y_offset = (text_metrics.horiBearingY / 64) - (text_metrics.height / 64);

// Fetch & draw texture
xpd_texture_t image = xpd_font_get_texture(font, size, text[i]);
xpd_texture_t image = font->letters[size][text[i]].bitmap;
xpd_draw_texture(&image, x + (text_metrics.horiBearingX / 64), y + y_offset, image.width, image.height,
textColor);
textColor);

// Advance to the next character
x += text_metrics.horiAdvance / 64;
Expand Down
22 changes: 3 additions & 19 deletions src/xpdraw/fonts.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,33 +36,17 @@ typedef enum {
} xpd_text_align_t;

typedef struct {
char letter;
FT_Glyph_Metrics metrics;
xpd_texture_t bitmap;
} xpd_font_cache_t;

typedef struct {
int size;
char letter;
xpd_font_cache_t data;
} xpd_font_letter_t;

typedef struct {
FT_Face ftFace;
xpd_font_letter_t letters[XPD_CHAR_MAX];
xpd_font_letter_t letters[255][XPD_CHAR_MAX];
int letters_idx;
} xpd_font_face_t;

static const FT_Glyph_Metrics xpd_metrics_empty = {
0,
0,
0,
0,
0,
0,
0,
0
};

/**
* @brief Load a new font
*
Expand Down Expand Up @@ -93,7 +77,7 @@ int xpd_text_length(xpd_font_face_t *font, const char *text, int size);
* @param color Color of the text; defaults to white
*/
void xpd_text_draw(xpd_font_face_t *font, const char *text, int x, int y, int size, xpd_text_align_t align,
xpd_color_t color);
xpd_color_t color);

#ifdef __cplusplus
}
Expand Down

0 comments on commit 19f2742

Please sign in to comment.