summaryrefslogtreecommitdiffstatsci
diff options
context:
space:
mode:
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>2026-10-10 17:27:23 +0200
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>2026-10-10 17:27:23 +0200
commitb4babe4eab5bf39191e435e288edd78cff3c233b (patch)
tree56ae40b587730050329a525c57fe777b07be776d
parentd13fdbbfc6ec430333e517b9646efa47b911c2b8 (diff)
update stbtt
-rw-r--r--src/vkvg_context.c2
-rw-r--r--src/vkvg_fonts.c33
-rw-r--r--tests/fonts/allfont.c21
-rw-r--r--tests/fonts/fontquery.c457
4 files changed, 489 insertions, 24 deletions
diff --git a/src/vkvg_context.c b/src/vkvg_context.c
index 29e9a6a..5893c82 100644
--- a/src/vkvg_context.c
+++ b/src/vkvg_context.c
@@ -1188,7 +1188,7 @@ void vkvg_select_font_face(VkvgContext ctx, const char* name) {
if (vkvg_status(ctx))
return;
RECORD(ctx, VKVG_CMD_SET_FONT_FACE, name);
- _select_font_face(ctx, name);
+ vkvg_set_font(ctx, vkvg_font_create(ctx->dev, name, 12));
}
void vkvg_load_font_from_path(VkvgContext ctx, const char* path, const char* name) {
if (vkvg_status(ctx))
diff --git a/src/vkvg_fonts.c b/src/vkvg_fonts.c
index 01d9c20..342654c 100644
--- a/src/vkvg_fonts.c
+++ b/src/vkvg_fonts.c
@@ -474,7 +474,6 @@ vkvg_inline glyph_ref get_cached_glyph (VkvgDevice dev, VkvgFont font, uint32_t
return font->charLookup[gIndex];
}
-
bool try_get_font_face_from_query_hash(_font_cache_t* cache, uint64_t queryHash, FontFace *const face) {
*face = NULL;
FontBuffer* buffs = cache->fontBuffers.elements;
@@ -567,8 +566,8 @@ bool try_create_font_face_from_path (VkvgDevice dev, const char *fontFile, int f
#endif
#else
int font_count = stbtt_GetNumberOfFonts(buffPtr->buffer);
- int offset = stbtt_GetFontOffsetForIndex(buffPtr->buffer, faceIndex);
- int result = stbtt_InitFont(&newFace->stbInfo, buffPtr->buffer, offset);
+ int offset = stbtt_GetFontOffsetForIndex(buffPtr->buffer, faceIndex & 0xFFFF);//variable font not handled by stbtt
+ int result = stbtt_InitFont(&newFace->stbInfo, buffPtr->buffer, offset < 0 ? 0 : offset);
if (!result) {
LOGE("stbtt_InitFont error 0x%x creating face in %s at line %d\n", result, __FILE__, __LINE__);
return false;
@@ -662,8 +661,6 @@ void _font_lock_size (VkvgContext ctx, VkvgFont font) {
// retrieve global font extends of context's current font as defined by FreeType
void _font_cache_font_extents(VkvgContext ctx, vkvg_font_extents_t* extents) {
-
- // TODO: ensure correct metrics are returned (scalled/unscalled, etc..)
VkvgFont font = ctx->currentFont;
#ifdef VKVG_USE_FREETYPE
FT_Face face = font->face->face;
@@ -687,10 +684,9 @@ void _font_cache_font_extents(VkvgContext ctx, vkvg_font_extents_t* extents) {
/*if (ctx->dev->threadAware)
mtx_unlock(&font->face->mutex);*/
#else
- extents->ascent = roundf(font->scale * ctx->currentFont->ascent);
- extents->descent = -roundf(font->scale * ctx->currentFont->descent);
- extents->height =
- roundf(font->scale * (ctx->currentFont->ascent - ctx->currentFont->descent + ctx->currentFont->lineGap));
+ extents->ascent = font->ascent;
+ extents->descent = font->descent;
+ extents->height = font->ascent - font->descent + font->lineGap;
extents->max_x_advance = 0; // TODO
extents->max_y_advance = 0;
#endif
@@ -748,16 +744,14 @@ void text_run_init(VkvgContext ctx, const char* text, int length, VkvgText textR
textRun->glyphs = (vkvg_glyph_info_t*)malloc(textRun->glyph_count * sizeof(vkvg_glyph_info_t));
for (unsigned int i = 0; i < textRun->glyph_count; i++) {
#ifdef VKVG_USE_FREETYPE
- uint32_t gindex = FT_Get_Char_Index(textRun->font->face, tmp[i]);
+ uint32_t gIndex = FT_Get_Char_Index(textRun->font->face->face, tmp[i]);
#else
- uint32_t gindex = stbtt_FindGlyphIndex(&textRun->fontId->stbInfo, tmp[i]);
+ uint32_t gIndex = stbtt_FindGlyphIndex(&textRun->font->face->stbInfo, tmp[i]);
#endif
- _char_ref* cr = textRun->font->charLookup[gindex];
- if (cr == NULL)
- cr = _prepare_char(textRun->dev, textRun, gindex);
- textRun->glyphs[i].codepoint = gindex;
- textRun->glyphs[i].x_advance = cr->advance.x;
- textRun->glyphs[i].y_advance = cr->advance.y;
+ glyph_ref cr = get_cached_glyph(textRun->dev, textRun->font, gIndex);
+ textRun->glyphs[i].codepoint = gIndex;
+ textRun->glyphs[i].x_advance = cr.advance.x;
+ textRun->glyphs[i].y_advance = cr.advance.y;
textRun->glyphs[i].x_offset = 0;
textRun->glyphs[i].y_offset = 0;
/*textRun->glyphs[i].x_offset = cr->bmpDiff.x;
@@ -1010,7 +1004,6 @@ VkvgFont vkvg_font_create_from_file (VkvgDevice dev, const char* fontFilePath, i
}
return _create_font_from_face(dev, face, pointSize);
}
-
VkvgFont vkvg_font_create (VkvgDevice dev, const char* query, float pointSize) {
uint32_t pt26_6 = (uint32_t)(pointSize * 64.f);
@@ -1027,8 +1020,6 @@ VkvgFont vkvg_font_create (VkvgDevice dev, const char* query, float pointSize) {
return _create_font_from_face(dev, face, pointSize);
}
-
-
VkvgFont vkvg_font_reference(VkvgFont font) {
if (!vkvg_font_status(font))
atomic_fetch_add_explicit(&font->references, 1, memory_order_relaxed);
@@ -1040,9 +1031,7 @@ uint32_t vkvg_font_get_reference_count(VkvgFont 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;
diff --git a/tests/fonts/allfont.c b/tests/fonts/allfont.c
index efafa8a..e9f94a9 100644
--- a/tests/fonts/allfont.c
+++ b/tests/fonts/allfont.c
@@ -1,5 +1,7 @@
#include "test.h"
+#ifdef VKVG_USE_FONTCONFIG
#include <fontconfig/fontconfig.h>
+#endif
#ifdef VKVG_USE_FREETYPE
#include <ft2build.h>
@@ -18,6 +20,7 @@ bool redraw = true;
#define MAX_STRING_LEN 1024
+#ifdef VKVG_USE_FONTCONFIG
FcFontSet* font_database = NULL;
typedef struct {
@@ -247,6 +250,14 @@ void draw(VkvgSurface surfFont) {
vkvg_destroy(ctx);
}
+#else
+void draw(VkvgSurface surfFont) {
+ VkvgContext ctx = vkvg_create(surfFont);
+ vkvg_clear(ctx);
+ vkvg_destroy(ctx);
+}
+#endif
+
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if (action == GLFW_RELEASE)
return;
@@ -267,6 +278,8 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action,
static void mouse_move_callback(GLFWwindow* window, double x, double y) {
}
static void scroll_callback(GLFWwindow* window, double x, double y) {
+#ifdef VKVG_USE_FONTCONFIG
+
const int step = 5;
if (y > 0.f) {
if (curFontStartIdx > 0)
@@ -276,6 +289,7 @@ static void scroll_callback(GLFWwindow* window, double x, double y) {
if (curFontStartIdx < font_database->nfont)
curFontStartIdx +=5;
}
+#endif
redraw = true;
}
static void mouse_button_callback(GLFWwindow* window, int but, int state, int modif) {
@@ -290,7 +304,7 @@ static void mouse_button_callback(GLFWwindow* window, int but, int state, int mo
int main(int argc, char* argv[]) {
vkh_log_level = VKH_LOG_FULL;
- vkvg_log_level = VKVG_LOG_ERR | VKVG_LOG_WARN | VKVG_LOG_FONT;
+ vkvg_log_level = VKVG_LOG_ERR | VKVG_LOG_WARN | VKVG_LOG_FONT ;
_parse_args(argc, argv);
VkEngine e = vkengine_create (
@@ -315,6 +329,8 @@ int main(int argc, char* argv[]) {
vkh_presenter_build_blit_cmd(r, vkvg_surface_get_vk_image(surf), test_width, test_height);
+#ifdef VKVG_USE_FONTCONFIG
+
if (!FcInit()) {
fprintf(stderr, "Fatal Error: Failed to initialize Fontconfig.\n");
return -1;
@@ -326,6 +342,7 @@ int main(int argc, char* argv[]) {
FcPatternAddString(request, FC_FONTFORMAT, (const FcChar8 *)"TrueType");
font_database = FcFontList(config, request, requested_elements);
+#endif
while (!vkengine_should_close(e)) {
glfwPollEvents();
@@ -361,10 +378,12 @@ int main(int argc, char* argv[]) {
vkengine_destroy(e);
+#ifdef VKVG_USE_FONTCONFIG
FcFontSetDestroy(font_database);
FcObjectSetDestroy(requested_elements);
FcPatternDestroy(request);
FcFini();
+#endif
return 0;
}
diff --git a/tests/fonts/fontquery.c b/tests/fonts/fontquery.c
new file mode 100644
index 0000000..37b8a92
--- /dev/null
+++ b/tests/fonts/fontquery.c
@@ -0,0 +1,457 @@
+#include "test.h"
+#ifdef VKVG_USE_FONTCONFIG
+#include <fontconfig/fontconfig.h>
+#endif
+
+#ifdef VKVG_USE_FREETYPE
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_SFNT_NAMES_H
+#include FT_TRUETYPE_TABLES_H
+#include FT_TRUETYPE_IDS_H
+#include FT_MULTIPLE_MASTERS_H
+#endif
+
+#include <string.h>
+
+float fontSize = 28.0f;
+int curFontStartIdx = 0;
+bool redraw = true;
+
+#define MAX_STRING_LEN 1024
+
+FcConfig* config = NULL;
+FcFontSet* font_database = NULL;
+
+typedef struct {
+ const char* lang_code;
+ const char* sample_text;
+} LocaleSample;
+
+// A localized library of pan-unicode text variations
+static const LocaleSample script_samples[] = {
+ { "en", "The quick brown fox jumps over the lazy dog." }, // Latin Fallback
+ { "ja", "いろはにほへと ちりぬるを わかよたれそ つねならむ" }, // Japanese Hiragana
+ { "zh", "天地玄黃,宇宙洪荒。日月盈昃,辰宿列張。" }, // Chinese Hanzi
+ { "ko", "다람쥐 쳇바퀴에 타고파" }, // Korean Hangul
+ { "ru", "Съешь же ещё этих мягких французских булок, да выпей чаю" }, // Cyrillic
+ { "ar", "نص حكيم له سر قاطع وذو شأن عظيم ينقص" }, // Arabic
+ { "he", "דג אכל גזר באשחט" }, // Hebrew
+ { "hi", "ऋषियों को तपस्या करते देख, दुष्टों के मन में डर पैदा हुआ।" }, // Hindi / Devanagari
+ { "el", "διαφυλάξτε γενικά τη ζωή σας από τον φόβο" }, // Greek
+ { "th", "เป็นมนุษย์สุดประเสริฐเลิIศคุณค่า" }, // Thai
+ { "hy", "Ֆիզիկոս Մկրտիչը օճառաջուր ցողելով բժշկում է գնդապետ Հայկի փքված ձախ թևը։" }, // Armenian
+ { "agr", "Agatjai juuk tusha numpanum ijuaku jintanum pujawai" }, // Aguaruna (Latin script variation)
+ //{ "aa", "Afari fanta asat darih doro dahanik xisbisa" }, // Afar (Latin script variation)
+ { "bem", "Abantu bonse bafyalwa abalubuka no kulingana mu mucinshi" }, // Bemba (Bantu Latin script)
+ { "pa", "ਘੁਮਿਆਰ ਨੇ ਮਿੱਟੀ ਚੁੱਕ ਕੇ ਇੱਕ ਬਹੁਤ ਹੀ ਸੁੰਦਰ ਘੜਾ ਬਣਾਇਆ।" }, // Punjabi (Gurmukhi script)
+ { "te", "కచ్ఛపము మందగమనముతో చెరువు వైపు వెళుతోంది." }, // Telugu (Telugu script)
+ { "km", "ខ្យល់បក់បោកបបួលបក្សីហើរទៅកាន់ព្រៃព្រឹក្សាដ៏ស្ងប់ស្ងាត់។" }, // Khmer (Khmer script)
+ { "lo", "ນົກກາງແກບິນຂ້າມທົ່ງນາທີ່ກວ້າງໃຫຍ່ໄພສານຢ່າງສະຫງ່າງາມ." }, // Lao (Lao script)
+ { "ta", "தமிழ் மொழி உலகினில் மிக தொன்மையான மற்றும் அழகான மொழியாகும்." }, // Tamil (Tamil script)
+ { "or", "ଋଷିମାନେ ଅରଣ୍ୟ ମଧ୍ୟରେ ଧ୍ୟାନ କରି ଶାନ୍ତି ଲାଭ କରନ୍ତି।" }, // Odia/Oriya (Odia script)
+ { "ml", "ഭൂമിയിലെ എല്ലാ മനുഷ്യരും തുല്യ അവകാശങ്ങളോടെ ജനിക്കുന്നു." }, // Malayalam (Malayalam script)
+ { "as", "শৰৎকালৰ স্নিগ্ধ জোনাক ৰাতি আকাশখন বৰ ধুনীয়া দেখায়।" }, // Assamese (Bengali-Assamese script)
+ { "si", "සියලු මනුෂ්‍යයෝ නිදහස්ව උපත ලබා ඇත." }, // Sinhala (Sinhala script)
+ { "am", "የሰው ልጅ ሁሉ ሲወለድ ነጻና በክብርም በሕግም እኩል ነው።" }, // Amharic (Ge'ez/Ethiopic script)
+ { "anp", "सभ्भे मानुस जनम सँ सुतन्तर आरो अधिकार में बराबर छै।" }, // Angika (Devanagari variant)
+ { "kn", "ಎಲ್ಲಾ ಮಾನವರು ಸ್ವತಂತ್ರರಾಗಿಯೇ ಜನಿಸಿದ್ದಾರೆ." }, // Kannada (Kannada script)
+ { "gu", "બધા મનુષ્યો ગૌરવ અને અધિકારોની બાબતમાં સમાન છે." }, // Gujarati (Gujarati script)
+ { "bo", "འགྲོ་བ་མི་ཉིད་མ་སྐྱེས་པ་ནས་བཟུང་རང་དབང་ཡིན།" }, // Tibetan (Tibetan script)
+ { "ka", "ადამიანი იბადება თავისუფალი და თანასწორი." }, // Georgian (Mkhedruli script)
+ { "mnw", "မန်ဝံသဂမၠိုၚ် က္တဵုဒှ်ကၠုၚ် နကဵုဂုဏ်သိက္ခာ ညဳသၟဟ်ဂမၠိုၚ်။" }, // Mon (Mon-Burmese script)
+ { "ii", "ꊿꊪꉼꆹꊪꊪꊿꐊꐥꌋꅍꐛꐹꂿꆪꄮꄯꉼꆹꄮꄯꂿꆪꊨꏦꍏꆪꐥꌋꅍꐛꐹꏦꍏꅪꏦꍏꅪꐨꏦꍏꄮꄯꅪ" }, // Nuosu / Liangshan Yi (Yi syllabary)
+ { "chr", "ᏂᎦᏛ ᎠᏂᏰᎸᎢ ᎤᏂᎲᏓ ᏂᎨᏒᎾ ᎤᎾᏕᎲᎩ ᎢᏳᏍᏗᏉ ᎨᏒ ᎦᎸᏉᏗᏳ" }, // Cherokee (Cherokee syllabary)
+ { "ber-ma", "ⵎⴰⵕⵕⴰ ⵉⵎᏓⵏⵏ ⴷⴳ ⵓⵍⵉⴼ ⴷ ⵉⵣⵔⴼⴰⵏ ⴷ ⵍⵃⵇⵇ ⵜⵜⵍⴰⵍⴰⵏ ⴷ ⵉⵍⴻⵍⵍⵉⵢⵏ." }, // Moroccan Berber (Tifinagh script)
+ { "ab", "Дарбанზаалак ауаҩы дшоуп ихы ақәgenericуи иҳаgenericти." }, // Abkhaz (Cyrillic variation)
+ { "syr", "ܟܠ ܒܪܢܫܐ ܡܬܝܠܕ ܚܐܪܐ ܘܫܘܝܐ ܒܐܝܩܪܐ ܘܒܙܕܩܐ." }, // Syriac (Syriac script, right-to-left)
+ { "iu", "ᐃᓅᔪᓕᒫᑦ ᐊᓂᖅᑎᕆᔪᓕᒫᑦ ᐃᓅᕗᑦ ᐃᓱᒪᕐᓱᕐᖢᑎᒃ ᐊᒻᒪ ᐊᔾᔨᒌᒃᑐᒥᒃ" }, // Inuktitut (Canadian Aboriginal Syllabics)
+ { "dv", "ހުރިހާ އިންސާނުންވެސް އުފަންވަނީ މިނިވަންކަމާއި ޢިއްޒަތާއި" }, // Dhivehi / Maldivian (Thaana script, right-to-left)
+ { "mn-cn", "Хүн бүр төрөхDiscussion эрх чөлөөтэй, нэр төр, эрхийн хувьд адилхан." }, // Mongolian - Inner Mongolia (Cyrillic fallback)
+};
+
+bool try_get_sample_by_fc_lang(const char* fc_lang, char* str) {
+ const char* sample = NULL;
+ if (fc_lang) {
+ for (int i = 1; i < 37; i++) {
+ if (strncmp(fc_lang, script_samples[i].lang_code, strlen(script_samples[i].lang_code)) == 0) {
+ sample = script_samples[i].sample_text;
+ break;
+ }
+ }
+ if (!sample) {
+ sample = script_samples[0].sample_text;
+ return false;
+ }
+ }
+
+ strncpy(str, sample, strlen(sample)+1);
+ return true;
+}
+
+#ifdef VKVG_USE_FREETYPE
+void convert_utf16be_to_utf8(const FT_Byte* src, FT_UInt src_len, char* dest, FT_UInt dest_max_len) {
+ FT_UInt d_idx = 0;
+
+ // Iterate 2 bytes at a time for UTF-16
+ for (FT_UInt s_idx = 0; s_idx < src_len && d_idx < (dest_max_len - 4); s_idx += 2) {
+ // Reconstruct the 16-bit Unicode Code Point (Big Endian)
+ unsigned short cp = (src[s_idx] << 8) | src[s_idx + 1];
+
+ // 1-byte UTF-8 (Standard ASCII Range)
+ if (cp < 0x80) {
+ dest[d_idx++] = (char)cp;
+ }
+ // 2-byte UTF-8 (Cyrillic, Hebrew, Arabic, etc.)
+ else if (cp < 0x800) {
+ dest[d_idx++] = (char)(0xC0 | (cp >> 6));
+ dest[d_idx++] = (char)(0x80 | (cp & 0x3F));
+ }
+ // 3-byte UTF-8 (Chinese, Japanese, Devanagari, Hangul, etc.)
+ else {
+ dest[d_idx++] = (char)(0xE0 | (cp >> 12));
+ dest[d_idx++] = (char)(0x80 | ((cp >> 6) & 0x3F));
+ dest[d_idx++] = (char)(0x80 | (cp & 0x3F));
+ }
+ }
+ dest[d_idx] = '\0'; // Properly null-terminate
+}
+
+bool try_get_sample_string(FcPattern* font_entry, FT_Face face, char* str) {
+ // Check if the font has an SFNT name table (TrueType/OpenType files)
+ if (!FT_IS_SFNT(face)) {
+ return false;
+ }
+
+ FT_UInt count = FT_Get_Sfnt_Name_Count(face);
+ FT_SfntName name_rec;
+
+ for (FT_UInt i = 0; i < count; i++) {
+ if (FT_Get_Sfnt_Name(face, i, &name_rec) == 0) {
+ // 19 is the pre-defined ID for "Sample text"
+ if (name_rec.name_id == 19) {
+ if (name_rec.platform_id == 3 || name_rec.platform_id == 0) {
+ convert_utf16be_to_utf8(name_rec.string, name_rec.string_len, str, MAX_STRING_LEN);
+ } else {
+ memcpy(str, name_rec.string, name_rec.string_len);
+ str[name_rec.string_len] = '\0';
+ }
+
+ return true;
+ }
+ }
+ }
+ FcLangSet* langset = NULL;
+ FcChar8* lang = NULL;
+ if (FcPatternGetLangSet(font_entry, FC_LANG, 0, &langset) == FcResultMatch) {
+ FcStrSet *lang_set = FcLangSetGetLangs(langset);
+ if (!lang_set)
+ return false;
+ FcStrList *lang_list = FcStrListCreate(lang_set);
+ if (!lang_list) {
+ FcStrListDone(lang_list);
+ return false;
+ }
+
+ while ((lang = FcStrListNext(lang_list))) {
+ if (!lang)
+ continue;
+ if (try_get_sample_by_fc_lang((char*)lang, str))
+ break;
+ }
+
+ FcStrListDone(lang_list);
+ FcStrSetDestroy(lang_set);
+ return true;
+ }
+ return false;
+}
+#endif
+
+#define MAX_TXT_LEN 1024
+VkvgFont sans16 = NULL;
+char txtBuffer[MAX_TXT_LEN];
+int txtBuffPtr = 0;
+
+float uiFontSize = 18.f;
+
+void draw(VkvgSurface surfFont) {
+ if (!font_database)
+ return;
+ VkvgContext ctx = vkvg_create(surfFont);
+ vkvg_clear(ctx);
+
+ float xQ = 10;
+ float yQ = 20;
+
+ vkvg_set_font(ctx,sans16);
+ vkvg_set_source_rgb(ctx,1,1,1);
+
+ vkvg_font_extents_t fe = {0};
+ vkvg_font_extents(ctx, &fe);
+
+ vkvg_rectangle(ctx, xQ-2, yQ - fe.ascent, fe.max_x_advance + MAX_TXT_LEN + 4, fe.height);
+ vkvg_set_line_width(ctx, 1);
+ vkvg_stroke(ctx);
+
+ vkvg_move_to(ctx, xQ, yQ);
+ vkvg_show_text(ctx, txtBuffer);
+
+ char tmp[1024];
+ strcpy(tmp,txtBuffer);
+
+ FcPattern* request = FcPatternCreate();
+ FcObjectSet* requested_elements = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_LANG, (char *)0);
+
+ char *style = strstr(tmp, ":");
+ if (style) {
+ *style='\0';
+ style++;
+ if (strlen(style) > 0) {
+ FcPatternAddString(request, FC_STYLE, (const FcChar8 *)style);
+ } else {
+ style = NULL;
+ }
+ }
+ FcPatternAddString(request, FC_FAMILY, (const FcChar8 *)tmp);
+ FcPatternAddBool(request, FC_SCALABLE, FcTrue);
+ FcPatternAddString(request, FC_FONTFORMAT, (const FcChar8 *)"TrueType");
+
+ //FcConfigSubstitute(config, request, FcMatchPattern);
+ //FcDefaultSubstitute(request);
+
+ FcFontSet* fonts = FcFontList(config, request, requested_elements);
+
+ FcChar8* family_name = NULL;
+ FcChar8* style_variant = NULL;
+ FcChar8* fontFile = NULL;
+ int index = 0;
+
+ float penX = 10;
+ float penY = 50;
+
+ FcPattern* font = NULL;
+ if (fonts && fonts->nfont == 1) {
+ font = fonts->fonts[0];
+
+ FcPatternGetString (font, FC_FILE, 0, &fontFile);
+ FcPatternGetInteger (font, FC_INDEX, 0, &index);
+ FcPatternGetString (font, FC_FAMILY, 0, &family_name);
+ FcPatternGetString (font, FC_STYLE, 0, &style_variant);
+ VkvgFont f = vkvg_font_create_from_file(device, (char*)fontFile, index, fontSize);
+ if (vkvg_font_status(f)) {
+ printf("Error trying to load %s, from %s\n", tmp, fontFile);
+ fflush(stdout);
+ goto clean_exit;
+ }
+ vkvg_set_font (ctx, f);
+
+ penX = 200;
+ penY = 200;
+ sprintf(tmp, "%s : %s", family_name, style_variant);
+
+ vkvg_font_extents_t fe = {0};
+ vkvg_font_extents(ctx, &fe);
+ VkvgText tr = vkvg_text_run_create(ctx, tmp);
+ vkvg_text_extents_t extents = {0};
+ vkvg_text_run_get_extents(tr, &extents);
+ vkvg_rectangle(ctx, penX, penY - fe.ascent, extents.width, extents.height);
+ vkvg_set_line_width(ctx, 1);
+ vkvg_set_source_rgba(ctx,0.5f,0.5f,1, 0.7f);
+ vkvg_stroke(ctx);
+
+ vkvg_move_to (ctx, penX, penY);
+ vkvg_set_source_rgba(ctx,1,0,0,0.5f);
+ vkvg_arc(ctx, penX, penY, 3, 0, 2 * M_PI);
+ vkvg_fill(ctx);
+
+ vkvg_move_to (ctx, penX, penY);
+ vkvg_set_source_rgb(ctx,1,1,1);
+ vkvg_show_text_run(ctx,tr);
+
+ vkvg_flush(ctx);
+ vkvg_text_run_destroy(tr);
+ vkvg_font_destroy(f);
+ } else {
+ for (int i = curFontStartIdx; i < font_database->nfont; i++) {
+ font = font_database->fonts[i];
+ FcPatternGetString (font, FC_FILE, 0, &fontFile);
+ FcPatternGetInteger (font, FC_INDEX, 0, &index);
+ FcPatternGetString (font, FC_FAMILY, 0, &family_name);
+ FcPatternGetString (font, FC_STYLE, 0, &style_variant);
+
+ if (family_name && strstr((char*)family_name, tmp)) {
+ if (!style || (style && style_variant && strstr((char*)style_variant, style))) {
+ sprintf(tmp, "%s:%s", family_name,style_variant);
+ vkvg_move_to(ctx, penX, penY);
+ vkvg_show_text(ctx, tmp);
+ penY+=fe.height;
+
+ if (penY > test_height)
+ break;
+ }
+ }
+
+ }
+ }
+clean_exit:
+ FcPatternDestroy(request);
+ FcFontSetDestroy(fonts);
+ FcObjectSetDestroy(requested_elements);
+
+ vkvg_destroy(ctx);
+}
+
+static void char_callback(GLFWwindow *window, unsigned int codepoint) {
+ if (txtBuffPtr >= MAX_TXT_LEN - 1)
+ return;
+ txtBuffer[txtBuffPtr] = (char)codepoint;
+ txtBuffer[++txtBuffPtr] = '\0';
+ redraw = true;
+}
+static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
+ if (action == GLFW_RELEASE)
+ return;
+ switch (key) {
+ case GLFW_KEY_ESCAPE:
+ glfwSetWindowShouldClose(window, GLFW_TRUE);
+ break;
+ case GLFW_KEY_KP_ADD:
+ fontSize++;
+ break;
+ case GLFW_KEY_KP_SUBTRACT:
+ if (fontSize > 5.f)
+ fontSize--;
+ break;
+ case GLFW_KEY_BACKSPACE:
+ if (txtBuffPtr > 0) {
+ txtBuffPtr--;
+ }
+ txtBuffer[txtBuffPtr]='\0';
+ break;
+ /*case GLFW_KEY_DELETE:
+ if (txtBuffPtr > 0 && txtBuffPtr < MAX_TXT_LEN - 2) {
+ memmove(&txtBuffer[txtBuffPtr], (&txtBuffer[txtBuffPtr + 1], MAX_TXT_LEN - txtB)
+ }
+ break;*/
+ }
+ redraw = true;
+}
+static void mouse_move_callback(GLFWwindow* window, double x, double y) {
+}
+static void scroll_callback(GLFWwindow* window, double x, double y) {
+#ifdef VKVG_USE_FONTCONFIG
+
+ const int step = 5;
+ if (y > 0.f) {
+ if (curFontStartIdx > 0)
+ curFontStartIdx -= step;
+ curFontStartIdx = MAX(curFontStartIdx, 0);
+ } else {
+ curFontStartIdx +=step;
+ }
+#endif
+ redraw = true;
+}
+static void mouse_button_callback(GLFWwindow* window, int but, int state, int modif) {
+ if (but != GLFW_MOUSE_BUTTON_1)
+ return;
+ if (state == GLFW_TRUE)
+ mouseDown = true;
+ else
+ mouseDown = false;
+ redraw = true;
+}
+
+int main(int argc, char* argv[]) {
+ vkh_log_level = VKH_LOG_FULL;
+ vkvg_log_level = VKVG_LOG_ERR | VKVG_LOG_WARN | VKVG_LOG_FONT ;
+
+ _parse_args(argc, argv);
+ VkEngine e = vkengine_create (
+ VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, VK_PRESENT_MODE_FIFO_KHR, test_width, test_height);
+
+ VkhPresenter r = e->renderer;
+ vkengine_set_key_callback(e, key_callback);
+ vkengine_set_mouse_but_callback(e, mouse_button_callback);
+ vkengine_set_cursor_pos_callback(e, mouse_move_callback);
+ vkengine_set_scroll_callback(e, scroll_callback);
+ vkengine_set_char_callback(e, char_callback);
+
+ vkvg_device_create_info_t info = {
+ 1, false,
+ vkh_app_get_inst(e->app),
+ vkh_device_get_phy(e->dev),
+ vkh_device_get_vkdev(e->dev),
+ e->gQFamIdx, 0};
+
+ device = vkvg_device_create(&info);
+ surf = vkvg_surface_create(device, test_width, test_height);
+ VkvgSurface surfFont = vkvg_surface_create(device, test_width, test_height);
+
+ vkh_presenter_build_blit_cmd(r, vkvg_surface_get_vk_image(surf), test_width, test_height);
+
+#ifdef VKVG_USE_FONTCONFIG
+
+ if (!FcInit()) {
+ fprintf(stderr, "Fatal Error: Failed to initialize Fontconfig.\n");
+ return -1;
+ }
+ config = FcConfigGetCurrent();
+ FcPattern* request = FcPatternCreate();
+ FcObjectSet* requested_elements = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_LANG, (char *)0);
+ FcPatternAddBool(request, FC_SCALABLE, FcTrue);
+ FcPatternAddString(request, FC_FONTFORMAT, (const FcChar8 *)"TrueType");
+
+ font_database = FcFontList(config, request, requested_elements);
+#endif
+
+ strcpy (txtBuffer, "sans");
+ txtBuffPtr = 4;
+ sans16 = vkvg_font_create(device, "sans", 16);
+
+ while (!vkengine_should_close(e)) {
+ glfwPollEvents();
+
+ if (redraw) {
+ draw(surfFont);
+ VkvgContext ctx = vkvg_create(surf);
+ vkvg_clear(ctx);
+ vkvg_set_source_surface(ctx,surfFont,0,0);
+ vkvg_paint(ctx);
+ vkvg_destroy(ctx);
+ redraw = false;
+ }
+
+ if (!vkh_presenter_draw(r)) {
+ vkh_presenter_get_size(r, &test_width, &test_height);
+ vkvg_surface_destroy(surf);
+ vkvg_surface_destroy(surfFont);
+ surf = vkvg_surface_create(device, test_width, test_height);
+ surfFont = vkvg_surface_create(device, test_width, test_height);
+ vkh_presenter_build_blit_cmd(r, vkvg_surface_get_vk_image(surf), test_width, test_height);
+ vkengine_wait_idle(e);
+ redraw = true;
+ continue;
+ }
+ }
+ vkengine_wait_idle(e);
+
+ vkvg_surface_destroy(surfFont);
+ vkvg_surface_destroy(surf);
+
+ vkvg_font_destroy(sans16);
+
+ vkvg_device_destroy(device);
+
+ vkengine_destroy(e);
+
+#ifdef VKVG_USE_FONTCONFIG
+ FcFontSetDestroy(font_database);
+ FcObjectSetDestroy(requested_elements);
+ FcPatternDestroy(request);
+ FcFini();
+#endif
+
+ return 0;
+}