summaryrefslogtreecommitdiffstatsci
diff options
context:
space:
mode:
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>2026-10-04 17:01:10 +0200
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>2026-10-04 17:13:41 +0200
commit61fc5439991549bc8060a376dbf2c6d39c77bcb8 (patch)
treee01d149d839abf8faee602078d685995a452e1ad
parentcb4dd3f4274aecd243f6de9e5c162a1b2a1b0ed8 (diff)
first test string ok
-rw-r--r--CMakeLists.txt6
-rw-r--r--include/vkvg.h47
-rw-r--r--src/vkvg_context.c63
-rw-r--r--src/vkvg_context_internal.c4
-rw-r--r--src/vkvg_context_internal.h22
-rw-r--r--src/vkvg_device_internal.h2
-rw-r--r--src/vkvg_fonts.c537
-rw-r--r--src/vkvg_fonts.h149
-rw-r--r--src/vkvg_internal.h27
-rw-r--r--tests/font_tests/font_tests.c23
10 files changed, 598 insertions, 282 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ead44f5..32b430d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -135,7 +135,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" OFF "Fontconfig_FOUND" OFF)
+CMAKE_DEPENDENT_OPTION(VKVG_USE_FONTCONFIG "use FontConfig to resolve font names" ON "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)
@@ -217,9 +217,7 @@ IF(GLSLC AND XXD)
FOREACH(shad_spv ${SHADER_OUTPUTS})
GET_FILENAME_COMPONENT(SPV ${shad_spv} NAME)
- ADD_CUSTOM_COMMAND (
- TARGET BuildShadersHeader
- PRE_BUILD
+ ADD_CUSTOM_COMMAND (TARGET BuildShadersHeader PRE_BUILD
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${SHADER_DIR}
COMMAND ${XXD} -i ${SPV} >> ${SHADERS_H}
)
diff --git a/include/vkvg.h b/include/vkvg.h
index 0a52a0d..c55a9ad 100644
--- a/include/vkvg.h
+++ b/include/vkvg.h
@@ -286,20 +286,6 @@ typedef struct _glyph_info_t {
} vkvg_glyph_info_t;
/**
- * @brief Opaque pointer on a vkvg text run.
- *
- * A VkvgText is an intermediate representation of a text to be drawn.
- * It contains the measurements computed for character positioning.
- *
- * This object is used to speed up the rendering of the same text with the same font multiple times
- * by storing typographic computations.
- *
- * Drawing text with @ref vkvg_show_text() implicitly create such intermediate structure
- * that is destroyed imediatly after the function call.
- */
-typedef struct _vkvg_text_run_t* VkvgText;
-
-/**
* @brief The Vkvg drawing Context.
* @ingroup context
*
@@ -333,7 +319,33 @@ typedef struct _vkvg_device_t* VkvgDevice;
* configurable parameters such as the wrap mode, the filtering, etc...
*/
typedef struct _vkvg_pattern_t* VkvgPattern;
+/**
+ * @brief Opaque pointer on a vkvg text run.
+ *
+ * A VkvgText is an intermediate representation of a text to be drawn.
+ * It contains the measurements computed for character positioning.
+ *
+ * This object is used to speed up the rendering of the same text with the same font multiple times
+ * by storing typographic computations.
+ *
+ * Drawing text with @ref vkvg_show_text() implicitly create such intermediate structure
+ * that is destroyed imediatly after the function call.
+ */
+typedef struct _vkvg_text_run_t* VkvgText;
+/**
+ * @brief Opaque pointer on a vkvg font.
+ *
+ * VkvgFont
+ *
+ */
+typedef struct _vkvg_font_t* VkvgFont;
+
+vkvg_public VkvgFont vkvg_font_create (VkvgDevice dev, const char* queryString, float pointSize);
+vkvg_public VkvgFont vkvg_font_reference (VkvgFont font);
+vkvg_public uint32_t vkvg_font_get_reference_count (VkvgFont font);
+vkvg_public vkvg_status_t vkvg_font_status (VkvgFont font);
+vkvg_public void vkvg_font_destroy (VkvgFont font);
#if VKVG_DBG_STATS
/**
* @brief vkvg memory and vulkan statistiques.
@@ -1677,6 +1689,13 @@ vkvg_public void vkvg_load_font_from_memory(VkvgContext ctx, unsigned char* font
*/
vkvg_public void vkvg_set_font_size(VkvgContext ctx, uint32_t size);
/**
+ * @brief set current font for context, see @ref vkvg_font_create to create fonts.
+ *
+ * @param ctx a valid vkvg @ref context
+ * @param font a valid @ref VkvgFont object.
+ */
+vkvg_public void vkvg_set_font (VkvgContext ctx, VkvgFont font);
+/**
* @brief Show a string of text.
*
* Show the provided null-terminated utf8 string of text a the current position using the
diff --git a/src/vkvg_context.c b/src/vkvg_context.c
index 2acf2e7..fb3b86b 100644
--- a/src/vkvg_context.c
+++ b/src/vkvg_context.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2024 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+// Copyright (c) 2018-2026 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
//
// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
@@ -33,10 +33,9 @@ static const vkvg_context_save_t CTX_INIT = {
VKVG_LINE_JOIN_MITER,
VKVG_FILL_RULE_NON_ZERO,
VKVG_HORIZONTAL,
- 10 << 6,
- 0,
{0},
- NULL
+ NULL,
+ NULL,
};
void _init_ctx(VkvgContext ctx) {
@@ -161,8 +160,8 @@ VkvgContext vkvg_create(VkvgSurface surf) {
ctx->cmd = ctx->cmdBuffers[0]; // current recording buffer
- ctx->references = 1;
- ctx->status = VKVG_STATUS_SUCCESS;
+ ctx->status = VKVG_STATUS_SUCCESS;
+ atomic_init (&ctx->references, 1);
LOG(VKVG_LOG_DBG_ARRAYS, "INIT\tctx = %p; pathes:%ju pts:%ju vch:%d vbo:%d ich:%d ibo:%d\n", ctx,
(uint64_t)ctx->sizePathes, (uint64_t)ctx->sizePoints, ctx->sizeVertices, ctx->sizeVBO, ctx->sizeIndices,
@@ -242,8 +241,7 @@ void vkvg_destroy(VkvgContext ctx) {
if (vkvg_status(ctx))
return;
- ctx->references--;
- if (ctx->references > 0)
+ if (atomic_fetch_sub_explicit(&ctx->references, 1, memory_order_acq_rel) != 1)
return;
LOG(VKVG_LOG_INFO, "DESTROY Context: ctx = %p (status:%d); surf = %p\n", ctx, ctx->status, ctx->pSurf);
@@ -315,14 +313,14 @@ float vkvg_get_opacity(VkvgContext ctx) {
}
vkvg_status_t vkvg_status(VkvgContext ctx) { return !ctx ? VKVG_STATUS_NULL_POINTER : ctx->status; }
VkvgContext vkvg_reference(VkvgContext ctx) {
- if (!ctx->status)
- ctx->references++;
+ if (!vkvg_status(ctx))
+ atomic_fetch_add_explicit(&ctx->references, 1, memory_order_relaxed);
return ctx;
}
uint32_t vkvg_get_reference_count(VkvgContext ctx) {
if (vkvg_status(ctx))
return 0;
- return ctx->references;
+ return atomic_load_explicit(&ctx->references, memory_order_relaxed);
}
void vkvg_new_sub_path(VkvgContext ctx) {
if (vkvg_status(ctx))
@@ -1177,6 +1175,14 @@ VkvgPattern vkvg_get_source(VkvgContext ctx) {
return ctx->pattern;
}
+void vkvg_set_font (VkvgContext ctx, VkvgFont font) {
+ if (ctx->currentFont == font)
+ return;
+ if (ctx->currentFont)
+ vkvg_font_destroy(ctx->currentFont);
+ ctx->currentFont = font;
+ vkvg_font_reference (font);
+}
void vkvg_select_font_face(VkvgContext ctx, const char* name) {
if (vkvg_status(ctx))
return;
@@ -1187,20 +1193,20 @@ void vkvg_load_font_from_path(VkvgContext ctx, const char* path, const char* nam
if (vkvg_status(ctx))
return;
RECORD(ctx, VKVG_CMD_SET_FONT_PATH, name);
- _vkvg_font_identity_t* fid = _font_cache_add_font_identity(ctx, path, name);
+ /*vkvg_font_face_t* fid = _font_cache_add_font_identity(ctx, path, name);
if (!_font_cache_load_font_file_in_memory(fid)) {
ctx->status = VKVG_STATUS_FILE_NOT_FOUND;
return;
}
- _select_font_face(ctx, name);
+ _select_font_face(ctx, name);*/
}
void vkvg_load_font_from_memory(VkvgContext ctx, unsigned char* fontBuffer, long fontBufferByteSize, const char* name) {
if (vkvg_status(ctx))
return;
// RECORD(ctx, VKVG_CMD_SET_FONT_PATH, name);
- _vkvg_font_identity_t* fid = _font_cache_add_font_identity(ctx, NULL, name);
+ /*vkvg_font_face_t* fid = _font_cache_add_font_identity(ctx, NULL, name);
fid->fontBuffer = fontBuffer;
- fid->fontBufSize = fontBufferByteSize;
+ fid->fontBufSize = fontBufferByteSize;*/
_select_font_face(ctx, name);
}
@@ -1213,14 +1219,16 @@ void vkvg_set_font_size(VkvgContext ctx, uint32_t size) {
#else
uint32_t newSize = size;
#endif
- if (ctx->selectedCharSize == newSize)
- return;
- ctx->selectedCharSize = newSize;
+ //if (ctx->selectedCharSize == newSize)
+ // return;
+ //ctx->selectedCharSize = newSize;
//ctx->currentFont = NULL;
//ctx->currentFontSize = NULL;
}
-void vkvg_set_text_direction(vkvg_context* ctx, vkvg_direction_t direction) {}
+void vkvg_set_text_direction(vkvg_context* ctx, vkvg_direction_t direction) {
+
+}
void vkvg_show_text(VkvgContext ctx, const char* text) {
if (vkvg_status(ctx))
@@ -1236,14 +1244,14 @@ VkvgText vkvg_text_run_create(VkvgContext ctx, const char* text) {
if (vkvg_status(ctx))
return NULL;
VkvgText tr = (vkvg_text_run_t*)calloc(1, sizeof(vkvg_text_run_t));
- _font_cache_create_text_run(ctx, text, -1, tr);
+ _font_cache_init_text_run(ctx, text, -1, tr);
return tr;
}
VkvgText vkvg_text_run_create_with_length(VkvgContext ctx, const char* text, uint32_t length) {
if (vkvg_status(ctx))
return NULL;
VkvgText tr = (vkvg_text_run_t*)calloc(1, sizeof(vkvg_text_run_t));
- _font_cache_create_text_run(ctx, text, length, tr);
+ _font_cache_init_text_run(ctx, text, length, tr);
return tr;
}
uint32_t vkvg_text_run_get_glyph_count(VkvgText textRun) { return textRun->glyph_count; }
@@ -1259,7 +1267,7 @@ void vkvg_text_run_get_glyph_position(VkvgText textRun, uint32_t index, vkvg
#endif
}
void vkvg_text_run_destroy(VkvgText textRun) {
- _font_cache_destroy_text_run(textRun);
+ _font_cache_term_text_run(textRun);
free(textRun);
}
void vkvg_show_text_run(VkvgContext ctx, VkvgText textRun) {
@@ -1395,6 +1403,8 @@ void vkvg_save(VkvgContext ctx) {
if (ctx->pattern)
vkvg_pattern_reference(ctx->pattern);
+ if (ctx->currentFont)
+ vkvg_font_reference(ctx->currentFont);
ctx->ctxSaveCount++;
}
@@ -1512,6 +1522,15 @@ void vkvg_restore(VkvgContext ctx) {
} else {
_update_cur_pattern(ctx, NULL);
}
+ if (sav->currentFont) {
+ if (sav->currentFont == ctx->currentFont) {
+ vkvg_font_destroy(sav->currentFont);
+ } else {
+ if (ctx->currentFont)
+ vkvg_font_destroy(ctx->currentFont);
+ ctx->currentFont = sav->currentFont;
+ }
+ }
ctx->ctxSaveCount--;
}
diff --git a/src/vkvg_context_internal.c b/src/vkvg_context_internal.c
index 358b39a..effc40b 100644
--- a/src/vkvg_context_internal.c
+++ b/src/vkvg_context_internal.c
@@ -1950,9 +1950,9 @@ void _draw_full_screen_quad(VkvgContext ctx, vec4* scissor) {
void _select_font_face(VkvgContext ctx, const char* name) {
uint64_t h = fnv1a_64_str(name);
- if (ctx->selectedFont == h)
+ /*if (ctx->selectedFont == h)
return;
- ctx->selectedFont = h;
+ ctx->selectedFont = h;*/
/*ctx->currentFont = NULL;
ctx->currentFontSize = NULL;*/
}
diff --git a/src/vkvg_context_internal.h b/src/vkvg_context_internal.h
index 2fb5712..a186b47 100644
--- a/src/vkvg_context_internal.h
+++ b/src/vkvg_context_internal.h
@@ -22,8 +22,8 @@
#ifndef VKVG_CONTEXT_INTERNAL_H
#define VKVG_CONTEXT_INTERNAL_H
-#include "vkvg_internal.h"
#include "vkvg_fonts.h"
+#include "vkvg_internal.h"
#if VKVG_RECORDING
#include "recording/vkvg_record_internal.h"
@@ -112,18 +112,17 @@ typedef struct _vkvg_context_save_t {
vkvg_line_join_t lineJoint;
vkvg_fill_rule_t curFillRule;
vkvg_direction_t textDirection;
- uint32_t selectedCharSize; /* Font size*/
- uint64_t selectedFont;
- //_vkvg_font_identity_t selectedFont; // hold current face and size before cache addition
- //_vkvg_font_identity_t* currentFont; // font ready for lookup
+ //vkvg_font_face_t selectedFont; // hold current face and size before cache addition
+ //vkvg_font_face_t* currentFont; // font ready for lookup
push_constants pushConsts;
VkvgPattern pattern;
+ VkvgFont currentFont;
vkvg_clip_state_t clippingState;
} vkvg_context_save_t;
typedef struct _vkvg_context_t {
- vkvg_status_t status;
- uint32_t references; // reference count
+ vkvg_status_t status;
+ atomic_int references; // reference count
uint32_t curColor;
float lineWidth;
@@ -137,14 +136,13 @@ typedef struct _vkvg_context_t {
vkvg_line_join_t lineJoin;
vkvg_fill_rule_t curFillRule;
vkvg_direction_t textDirection;
- uint32_t selectedCharSize; /* Font size*/
- uint64_t selectedFont;
- // _vkvg_font_identity_t* currentFont; // font pointing to cached fonts identity
+ // vkvg_font_face_t* currentFont; // font pointing to cached fonts identity
/****************************/
- //_vkvg_font_t selectedFont; //hold current face and size before cache addition
- //_vkvg_font_t* currentFontSize; // font structure by size ready for lookup
+ //vkvg_font_t selectedFont; //hold current face and size before cache addition
+ //vkvg_font_t* currentFontSize; // font structure by size ready for lookup
push_constants pushConsts;
VkvgPattern pattern;
+ VkvgFont currentFont;
//--------------------------------------------------------------------------------
uint32_t indCount; // current indice count
diff --git a/src/vkvg_device_internal.h b/src/vkvg_device_internal.h
index 46f755e..5dced18 100644
--- a/src/vkvg_device_internal.h
+++ b/src/vkvg_device_internal.h
@@ -22,8 +22,8 @@
#ifndef VKVG_DEVICE_INTERNAL_H
#define VKVG_DEVICE_INTERNAL_H
-#include "vkvg_internal.h"
#include "vkvg_fonts.h"
+#include "vkvg_internal.h"
#define STENCIL_FILL_BIT 0x1
#define STENCIL_CLIP_BIT 0x2
diff --git a/src/vkvg_fonts.c b/src/vkvg_fonts.c
index 2ddbb93..f000827 100644
--- a/src/vkvg_fonts.c
+++ b/src/vkvg_fonts.c
@@ -51,12 +51,40 @@ void _fonts_cache_create(VkvgDevice dev, const char* fontDirs) {
free(cache);
return;
}
- cache->config = FcConfigGetCurrent();
- if (!cache->config) {
- LOG(VKVG_LOG_DEBUG,
- "Font config initialisation failed, consider using 'FONTCONFIG_PATH' and 'FONTCONFIG_FILE' environmane\
- variables to point to 'fonts.conf' needed for FontConfig startup");
- assert(cache->config);
+ if (fontDirs) {
+ cache->config = FcConfigCreate();
+ if (!cache->config) {
+ LOG(VKVG_LOG_ERR, "Failed to create custom Fontconfig configuration.\n");
+ dev->status = VKVG_STATUS_DEVICE_ERROR;
+ free(cache);
+ return;
+ }
+ const char *start = fontDirs;
+ const char *end;
+ char fontDir[1024];
+
+ while (start && *start != '\0') {
+ end = strchr(start, ',');
+ size_t len = end ? (size_t)(end - start) : strlen(start);
+ strncpy(fontDir, start, len);
+
+ if (!FcConfigAppFontAddDir(cache->config, (FcChar8*)fontDir))
+ LOG(VKVG_LOG_DEBUG, "Failed to add '%s' to Fontconfig configuration.\n", fontDir);
+
+ start = end ? end + 1 : NULL;
+ }
+ FcConfigSetCurrent(cache->config);
+ } else {
+ cache->config = FcConfigGetCurrent();
+ if (!cache->config) {
+ LOG(VKVG_LOG_ERR,
+ "Font config initialisation failed, consider using 'FONTCONFIG_PATH' and 'FONTCONFIG_FILE' environmane\
+ variables to point to 'fonts.conf' needed for FontConfig startup");
+ dev->status = VKVG_STATUS_DEVICE_ERROR;
+ free(cache);
+ return;
+ }
+ cache->fcReqElts = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_INDEX, (char *)0);
}
#endif
@@ -104,6 +132,8 @@ void _fonts_cache_create(VkvgDevice dev, const char* fontDirs) {
cache->hostBuff = (uint8_t*)malloc(buffLength);
cache->pensY = (int*)calloc(cache->texLength, sizeof(int));
+ cache->fontBuffers = array_create_vkvg_font_buffer_t(4);
+
dev->fontCache = cache;
}
/// increase layer count of 2d texture array used as font cache.
@@ -168,7 +198,7 @@ void _increase_font_tex_array(VkvgDevice dev) {
}
// flush font stagging buffer to cache texture array
// Trigger stagging buffer to be uploaded in font cache. Groupping upload improve performances.
-void _flush_chars_to_tex(VkvgDevice dev, _vkvg_font_t* f) {
+void _flush_chars_to_tex(VkvgDevice dev, vkvg_font_t* f) {
_font_cache_t* cache = dev->fontCache;
if (cache->stagingX == 0) // no char in stagging buff to flush
@@ -212,7 +242,7 @@ void _flush_chars_to_tex(VkvgDevice dev, _vkvg_font_t* f) {
memset(cache->hostBuff, 0, (uint64_t)FONT_PAGE_SIZE * FONT_PAGE_SIZE * cache->texPixelSize);
}
/// Start a new line in font cache, increase texture layer count if needed.
-void _init_next_line_in_tex_cache(VkvgDevice dev, _vkvg_font_t* f) {
+void _init_next_line_in_tex_cache(VkvgDevice dev, vkvg_font_t* f) {
_font_cache_t* cache = dev->fontCache;
int i;
for (i = 0; i < cache->texLength; ++i) {
@@ -233,10 +263,30 @@ void _font_cache_destroy(VkvgDevice dev) {
free(cache->hostBuff);
- for (int i = 0; i < cache->fontsCount; ++i) {
- _vkvg_font_identity_t* f = &cache->fonts[i];
+ vkvg_font_buffer_t* buffs = cache->fontBuffers.elements;
+ for (int i = 0; i < cache->fontBuffers.count; ++i) {
+ vkvg_font_face_t* faces = buffs[i].faces.elements;
+ for (int j = 0; j < buffs[i].faces.count; ++j) {
+ VkvgFont *fonts = faces[j].sizes.elements;
+ for (int k = 0; k < faces[j].sizes.count; ++k) {
+ vkvg_font_destroy(fonts[k]);
+ }
+ vkvg_font_face_t* face = &faces[j];
+ array_destroy_VkvgFont(&face->sizes);
+ array_destroy_uint64_t(&face->queryHashes);
+ FT_Done_Face(face->face);
+ mtx_destroy(&face->mutex);
+ }
+ vkvg_font_buffer_t* buff = &buffs[i];
+ free(buff->buffer);
+ array_destroy_vkvg_font_face_t(&buff->faces);
+ }
+ array_destroy_vkvg_font_buffer_t(&cache->fontBuffers);
+
+/* for (int i = 0; i < cache->fontsCount; ++i) {
+ vkvg_font_face_t* f = &cache->fonts[i];
for (uint32_t j = 0; j < f->sizeCount; j++) {
- _vkvg_font_t* s = &f->sizes[j];
+ vkvg_font_t* s = &f->sizes[j];
#ifdef VKVG_USE_FREETYPE
for (int g = 0; g < s->face->num_glyphs; ++g) {
if (s->charLookup[g] != NULL)
@@ -250,9 +300,6 @@ void _font_cache_destroy(VkvgDevice dev) {
}
#endif
-#ifdef VKVG_USE_HARFBUZZ
- hb_font_destroy(s->hb_font);
-#endif
free(s->charLookup);
}
@@ -263,7 +310,7 @@ void _font_cache_destroy(VkvgDevice dev) {
free(f->fontBuffer);
}
- free(cache->fonts);
+ free(cache->fonts);*/
free(cache->pensY);
vkh_buffer_reset(&cache->buff);
@@ -274,6 +321,8 @@ void _font_cache_destroy(VkvgDevice dev) {
FT_Done_FreeType(cache->library);
#endif
#ifdef VKVG_USE_FONTCONFIG
+ FcObjectSetDestroy(cache->fcReqElts);
+ //FcPatternDestroy(cache->fcPattern);
FcConfigDestroy(cache->config);
FcFini();
#endif
@@ -298,17 +347,17 @@ void _font_cache_update_context_descset(VkvgContext ctx) {
UNLOCK_FONTCACHE(ctx->dev)
}
// create a new char entry and put glyph in stagging buffer, ready for upload.
-_char_ref* _prepare_char(VkvgDevice dev, VkvgText tr, uint32_t gindex) {
- _vkvg_font_t* f = tr->font;
+_char_ref _prepare_char(VkvgDevice dev, VkvgText tr, uint32_t gindex) {
+ VkvgFont f = tr->font;
#ifdef VKVG_USE_FREETYPE
#if defined(VKVG_LCD_FONT_FILTER) && defined(FT_CONFIG_OPTION_SUBPIXEL_RENDERING)
FT_CHECK_RESULT(FT_Load_Glyph(f->face, gindex, FT_LOAD_TARGET_NORMAL));
FT_CHECK_RESULT(FT_Render_Glyph(f->face->glyph, FT_RENDER_MODE_LCD));
#else
- FT_CHECK_RESULT(FT_Load_Glyph(f->face, gindex, FT_LOAD_RENDER));
+ FT_CHECK_RESULT(FT_Load_Glyph(f->face->face, gindex, FT_LOAD_RENDER));
#endif
- FT_GlyphSlot slot = f->face->glyph;
+ FT_GlyphSlot slot = f->face->face->glyph;
FT_Bitmap bmp = slot->bitmap;
uint32_t bmpByteWidth = bmp.width;
uint32_t bmpPixelWidth = bmp.width;
@@ -333,7 +382,7 @@ _char_ref* _prepare_char(VkvgDevice dev, VkvgText tr, uint32_t gindex) {
_init_next_line_in_tex_cache(dev, f);
}
- _char_ref* cr = (_char_ref*)malloc(sizeof(_char_ref));
+ _char_ref cr = {slot->glyph_index};
int penX = dev->fontCache->stagingX;
#ifdef VKVG_USE_FREETYPE
@@ -353,30 +402,30 @@ _char_ref* _prepare_char(VkvgDevice dev, VkvgText tr, uint32_t gindex) {
#endif
}
}
- cr->bmpDiff.x = (int16_t)slot->bitmap_left;
- cr->bmpDiff.y = (int16_t)slot->bitmap_top;
- cr->advance = slot->advance;
+ cr.bmpDiff.x = (int16_t)slot->bitmap_left;
+ cr.bmpDiff.y = (int16_t)slot->bitmap_top;
+ cr.advance = slot->advance;
#else
int advance;
int lsb;
stbtt_GetGlyphHMetrics(pStbInfo, gindex, &advance, &lsb);
stbtt_MakeGlyphBitmap(pStbInfo, data + penX, bmpPixelWidth, bmpRows, FONT_PAGE_SIZE, f->scale, f->scale, gindex);
- cr->bmpDiff.x = (int16_t)c_x1;
- cr->bmpDiff.y = (int16_t)-c_y1;
- cr->advance = (vec2){(uint32_t)roundf(f->scale * advance) << 6, 0};
+ cr.bmpDiff.x = (int16_t)c_x1;
+ cr.bmpDiff.y = (int16_t)-c_y1;
+ cr.advance = (vec2){(uint32_t)roundf(f->scale * advance) << 6, 0};
#endif
vec4 uvBounds = {{(float)(penX + f->curLine.penX) / (float)FONT_PAGE_SIZE},
{(float)f->curLine.penY / (float)FONT_PAGE_SIZE},
{(float)bmpPixelWidth},
{(float)bmpRows}};
- cr->bounds = uvBounds;
- cr->pageIdx = f->curLine.pageIdx;
+ cr.bounds = uvBounds;
+ cr.pageIdx = f->curLine.pageIdx;
f->charLookup[gindex] = cr;
dev->fontCache->stagingX += bmpPixelWidth;
return cr;
}
-void _font_add_name(_vkvg_font_identity_t* font, const char* name) {
+void _font_add_name(vkvg_font_face_t* font, const char* name) {
/*if (++font->namesCount == 1)
font->names = (char**)malloc(sizeof(char*));
else
@@ -384,40 +433,11 @@ void _font_add_name(_vkvg_font_identity_t* font, const char* name) {
font->names[font->namesCount - 1] = (char*)calloc(strlen(name) + 1, sizeof(char));
strcpy(font->names[font->namesCount - 1], name);*/
}
-bool _font_cache_load_font_file_in_memory(_vkvg_font_identity_t* fontId) {
- FILE* fontFile = fopen(fontId->fontFile, "rb");
- if (!fontFile)
- return false;
- fseek(fontFile, 0, SEEK_END);
- fontId->fontBufSize = ftell(fontFile); /* how long is the file ? */
- fseek(fontFile, 0, SEEK_SET); /* reset */
- fontId->fontBuffer = malloc(fontId->fontBufSize);
- fread(fontId->fontBuffer, fontId->fontBufSize, 1, fontFile);
- fclose(fontFile);
- return true;
-}
-_vkvg_font_identity_t* _font_cache_add_font_identity(VkvgContext ctx, const char* fontFilePath, const char* name) {
- _font_cache_t* cache = (_font_cache_t*)ctx->dev->fontCache;
- if (++cache->fontsCount == 1)
- cache->fonts = (_vkvg_font_identity_t*)malloc(cache->fontsCount * sizeof(_vkvg_font_identity_t));
- else
- cache->fonts = (_vkvg_font_identity_t*)realloc(cache->fonts, cache->fontsCount * sizeof(_vkvg_font_identity_t));
- _vkvg_font_identity_t nf = {0};
-
- if (fontFilePath) {
- int fflength = strlen(fontFilePath) + 1;
- nf.fontFile = (char*)malloc(fflength * sizeof(char));
- strcpy(nf.fontFile, fontFilePath);
- }
- _font_add_name(&nf, name);
- cache->fonts[cache->fontsCount - 1] = nf;
- return &cache->fonts[cache->fontsCount - 1];
-}
// select current font for context
-_vkvg_font_t* _find_or_create_font_size(VkvgContext ctx) {
- _vkvg_font_identity_t* font;// = ctx->currentFont;
+vkvg_font_t* _find_or_create_font_size(VkvgContext ctx) {
+ /*vkvg_font_face_t* font;// = ctx->currentFont;
for (uint32_t i = 0; i < font->sizeCount; ++i) {
if (font->sizes[i].charSize == ctx->selectedCharSize)
@@ -425,39 +445,12 @@ _vkvg_font_t* _find_or_create_font_size(VkvgContext ctx) {
}
// if not found, create a new font size structure
if (++font->sizeCount == 1)
- font->sizes = (_vkvg_font_t*)malloc(sizeof(_vkvg_font_t));
+ font->sizes = (vkvg_font_t*)malloc(sizeof(vkvg_font_t));
else
- font->sizes = (_vkvg_font_t*)realloc(font->sizes, font->sizeCount * sizeof(_vkvg_font_t));
- _vkvg_font_t newSize = {.charSize = ctx->selectedCharSize};
+ font->sizes = (vkvg_font_t*)realloc(font->sizes, font->sizeCount * sizeof(vkvg_font_t));
+ vkvg_font_t newSize = {.charSize = ctx->selectedCharSize};
VkvgDevice dev = ctx->dev;
-#ifdef VKVG_USE_FREETYPE
- _font_cache_t* cache = (_font_cache_t*)ctx->dev->fontCache;
- FT_CHECK_RESULT(FT_New_Memory_Face(cache->library, font->fontBuffer, font->fontBufSize, 0, &newSize.face));
- FT_CHECK_RESULT(FT_Set_Char_Size(newSize.face, 0, newSize.charSize, dev->hdpi, dev->vdpi));
-
- newSize.charLookup = (_char_ref**)calloc(newSize.face->num_glyphs, sizeof(_char_ref*));
-
- if (FT_IS_SCALABLE(newSize.face))
- newSize.curLine.height = newSize.face->size->metrics.height >> 6;
- else
- newSize.curLine.height = newSize.face->height >> 6;
-#else
- int result = stbtt_InitFont(&font->stbInfo, font->fontBuffer, 0);
- assert(result && "stbtt_initFont failed");
- if (!result) {
- ctx->status = VKVG_STATUS_INVALID_FONT;
- return NULL;
- }
- stbtt_GetFontVMetrics(&font->stbInfo, &font->ascent, &font->descent, &font->lineGap);
- newSize.charLookup = (_char_ref**)calloc(font->stbInfo.numGlyphs, sizeof(_char_ref*));
- // newSize.scale = stbtt_ScaleForPixelHeight(&font->stbInfo, newSize.charSize);
- newSize.scale = stbtt_ScaleForMappingEmToPixels(&font->stbInfo, newSize.charSize);
- newSize.curLine.height = roundf(newSize.scale * (font->ascent - font->descent + font->lineGap));
- newSize.ascent = roundf(newSize.scale * font->ascent);
- newSize.descent = roundf(newSize.scale * font->descent);
- newSize.lineGap = roundf(newSize.scale * font->lineGap);
-#endif
#ifdef VKVG_USE_HARFBUZZ
newSize.hb_font = hb_ft_font_create(newSize.face, NULL);
@@ -466,11 +459,11 @@ _vkvg_font_t* _find_or_create_font_size(VkvgContext ctx) {
_init_next_line_in_tex_cache(dev, &newSize);
font->sizes[font->sizeCount - 1] = newSize;
- return &font->sizes[font->sizeCount - 1];
+ return &font->sizes[font->sizeCount - 1];*/
}
// try find font already resolved with fontconfig by font name
-bool _tryFindFontByName(VkvgContext ctx, _vkvg_font_identity_t** font) {
+bool _tryFindFontByName(VkvgContext ctx, vkvg_font_face_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++) {
@@ -483,83 +476,185 @@ bool _tryFindFontByName(VkvgContext ctx, _vkvg_font_identity_t** font) {
return false;
}
-#ifdef VKVG_USE_FONTCONFIG
-bool _tryResolveFontNameWithFontConfig(VkvgContext ctx, _vkvg_font_identity_t** resolvedFont) {
- _font_cache_t* cache = (_font_cache_t*)ctx->dev->fontCache;
- char* fontFile = NULL;
+/*void _fcpattern_append(FcPattern* dest, const FcPattern* src) {
+ FcPatternIter iter;
+ FcPatternIterStart(src, &iter);
+ while (FcPatternIterIsValid(src, &iter)) {
+ const char* object_name = FcPatternIterGetObject(src, &iter);
+ int value_count = FcPatternIterValueCount(src, &iter);
+ for (int i = 0; i < value_count; i++) {
+ FcValue value;
+ if (FcPatternIterGetValue(src, &iter, i, &value, NULL) == FcResultMatch) {
+ FcPatternAdd(dest, object_name, value, FcTrue);
+ }
+ }
+ FcPatternIterNext(src, &iter);
+ }
+}
+FcPattern* _get_font_config_pattern(VkvgContext ctx, const char* fontQueryStr, ...) {
+ FcPattern* request = NULL;
+ va_list args;
+ va_start(args, fontQueryStr);
+ if (va_arg(args, const char*) == NULL) { //only one font request
+ request = FcNameParse((const FcChar8*)fontQueryStr);
+ } else {
+ va_end(args);
+ va_start(args, fontQueryStr);
- /*FcPattern* pat = FcNameParse((const FcChar8*)ctx->selectedFontName);
- FcConfigSubstitute(cache->config, pat, FcMatchPattern);
- FcDefaultSubstitute(pat);
- FcResult result;
- FcPattern* font = FcFontMatch(cache->config, pat, &result);
- if (font)
- FcPatternGetString(font, FC_FILE, 0, (FcChar8**)&fontFile);
- *resolvedFont = NULL;
- if (fontFile) {
- // try find font in cache by path
- for (int i = 0; i < cache->fontsCount; ++i) {
- if (cache->fonts[i].fontFile && strcmp(cache->fonts[i].fontFile, fontFile) == 0) {
- _font_add_name(&cache->fonts[i], ctx->selectedFontName);
- *resolvedFont = &cache->fonts[i];
- break;
+ request = FcPatternCreate();
+
+ const char* str = fontQueryStr;
+ int i = 0;
+ while (str != NULL) {
+ FcPattern* subPat = FcNameParse((const FcChar8*)str);
+ if (subPat) {
+ _fcpattern_append(request, subPat);
}
+ FcPatternDestroy(subPat);
+ str = va_arg(args, const char*);
}
- if (!*resolvedFont) {
- // if not found, create a new vkvg_font
- _vkvg_font_identity_t* fid = _font_cache_add_font_identity(ctx, fontFile, ctx->selectedFontName);
- _font_cache_load_font_file_in_memory(fid);
- *resolvedFont = &cache->fonts[cache->fontsCount - 1];
+
+
+ }
+ va_end(args);
+ FcConfigSubstitute(ctx->dev->fontCache->config, request, FcMatchPattern);
+ FcDefaultSubstitute(request);
+ return request;
+}
+#define get_font_config_pattern(...) _get_font_config_pattern(__VA_ARGS__, NULL)*/
+bool tryGetFontFaceFromQueryHash(_font_cache_t* cache, uint64_t queryHash, vkvg_font_face_t **const face) {
+ *face = NULL;
+ vkvg_font_buffer_t* buffs = cache->fontBuffers.elements;
+ for (int i = 0; i < cache->fontBuffers.count; ++i) {
+ vkvg_font_face_t* faces = buffs[i].faces.elements;
+ for (int j = 0; j < buffs[i].faces.count; ++j) {
+ uint64_t* queries = faces[j].queryHashes.elements;
+ for (int k = 0; k < faces[j].queryHashes.count; ++k) {
+ if (queries[k] == queryHash) {
+ *face = &faces[j];
+ return true;
+ }
+ }
}
}
+ return false;
+}
+bool tryGetFontBufferFromPathHash(_font_cache_t* cache, uint64_t pathHash, vkvg_font_buffer_t **const fontBuff) {
+ *fontBuff = NULL;
+ for (int i = 0; i < cache->fontBuffers.count; ++i) {
+ if (cache->fontBuffers.elements[i].fontPathHash == pathHash) {
+ *fontBuff = &cache->fontBuffers.elements[i];
+ return true;
+ }
- FcPatternDestroy(pat);
- FcPatternDestroy(font);*/
-
- return (fontFile != NULL);
+ }
+ return false;
}
-#endif
+#ifdef VKVG_USE_FONTCONFIG
+bool _tryResolveFontNameWithFontConfig(VkvgDevice dev, const char* fontString, vkvg_font_face_t **const face) {
+ _font_cache_t* cache = dev->fontCache;
+ uint64_t queryHash = fnv1a_64_str(fontString);
-// try to find corresponding font in cache (defined by context selectedFont) and create a new font entry if not found.
-void _update_current_font(VkvgContext ctx) {
- /*if (ctx->currentFont == NULL) {
- LOCK_FONTCACHE(ctx->dev)
- if (ctx->selectedFontName[0] == 0)
- _select_font_face(ctx, "sans");
+ if (tryGetFontFaceFromQueryHash(cache, queryHash, face))
+ return true;
- if (!_tryFindFontByName(ctx, &ctx->currentFont)) {
-#ifdef VKVG_USE_FONTCONFIG
- _tryResolveFontNameWithFontConfig(ctx, &ctx->currentFont);
+
+ char *fontFile = NULL;
+
+ FcPattern* request = FcNameParse((const FcChar8*)fontString);
+ FcConfigSubstitute(cache->config, request, FcMatchPattern);
+ FcDefaultSubstitute(request);
+
+ FcResult result;
+ FcPattern* font = FcFontMatch(cache->config, request, &result);
+ vkvg_font_buffer_t *buffPtr = NULL;
+
+ if (font) {
+ int index = 0;
+ FcPatternGetString (font, FC_FILE, 0, (FcChar8**)&fontFile);
+ FcPatternGetInteger (font, FC_INDEX, 0, &index);
+ uint64_t fontFileHash = fnv1a_64_str(fontFile);
+
+ if (tryGetFontBufferFromPathHash(cache, fontFileHash, &buffPtr)) {
+ for (int f = 0; f < buffPtr->faces.count; ++f) {
+ if (buffPtr->faces.elements[f].face->face_index == index) {
+ *face = &buffPtr->faces.elements[f];
+ array_add_uint64_t (&(*face)->queryHashes, queryHash);
+ return true;
+ }
+ }
+ } else {
+ vkvg_font_buffer_t buff = {fontFileHash};
+ FILE *file = fopen(fontFile, "rb");
+ if (!file) {
+ LOG(VKVG_LOG_ERR, "Error opening font file: %s\n", fontFile);
+ return false;
+ }
+ fseek(file, 0, SEEK_END);
+ buff.bufferSize = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ buff.buffer = malloc(buff.bufferSize);
+ if (!buff.buffer) {
+ LOG(VKVG_LOG_ERR, "Error opening font file: %s\n", fontFile);
+ fclose(file);
+ return false;
+ }
+ size_t bytes_read = fread(buff.buffer, 1, buff.bufferSize, file);
+ fclose(file);
+ buff.faces = array_create_vkvg_font_face_t(2);
+ buffPtr = &cache->fontBuffers.elements[cache->fontBuffers.count];
+ array_add_vkvg_font_buffer_t(&cache->fontBuffers, buff);
+ }
+
+ vkvg_font_face_t newFace = {0};
+ newFace.queryHashes = array_create_uint64_t(2);
+ array_add_uint64_t(&newFace.queryHashes, queryHash);
+ if (dev->threadAware)
+ mtx_init(&newFace.mutex, mtx_plain);
+
+#ifdef VKVG_USE_FREETYPE
+ FT_CHECK_RESULT(FT_New_Memory_Face(cache->library, buffPtr->buffer, buffPtr->bufferSize, index, &newFace.face));
#else
- LOG(VKVG_LOG_ERR, "Unresolved font: %s\n", ctx->selectedFontName);
- UNLOCK_FONTCACHE(ctx->dev)
+ int result = stbtt_InitFont(&font->stbInfo, font->fontBuffer, 0);
+ assert(result && "stbtt_initFont failed");
+ if (!result) {
ctx->status = VKVG_STATUS_INVALID_FONT;
- return;
-#endif
+ return NULL;
}
-
- ctx->currentFontSize = _find_or_create_font_size(ctx);
- UNLOCK_FONTCACHE(ctx->dev)
- }*/
+ stbtt_GetFontVMetrics(&font->stbInfo, &font->ascent, &font->descent, &font->lineGap);
+ newSize.charLookup = (_char_ref**)calloc(font->stbInfo.numGlyphs, sizeof(_char_ref*));
+ // newSize.scale = stbtt_ScaleForPixelHeight(&font->stbInfo, newSize.charSize);
+ newSize.scale = stbtt_ScaleForMappingEmToPixels(&font->stbInfo, newSize.charSize);
+ newSize.curLine.height = roundf(newSize.scale * (font->ascent - font->descent + font->lineGap));
+ newSize.ascent = roundf(newSize.scale * font->ascent);
+ newSize.descent = roundf(newSize.scale * font->descent);
+ newSize.lineGap = roundf(newSize.scale * font->lineGap);
+#endif
+ *face = &buffPtr->faces.elements[buffPtr->faces.count];
+ array_add_vkvg_font_face_t(&buffPtr->faces, newFace);
+ return true;
+ }
+ FcPatternDestroy(request);
+ return false;
}
+#endif
#ifdef VKVG_USE_HARFBUZZ
// Get harfBuzz buffer for provided text.
-hb_buffer_t* _get_hb_buffer(_vkvg_font_t* font, const char* text, int length) {
+hb_buffer_t* _get_hb_buffer(vkvg_font_t* font, const char* text, int length) {
hb_buffer_t* buf = hb_buffer_create();
hb_script_t script = HB_SCRIPT_LATIN;
hb_unicode_funcs_t* ucfunc = hb_unicode_funcs_get_default();
- wchar_t firstChar = 0;
- if (mbstowcs(&firstChar, text, 1))
- script = hb_unicode_script(ucfunc, firstChar);
+ uint32_t firstChar = decode_unicode_codepoint((uint8_t*)text);
+
+ script = hb_unicode_script(ucfunc, firstChar);
hb_direction_t dir = hb_script_get_horizontal_direction(script);
hb_buffer_set_direction(buf, dir);
hb_buffer_set_script(buf, script);
// hb_buffer_set_language (buf, hb_language_from_string (lng, (int)strlen(lng)));
hb_buffer_add_utf8(buf, text, length, 0, length);
-
hb_shape(font->hb_font, buf, NULL, 0);
return buf;
@@ -574,7 +669,7 @@ void _font_cache_font_extents(VkvgContext ctx, vkvg_font_extents_t* extents) {
return;
// TODO: ensure correct metrics are returned (scalled/unscalled, etc..)
- _vkvg_font_t* font = ctx->currentFontSize;
+ vkvg_font_t* font = ctx->currentFontSize;
#ifdef VKVG_USE_FREETYPE
FT_BBox* bbox = &font->face->bbox;
FT_Size_Metrics* metrics = &font->face->size->metrics;
@@ -601,32 +696,26 @@ void _font_cache_text_extents(VkvgContext ctx, const char* text, int length, vkv
}
vkvg_text_run_t tr = {0};
- _font_cache_create_text_run(ctx, text, length, &tr);
+ _font_cache_init_text_run(ctx, text, length, &tr);
if (ctx->status)
return;
*extents = tr.extents;
- _font_cache_destroy_text_run(&tr);
+ _font_cache_term_text_run(&tr);
}
// text is expected as utf8 encoded
// if length is < 0, text must be null terminated, else it contains glyph count
-void _font_cache_create_text_run(VkvgContext ctx, const char* text, int length, VkvgText textRun) {
-
- _update_current_font(ctx);
-
- if (ctx->status)
- return;
-
- //textRun->fontId = ctx->currentFont;
- //textRun->font = ctx->currentFontSize;
+void _font_cache_init_text_run(VkvgContext ctx, const char* text, int length, VkvgText textRun) {
textRun->dev = ctx->dev;
+ textRun->font = ctx->currentFont;
+ vkvg_font_reference (textRun->font);
LOCK_FONTCACHE(ctx->dev)
#ifdef VKVG_USE_HARFBUZZ
- //textRun->hbBuf = _get_hb_buffer(ctx->currentFontSize, text, length);
+ textRun->hbBuf = _get_hb_buffer(textRun->font, text, length);
textRun->glyphs = hb_buffer_get_glyph_positions(textRun->hbBuf, &textRun->glyph_count);
#else
@@ -664,9 +753,9 @@ void _font_cache_create_text_run(VkvgContext ctx, const char* text, int length,
for (uint32_t i = 0; i < textRun->glyph_count; ++i)
string_width_in_pixels += textRun->glyphs[i].x_advance >> 6;
#ifdef VKVG_USE_FREETYPE
- /*FT_Size_Metrics* metrics = &ctx->currentFontSize->face->size->metrics;
- textRun->extents.height = (float)(FT_MulFix(ctx->currentFontSize->face->height, metrics->y_scale) >>
- 6); // (metrics->ascender + metrics->descender) >> 6;*/
+ FT_Size_Metrics* metrics = &textRun->font->ftSize->metrics;
+ textRun->extents.height = (float)(FT_MulFix(textRun->font->face->face->height, metrics->y_scale) >>
+ 6); // (metrics->ascender + metrics->descender) >> 6;
#else
textRun->extents.height = textRun->font->ascent - textRun->font->descent + textRun->font->lineGap;
#endif
@@ -679,13 +768,14 @@ void _font_cache_create_text_run(VkvgContext ctx, const char* text, int length,
textRun->extents.width = textRun->extents.x_advance;
}
-void _font_cache_destroy_text_run(VkvgText textRun) {
+void _font_cache_term_text_run(VkvgText textRun) {
#ifdef VKVG_USE_HARFBUZZ
hb_buffer_destroy(textRun->hbBuf);
#else
if (textRun->glyph_count > 0)
free(textRun->glyphs);
#endif
+ vkvg_font_destroy(textRun->font);
}
#ifdef DEBUG
void _show_texture(vkvg_context* ctx) {
@@ -722,37 +812,37 @@ void _font_cache_show_text_run(VkvgContext ctx, VkvgText tr) {
LOCK_FONTCACHE(ctx->dev)
for (uint32_t i = 0; i < glyph_count; ++i) {
- _char_ref* cr = tr->font->charLookup[glyph_info[i].codepoint];
+ _char_ref cr = tr->font->charLookup[glyph_info[i].codepoint];
#ifdef VKVG_USE_HARFBUZZ
- if (cr == NULL)
+ if (!cr.index)
cr = _prepare_char(tr->dev, tr, glyph_info[i].codepoint);
#endif
- float uvWidth = cr->bounds.width / (float)FONT_PAGE_SIZE;
- float uvHeight = cr->bounds.height / (float)FONT_PAGE_SIZE;
- vec2 p0 = {pen.x + cr->bmpDiff.x + (tr->glyphs[i].x_offset >> 6),
- pen.y - cr->bmpDiff.y + (tr->glyphs[i].y_offset >> 6)};
+ float uvWidth = cr.bounds.width / (float)FONT_PAGE_SIZE;
+ float uvHeight = cr.bounds.height / (float)FONT_PAGE_SIZE;
+ vec2 p0 = {pen.x + cr.bmpDiff.x + (tr->glyphs[i].x_offset >> 6),
+ pen.y - cr.bmpDiff.y + (tr->glyphs[i].y_offset >> 6)};
v.pos = p0;
VKVG_IBO_INDEX_TYPE firstIdx = (VKVG_IBO_INDEX_TYPE)(ctx->vertCount - ctx->curVertOffset);
- v.uv.x = cr->bounds.x;
- v.uv.y = cr->bounds.y;
- v.uv.z = cr->pageIdx;
+ v.uv.x = cr.bounds.x;
+ v.uv.y = cr.bounds.y;
+ v.uv.z = cr.pageIdx;
_add_vertex(ctx, v);
- v.pos.y += cr->bounds.height;
+ v.pos.y += cr.bounds.height;
v.uv.y += uvHeight;
_add_vertex(ctx, v);
- v.pos.x += cr->bounds.width;
+ v.pos.x += cr.bounds.width;
v.pos.y = p0.y;
v.uv.x += uvWidth;
- v.uv.y = cr->bounds.y;
+ v.uv.y = cr.bounds.y;
_add_vertex(ctx, v);
- v.pos.y += cr->bounds.height;
+ v.pos.y += cr.bounds.height;
v.uv.y += uvHeight;
_add_vertex(ctx, v);
@@ -775,16 +865,15 @@ void _font_cache_show_text_run(VkvgContext ctx, VkvgText tr) {
}
void _font_cache_show_text(VkvgContext ctx, const char* text) {
-
vkvg_text_run_t tr = {0};
- _font_cache_create_text_run(ctx, text, -1, &tr);
+ _font_cache_init_text_run(ctx, text, -1, &tr);
if (ctx->status)
return;
_font_cache_show_text_run(ctx, &tr);
- _font_cache_destroy_text_run(&tr);
+ _font_cache_term_text_run(&tr);
//_show_texture(ctx); return;
}
@@ -826,3 +915,99 @@ void _font_cache_show_text(VkvgContext ctx, const char* text) {
FT_Done_Face ( face );
FT_Done_FreeType( library );
}*/
+
+bool _try_get_font_size (vkvg_font_face_t* face, uint32_t pt26_6, VkvgFont *font) {
+ *font = NULL;
+ for (int i = 0; i < face->sizes.count; ++i) {
+ if (face->sizes.elements[i]->charSize == pt26_6) {
+ *font = face->sizes.elements[i];
+ return true;
+ }
+ }
+ return false;
+}
+
+VkvgFont vkvg_font_create (VkvgDevice dev, const char* query, float pointSize) {
+ uint32_t pt26_6 = (uint32_t)(pointSize * 64.f);
+ VkvgFont font = NULL;
+ vkvg_font_face_t* face = NULL;
+ if (_tryResolveFontNameWithFontConfig(dev, query, &face)) {
+ if (_try_get_font_size(face, pt26_6, &font)) {
+ vkvg_font_reference(font);
+ return font;
+ }
+ }
+
+ font = (vkvg_font_t*)calloc(1, sizeof(vkvg_font_t));
+ if (!font) {
+ return (VkvgFont)&_vkvg_status_no_memory;
+ }
+
+ font->charSize = pt26_6;
+ font->face = face;
+
+ _font_cache_t* cache = dev->fontCache;
+
+#ifdef VKVG_USE_FREETYPE
+ if (face->sizes.count) {
+ FT_CHECK_RESULT(FT_New_Size (face->face, &font->ftSize));
+ FT_Activate_Size(font->ftSize);
+ } else {
+ face->sizes = array_create_VkvgFont(5);
+ }
+ array_add_VkvgFont(&face->sizes, font);
+ FT_CHECK_RESULT(FT_Set_Char_Size(face->face, 0, font->charSize, dev->hdpi, dev->vdpi));
+
+ font->ftSize = face->face->size;
+ font->charLookup = (_char_ref*)calloc(face->face->num_glyphs, sizeof(_char_ref));
+
+ if (FT_IS_SCALABLE(face->face))
+ font->curLine.height = face->face->size->metrics.height >> 6;
+ else
+ font->curLine.height = face->face->height >> 6;
+#else
+ stbtt_GetFontVMetrics(&font->stbInfo, &font->ascent, &font->descent, &font->lineGap);
+ font->charLookup = (_char_ref**)calloc(font->stbInfo.numGlyphs, sizeof(_char_ref*));
+ // newSize.scale = stbtt_ScaleForPixelHeight(&font->stbInfo, newSize.charSize);
+ font->scale = stbtt_ScaleForMappingEmToPixels(&font->stbInfo, newSize.charSize);
+ font->curLine.height = roundf(font->scale * (font->ascent - font->descent + font->lineGap));
+ font->ascent = roundf(font->scale * font->ascent);
+ font->descent = roundf(font->scale * font->descent);
+ font->lineGap = roundf(font->scale * font->lineGap);
+#endif
+#ifdef VKVG_USE_HARFBUZZ
+ font->hb_font = hb_ft_font_create(face->face, NULL);
+#endif
+ atomic_init(&font->references, 1);
+ return font;
+}
+
+VkvgFont vkvg_font_reference(VkvgFont font) {
+ if (!vkvg_font_status(font))
+ atomic_fetch_add_explicit(&font->references, 1, memory_order_relaxed);
+
+ return font;
+}
+uint32_t vkvg_font_get_reference_count(VkvgFont font) {
+ if (vkvg_font_status(font))
+ return 0;
+ return atomic_load_explicit(&font->references, memory_order_relaxed);
+}
+vkvg_status_t vkvg_font_status(VkvgFont font) { return !font ? VKVG_STATUS_NULL_POINTER : font->status; }
+
+void vkvg_font_destroy(VkvgFont font) {
+ if (vkvg_font_status(font))
+ return;
+ if (atomic_fetch_sub_explicit(&font->references, 1, memory_order_acq_rel) != 1)
+ return;
+#ifdef VKVG_USE_FREETYPE
+ FT_Done_Size(font->ftSize);
+#endif
+#ifdef VKVG_USE_HARFBUZZ
+ hb_font_destroy(font->hb_font);
+#endif
+
+ free(font->charLookup);
+ free(font);
+ font = NULL;
+}
diff --git a/src/vkvg_fonts.h b/src/vkvg_fonts.h
index 037c254..6ccac88 100644
--- a/src/vkvg_fonts.h
+++ b/src/vkvg_fonts.h
@@ -31,6 +31,7 @@
#ifdef VKVG_USE_FREETYPE
#include <ft2build.h>
#include FT_FREETYPE_H
+#include FT_SIZES_H
#if defined(VKVG_LCD_FONT_FILTER) && defined(FT_CONFIG_OPTION_SUBPIXEL_RENDERING)
#include <freetype/ftlcdfil.h>
#endif
@@ -129,19 +130,48 @@ static const uint8_t utf8_len_table[256] = {
1,1,1,1,1,1,1,1
};
+static inline uint32_t decode_unicode_codepoint(const uint8_t* text) {
+ if (text == NULL || *text == '\0')
+ return 0;
+
+ uint32_t state = UTF8_ACCEPT;
+ uint32_t codepoint = 0;
+ int i = 0;
+ while (text[i] != '\0') {
+ decode_utf8_byte(&state, &codepoint, text[i]);
+
+ if (state == UTF8_ACCEPT)
+ return codepoint;
+
+ if (state == UTF8_REJECT) {
+ LOG(VKVG_LOG_DEBUG, "Malformed utf8 string.\n");
+ return 0;
+ }
+
+ i++;
+ }
+
+ // If the loop finished but we never reached UTF8_ACCEPT, the string cut off abruptly
+ if (state == UTF8_ACCEPT)
+ return codepoint;
+ LOG(VKVG_LOG_DEBUG, "Error: Incomplete UTF-8 character sequence.\n");
+ return 0;
+}
+
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. */
- vec2i16 bmpDiff; /* Difference in pixel between char bitmap top left corner and char glyph*/
- uint8_t pageIdx; /* Page index in font cache texture array */
+ uint32_t index; /* stored here only to detect empty char_ref */
+ vec4 bounds; /* normalized float bounds of character bitmap in font cache texture. */
+ vec2i16 bmpDiff; /* Difference in pixel between char bitmap top left corner and char glyph*/
#ifdef VKVG_USE_FREETYPE
- FT_Vector advance; /* horizontal or vertical advance */
+ FT_Vector advance; /* horizontal or vertical advance */
#else
vec2 advance;
#endif
+ uint8_t pageIdx; /* Page index in font cache texture array */
} _char_ref;
// Current location in font cache texture array for new character addition. Each font holds such structure to locate
@@ -153,44 +183,62 @@ typedef struct {
int height; /* Height of current line pointed by this structure */
} _tex_ref_t;
-// Loaded font structure, one per size, holds informations for glyphes upload in cache and the lookup table of
-// characters.
-typedef struct {
-#ifdef VKVG_USE_FREETYPE
- FT_F26Dot6 charSize; /* Font size*/
- FT_Face face; /* FreeType face*/
-#else
- uint32_t charSize; /* Font size in pixel */
- float scale; /* scale factor for the given size */
- int ascent; /* unscalled stb font metrics */
- int descent;
- int lineGap;
-#endif
-#ifdef VKVG_USE_HARFBUZZ
- hb_font_t* hb_font; /* HarfBuzz font instance*/
-#endif
- _char_ref** charLookup; /* Lookup table of characteres in cache, if not found, upload is queued*/
-
- _tex_ref_t curLine; /* tex coord where to add new char bmp's */
-} _vkvg_font_t;
+CTOR_ARRAY(uint64_t)
+CTOR_ARRAY(VkvgFont)
/* Font identification structure */
typedef struct {
- 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; /* */
- char* fontFile; /* Font file full path*/
+ array_uint64_t queryHashes;
+#ifdef VKVG_USE_FREETYPE
+ FT_Face face; /* FreeType face*/
+ mtx_t mutex; /* Only one font size at a time may use this face */
+#endif
+ array_VkvgFont sizes; /* loaded font size array */
+
#ifndef VKVG_USE_FREETYPE
stbtt_fontinfo stbInfo; /* stb_truetype structure */
int ascent; /* unscalled stb font metrics */
int descent;
int lineGap;
#endif
- uint32_t sizeCount; /* available font size loaded */
- _vkvg_font_t* sizes; /* loaded font size array */
-} _vkvg_font_identity_t;
+} vkvg_font_face_t;
+
+CTOR_ARRAY(vkvg_font_face_t)
+
+typedef struct
+{
+ uint64_t fontPathHash;
+ size_t bufferSize;
+ unsigned char* buffer;
+ array_vkvg_font_face_t faces;
+} vkvg_font_buffer_t;
+
+CTOR_ARRAY(vkvg_font_buffer_t)
+
+typedef struct _vkvg_font_t {
+ vkvg_status_t status;
+ atomic_int references; // reference count
+
+ vkvg_font_face_t* face;
+#ifdef VKVG_USE_FREETYPE
+ FT_F26Dot6 charSize; /* Font size in Point as fixed float 26.6 */
+ FT_Size ftSize; /* FT size rec */
+#else
+ uint32_t charSize; /* Font size in pixel */
+ float scale; /* scale factor for the given size */
+ int ascent; /* unscalled stb font metrics */
+ int descent;
+ int lineGap;
+#endif
+
+#ifdef VKVG_USE_HARFBUZZ
+ hb_font_t* hb_font; /* HarfBuzz font instance*/
+#endif
+ _char_ref* charLookup; /* Lookup table of characteres in cache, if not found, upload is queued*/
+ _tex_ref_t curLine; /* texture reference where to add new glyph bmp's in cache*/
+} vkvg_font_t;
+
// Font cache global structure, entry point for all font related operations.
typedef struct {
@@ -199,11 +247,13 @@ typedef struct {
#else
#endif
#ifdef VKVG_USE_FONTCONFIG
- FcConfig* config; /* Font config, used to find font files by font names*/
+ FcConfig* config; /* Font config, used to find font files by font names*/
+ //FcPattern* fcPattern; /* patter used for all Font config queries */
+ FcObjectSet* fcReqElts; /* Font config requested metadata elements */
#endif
- int stagingX; /* x pen in host buffer */
- uint8_t* hostBuff; /* host memory where bitmaps are first loaded */
+ int stagingX; /* x pen in host buffer */
+ uint8_t* hostBuff; /* host memory where bitmaps are first loaded */
VkCommandBuffer cmd; /* vulkan command buffer for font textures upload */
vkh_buffer_t buff; /* stagin buffer */
@@ -216,9 +266,7 @@ typedef struct {
VkFence uploadFence; /* Signaled when upload is finished */
mtx_t mutex; /* font cache global mutex, used only if device is in thread aware mode (see:
vkvg_device_set_thread_aware) */
-
- _vkvg_font_identity_t* fonts; /* Loaded fonts structure array */
- int32_t fontsCount; /* Loaded fonts array count*/
+ array_vkvg_font_buffer_t fontBuffers;
} _font_cache_t;
#define LOCK_FONTCACHE(dev) \
@@ -230,17 +278,16 @@ typedef struct {
// Precompute everything necessary to measure and draw one line of text, usefull to draw the same text multiple times.
typedef struct _vkvg_text_run_t {
- _vkvg_font_identity_t* fontId; /* vkvg font structure pointer */
- _vkvg_font_t* font; /* vkvg font structure pointer */
- VkvgDevice dev; /* vkvg device associated with this text run */
- vkvg_text_extents_t extents; /* store computed text extends */
- const char* text; /* utf8 char array of text*/
- unsigned int glyph_count; /* Total glyph count */
+ VkvgFont font; /* vkvg font structure pointer */
+ VkvgDevice dev; /* vkvg device associated with this text run */
+ vkvg_text_extents_t extents; /* store computed text extends */
+ const char* text; /* utf8 char array of text*/
+ unsigned int glyph_count;/* Total glyph count */
#ifdef VKVG_USE_HARFBUZZ
- hb_buffer_t* hbBuf; /* HarfBuzz buffer of text */
- hb_glyph_position_t* glyphs; /* HarfBuzz computed glyph positions array */
+ hb_buffer_t* hbBuf; /* HarfBuzz buffer of text */
+ hb_glyph_position_t* glyphs; /* HarfBuzz computed glyph positions array */
#else
- vkvg_glyph_info_t* glyphs; /* computed glyph positions array */
+ vkvg_glyph_info_t* glyphs; /* computed glyph positions array */
#endif
} vkvg_text_run_t;
@@ -248,8 +295,8 @@ typedef struct _vkvg_text_run_t {
void _fonts_cache_create(VkvgDevice dev, const char *fontDirs);
// Release all ressources of font cache.
void _font_cache_destroy(VkvgDevice dev);
-_vkvg_font_identity_t* _font_cache_add_font_identity(VkvgContext ctx, const char* fontFile, const char* name);
-bool _font_cache_load_font_file_in_memory(_vkvg_font_identity_t* fontId);
+vkvg_font_face_t* _font_cache_add_font_identity(VkvgContext ctx, const char* fontFile, const char* name);
+bool _font_cache_load_font_file_in_memory(vkvg_font_face_t* fontId);
// Draw text
void _font_cache_show_text(VkvgContext ctx, const char* text);
// Get text dimmensions
@@ -257,9 +304,9 @@ void _font_cache_text_extents(VkvgContext ctx, const char* text, int length, vkv
// Get font global dimmensions
void _font_cache_font_extents(VkvgContext ctx, vkvg_font_extents_t* extents);
// Create text object that could be drawn multiple times minimizing harfbuzz and compute processing.
-void _font_cache_create_text_run(VkvgContext ctx, const char* text, int length, VkvgText textRun);
+void _font_cache_init_text_run(VkvgContext ctx, const char* text, int length, VkvgText textRun);
// Release ressources held by a text run.
-void _font_cache_destroy_text_run(VkvgText textRun);
+void _font_cache_term_text_run(VkvgText textRun);
// Draw text run
void _font_cache_show_text_run(VkvgContext ctx, VkvgText tr);
// update context font cache descriptor set
diff --git a/src/vkvg_internal.h b/src/vkvg_internal.h
index dfafd48..c1fb758 100644
--- a/src/vkvg_internal.h
+++ b/src/vkvg_internal.h
@@ -39,6 +39,7 @@
#include <stdint.h>
#include <stdio.h> // needed before stdarg.h on Windows
#include <stdarg.h>
+#include <stdatomic.h>
// should be supported by c11
// #include <threads.h>
@@ -53,6 +54,32 @@
#define M_2_PIF 0.63661977236758134308f // 2/pi
#endif
+#define CTOR_ARRAY(type) \
+typedef struct { \
+ uint32_t count; \
+ uint32_t size; \
+ type *elements; \
+} array_##type; \
+static array_##type array_create_##type (uint32_t reservedSize) { \
+ type *elts = (type*)malloc(reservedSize * sizeof(type)); \
+ return (array_##type) {0, reservedSize, elts}; \
+} \
+static int array_add_##type (array_##type* arr, type elt) { \
+ if (arr->size <= arr->count) { \
+ uint32_t newSize = arr->size * 2; \
+ type *elts = (type*)realloc(arr->elements, newSize * sizeof(type)); \
+ if (elts == NULL) \
+ return 0; \
+ arr->elements = elts; \
+ arr->size = newSize; \
+ } \
+ arr->elements[arr->count++] = elt; \
+ return 1; \
+} \
+static void array_destroy_##type (array_##type* arr) { \
+ free(arr->elements); \
+}
+
/**
* @brief Computes the 64-bit FNV-1a hash of a ascii string lowering case.
*
diff --git a/tests/font_tests/font_tests.c b/tests/font_tests/font_tests.c
index 8d29a45..ed2a279 100644
--- a/tests/font_tests/font_tests.c
+++ b/tests/font_tests/font_tests.c
@@ -861,6 +861,29 @@ FcPattern* request = FcPatternCreate();
FcPatternAddString(request, FC_FAMILY, (const FcChar8*)"Roboto Flex");
FcPatternAddString(request, FC_FONT_VARIATIONS, (const FcChar8*)"wght=625,wdth=80");
*/
+/* Fallback chain
+ // 1. Initialize Fontconfig library and load system configuration
+ FcConfig* config = FcInitLoadConfigAndFonts();
+ if (!config) return 1;
+
+ // 2. Create an empty pattern
+ FcPattern* pat = FcPatternCreate();
+
+ // 3. Programmatically define the fallback chain (Order: Highest -> Lowest)
+ // The last argument 'FcTrue' appends the font to the end of the list
+ FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)"CustomWebFont", FcTrue);
+ FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)"Liberation Sans", FcTrue);
+ FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)"DejaVu Sans", FcTrue);
+ FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)"sans-serif", FcTrue); // Generic system fallback
+
+ // Add other attributes if needed (e.g., matching bold)
+ FcPatternAddInteger(pat, FC_WEIGHT, FC_WEIGHT_BOLD);
+
+ // 4. Perform standard configuration-defined substitutions
+ // This hooks into system/user XML configurations (~/.config/fontconfig/fonts.conf)
+ // to fill in missing properties or alias maps.
+ FcConfigSubstitute(config, pat,
+*/
void setup_variable_font(FcPattern* matched_font) {
// 1. Verify if the font handles open vector variables