summaryrefslogtreecommitdiffstatsci
diff options
context:
space:
mode:
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>2026-09-18 11:38:05 +0200
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>2026-10-01 13:29:27 +0200
commitfa2d1104677f27baea503882317e177d7fbfe6ab (patch)
tree8c8937264ecd1a445294153f2e21efce5d5656e0
parent9012958b4b9731e2859bf6f6938700dd4745b70b (diff)
replace font names char* by 64bit hash (wip)
-rw-r--r--CMakeLists.txt12
-rw-r--r--src/vkvg_context.c6
-rw-r--r--src/vkvg_context_internal.c5
-rw-r--r--src/vkvg_context_internal.h4
-rw-r--r--src/vkvg_fonts.c4
-rw-r--r--src/vkvg_fonts.h71
-rw-r--r--src/vkvg_internal.h23
7 files changed, 108 insertions, 17 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0e47c09..10cac4f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -132,7 +132,7 @@ endif()
FIND_PACKAGE(Doxygen QUIET)
CMAKE_DEPENDENT_OPTION(VKVG_USE_FREETYPE "use freetype to load and render font glyphs" ON "Freetype_FOUND" OFF)
-CMAKE_DEPENDENT_OPTION(VKVG_USE_FONTCONFIG "use FontConfig to resolve font names" ON "Fontconfig_FOUND" OFF)
+CMAKE_DEPENDENT_OPTION(VKVG_USE_FONTCONFIG "use FontConfig to resolve font names" OFF "Fontconfig_FOUND" OFF)
CMAKE_DEPENDENT_OPTION(VKVG_USE_HARFBUZZ "use Harbuzz for text layouting" ON "HarfBuzz_FOUND" OFF)
CMAKE_DEPENDENT_OPTION(VKVG_BUILD_TESTS "build tests with glfw" ON "GLFW3_FOUND" OFF)
CMAKE_DEPENDENT_OPTION(VKVG_BUILD_DOCS "build documentation" OFF "DOXYGEN_FOUND" OFF)
@@ -263,16 +263,16 @@ SOURCE_GROUP(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${VKVG_SRC})
ADD_LIBRARY("${PROJECT_NAME}" ${VKVG_SRC} ${VKVG_HEADERS})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
- OUTPUT_NAME ${PROJECT_NAME}
- VERSION ${vkvg_VERSION_MAJOR}.${vkvg_VERSION_MINOR}
- SOVERSION ${vkvg_VERSION_MAJOR}
- C_STANDARD 11
+ OUTPUT_NAME ${PROJECT_NAME}
+ VERSION ${vkvg_VERSION_MAJOR}.${vkvg_VERSION_MINOR}
+ SOVERSION ${vkvg_VERSION_MAJOR}
+ C_STANDARD 11
C_STANDARD_REQUIRED ON
C_EXTENSIONS ON
POSITION_INDEPENDENT_CODE "${BUILD_SHARED_LIBS}"
- PUBLIC_HEADER "${VKVG_PUBLIC_HEADERS}"
+ PUBLIC_HEADER "${VKVG_PUBLIC_HEADERS}"
C_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
diff --git a/src/vkvg_context.c b/src/vkvg_context.c
index c6fd714..aae1cef 100644
--- a/src/vkvg_context.c
+++ b/src/vkvg_context.c
@@ -56,7 +56,7 @@ void _init_ctx(VkvgContext ctx) {
ctx->selectedCharSize = 10 << 6;
ctx->currentFont = NULL;
- ctx->selectedFontName[0] = 0;
+ ctx->selectedFontName = 0;
ctx->pattern = NULL;
ctx->curColor = 0xff000000; // opaque black
ctx->cmdStarted = false;
@@ -1359,7 +1359,7 @@ void vkvg_save(VkvgContext ctx) {
sav->curFillRule = ctx->curFillRule;
sav->selectedCharSize = ctx->selectedCharSize;
- strcpy(sav->selectedFontName, ctx->selectedFontName);
+ sav->selectedFontName =ctx->selectedFontName;
sav->currentFont = ctx->currentFont;
sav->textDirection = ctx->textDirection;
@@ -1496,7 +1496,7 @@ void vkvg_restore(VkvgContext ctx) {
ctx->curFillRule = sav->curFillRule;
ctx->selectedCharSize = sav->selectedCharSize;
- strcpy(ctx->selectedFontName, sav->selectedFontName);
+ ctx->selectedFontName = sav->selectedFontName;
ctx->currentFont = sav->currentFont;
ctx->textDirection = sav->textDirection;
diff --git a/src/vkvg_context_internal.c b/src/vkvg_context_internal.c
index e89f332..5373035 100644
--- a/src/vkvg_context_internal.c
+++ b/src/vkvg_context_internal.c
@@ -1955,9 +1955,10 @@ void _draw_full_screen_quad(VkvgContext ctx, vec4* scissor) {
}
void _select_font_face(VkvgContext ctx, const char* name) {
- if (strcmp(ctx->selectedFontName, name) == 0)
+ uint64_t h = fnv1a_64_str(name);
+ if (ctx->selectedFontName == h)
return;
- strcpy(ctx->selectedFontName, name);
+ ctx->selectedFontName = h;
ctx->currentFont = NULL;
ctx->currentFontSize = NULL;
}
diff --git a/src/vkvg_context_internal.h b/src/vkvg_context_internal.h
index 7858229..327bea5 100644
--- a/src/vkvg_context_internal.h
+++ b/src/vkvg_context_internal.h
@@ -114,7 +114,7 @@ typedef struct _vkvg_context_save_t {
vkvg_fill_rule_t curFillRule;
long selectedCharSize; /* Font size*/
- char selectedFontName[FONT_NAME_MAX_SIZE];
+ uint64_t selectedFontName;
_vkvg_font_identity_t selectedFont; // hold current face and size before cache addition
_vkvg_font_identity_t* currentFont; // font ready for lookup
vkvg_direction_t textDirection;
@@ -211,7 +211,7 @@ typedef struct _vkvg_context_t {
vkvg_fill_rule_t curFillRule;
long selectedCharSize; /* Font size*/
- char selectedFontName[FONT_NAME_MAX_SIZE];
+ uint64_t selectedFontName;
//_vkvg_font_t selectedFont; //hold current face and size before cache addition
_vkvg_font_identity_t* currentFont; // font pointing to cached fonts identity
_vkvg_font_t* currentFontSize; // font structure by size ready for lookup
diff --git a/src/vkvg_fonts.c b/src/vkvg_fonts.c
index 0a57cc7..d9d9042 100644
--- a/src/vkvg_fonts.c
+++ b/src/vkvg_fonts.c
@@ -252,8 +252,6 @@ void _font_cache_destroy(VkvgDevice dev) {
}
free(f->sizes);
free(f->fontFile);
- for (uint32_t j = 0; j < f->namesCount; j++)
- free(f->names[j]);
if (f->namesCount > 0)
free(f->names);
free(f->fontBuffer);
@@ -470,7 +468,7 @@ bool _tryFindFontByName(VkvgContext ctx, _vkvg_font_identity_t** font) {
_font_cache_t* cache = ctx->dev->fontCache;
for (int i = 0; i < cache->fontsCount; ++i) {
for (uint32_t j = 0; j < cache->fonts[i].namesCount; j++) {
- if (strcmp(cache->fonts[i].names[j], ctx->selectedFontName) == 0) {
+ if (cache->fonts[i].names[j] == ctx->selectedFontName) {
*font = &cache->fonts[i];
return true;
}
diff --git a/src/vkvg_fonts.h b/src/vkvg_fonts.h
index db9c546..4138ae1 100644
--- a/src/vkvg_fonts.h
+++ b/src/vkvg_fonts.h
@@ -63,6 +63,75 @@
#define FONT_FILE_NAME_MAX_SIZE 1024
#define FONT_NAME_MAX_SIZE 128
+// Bjoern Hoehrmann's UTF-8 State Magic Constants
+#define UTF8_ACCEPT 0
+#define UTF8_REJECT 1
+
+static const uint8_t utf8d[] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
+ 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
+ 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
+ 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s7
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,2,1,1,1,1,1,1,1,1,1,1, // s8..s9
+ 1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // sA..sB
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1, // sC..sD
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,4,1,1,1,1,1,1,1,1, // sE..sF
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // sG..sH
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1, // sI..sJ
+};
+
+/**
+ * @brief Decodes a single byte stream step into a Unicode code point.
+ * @param state Pointer to the persistent decoder state tracker (initialize to UTF8_ACCEPT).
+ * @param codep Pointer to the resulting uint32_t Unicode scalar value.
+ * @param byte The raw next byte from the UTF-8 stream.
+ * @return uint32_t Returns UTF8_ACCEPT when a character is fully decoded.
+ */
+inline uint32_t decode_utf8_byte(uint32_t *const restrict state, uint32_t *const restrict codep, uint8_t byte) {
+ uint32_t type = utf8d[byte];
+
+ *codep = (*state != UTF8_ACCEPT) ?
+ (byte & 0x3fu) | (*codep << 6) :
+ (0xffu >> type) & (byte);
+
+ *state = utf8d[256 + *state * 16 + type];
+ return *state;
+}
+
+// A perfectly optimized, branchless UTF-8 length lookup table
+static const uint8_t utf8_len_table[256] = {
+ // 0x00 to 0x7F: Standard ASCII (1 byte)
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+
+ // 0x80 to 0xBF: Continuation bytes (invalid as first byte, but default to 1 to prevent infinite loops)
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+
+ // 0xC0 to 0xDF: Multi-byte leaders (2 bytes)
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+
+ // 0xE0 to 0xEF: Multi-byte leaders (3 bytes)
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+
+ // 0xF0 to 0xF7: Multi-byte leaders (4 bytes)
+ 4,4,4,4,4,4,4,4,
+
+ // 0xF8 to 0xFF: Invalid UTF-8 (safely fallback to 1)
+ 1,1,1,1,1,1,1,1
+};
+
+inline int get_utf8_char_length(uint8_t first_byte) {
+ return utf8_len_table[first_byte];
+}
// texture coordinates of one character in font cache array texture.
typedef struct {
vec4 bounds; /* normalized float bounds of character bitmap in font cache texture. */
@@ -108,7 +177,7 @@ typedef struct {
/* Font identification structure */
typedef struct {
- char** names; /* Resolved Input names to this font by fontConfig or custom name set by @ref vkvg_load_from_path*/
+ uint64_t names[5]; /* Resolved Input names to this font by fontConfig or custom name set by @ref vkvg_load_from_path. Maximum count is 5.*/
uint32_t namesCount; /* Count of resolved names by fontConfig */
unsigned char* fontBuffer; /* stb_truetype in memory buffer */
long fontBufSize; /* */
diff --git a/src/vkvg_internal.h b/src/vkvg_internal.h
index 41761d0..876e2d8 100644
--- a/src/vkvg_internal.h
+++ b/src/vkvg_internal.h
@@ -53,6 +53,29 @@
#define M_2_PIF 0.63661977236758134308f // 2/pi
#endif
+/**
+ * @brief Computes the 64-bit FNV-1a hash of a ascii string lowering case.
+ *
+ * @param str Pointer to the start of the null terminated ascii string to hash.
+ * @return uint64_t The 64-bit hash value.
+ */
+inline uint64_t fnv1a_64_str(const char *const restrict str) {
+ uint64_t hash = 0xcbf29ce484222325ULL;
+ const uint8_t *restrict data = (const uint8_t *)str;
+
+ while (*data) {
+ uint8_t c = *data;
+ // Fast branchless lowercase for ASCII font names/paths
+ if (c >= 'A' && c <= 'Z') {
+ c |= 0x20;
+ }
+ hash ^= c;
+ hash *= 0x00000100000001B3ULL;
+ data++;
+ }
+
+ return hash;
+}
/*#ifndef M_2_PI
#define M_2_PI 0.63661977236758134308 // 2/pi
#endif*/