diff options
| author | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2026-10-08 16:50:50 +0200 |
|---|---|---|
| committer | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2026-10-08 16:50:50 +0200 |
| commit | c5aeefe4836846fff467a0825d07f0ddefc6975d (patch) | |
| tree | 424e3aa7312b62196580d0d1f8d3bc69697546be | |
| parent | 748bd9b738df2a789811a02c76bacd4855959d65 (diff) | |
LOGE/LOGW;unlikely/likely;debug harfbuzz shaping
| -rw-r--r-- | include/vkvg.h | 2 | ||||
| -rw-r--r-- | src/cross_os.h | 17 | ||||
| -rw-r--r-- | src/vkvg_context.c | 18 | ||||
| -rw-r--r-- | src/vkvg_context_internal.c | 10 | ||||
| -rw-r--r-- | src/vkvg_device.c | 12 | ||||
| -rw-r--r-- | src/vkvg_device_internal.c | 4 | ||||
| -rw-r--r-- | src/vkvg_fonts.c | 289 | ||||
| -rw-r--r-- | src/vkvg_fonts.h | 53 | ||||
| -rw-r--r-- | src/vkvg_internal.h | 30 | ||||
| -rw-r--r-- | src/vkvg_surface.c | 18 | ||||
| -rw-r--r-- | src/vkvg_surface_internal.c | 4 | ||||
| -rw-r--r-- | tests/fonts/allfont.c | 62 | ||||
| -rw-r--r-- | tests/svg-parser-new/vkvg_svg_internal.h | 1 |
13 files changed, 290 insertions, 230 deletions
diff --git a/include/vkvg.h b/include/vkvg.h index 011230a..6bd64ff 100644 --- a/include/vkvg.h +++ b/include/vkvg.h @@ -105,8 +105,8 @@ extern "C" { #define VKVG_LOG_INFO 0x00008000 //(VKVG_LOG_INFO_PTS|VKVG_LOG_INFO_PATH|VKVG_LOG_INFO_CMD|VKVG_LOG_INFO_VAO) -#ifdef DEBUG vkvg_public extern uint32_t vkvg_log_level; +#ifdef DEBUG #ifdef VKVG_WIRED_DEBUG typedef enum { vkvg_wired_debug_mode_normal = 0x01, diff --git a/src/cross_os.h b/src/cross_os.h index bdd3f7a..13cdc8a 100644 --- a/src/cross_os.h +++ b/src/cross_os.h @@ -73,6 +73,23 @@ __pragma(warning(disable : 4505)) #define DIAGNOSTIC_RESTORE_UNUSED #endif +#if defined(__GNUC__) || defined(__clang__) +/* GCC and Clang (Linux, macOS, MinGW) */ +#define vkvg_likely(x) __builtin_expect(!!(x), 1) +#define vkvg_unlikely(x) __builtin_expect(!!(x), 0) +#elif defined(_MSC_VER) +/* MSVC (Windows) */ +/* MSVC does not have a static branch predictor builtin. */ +/* We fall back to standard evaluation. MSVC relies on PGO */ +/* (Profile-Guided Optimization) to sort branches at link time. */ +#define vkvg_likely(x) (!!(x)) +#define vkvg_unlikely(x) (!!(x)) +#else +/* Fallback for any other exotic compiler */ +#define vkvg_likely(x) (!!(x)) +#define vkvg_unlikely(x) (!!(x)) +#endif + // 1. Check if we are in a POSIX environment that supports strncasecmp #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L) #include <strings.h> /* Provides the native strncasecmp */ diff --git a/src/vkvg_context.c b/src/vkvg_context.c index c8d332c..600bd90 100644 --- a/src/vkvg_context.c +++ b/src/vkvg_context.c @@ -81,12 +81,12 @@ void _init_ctx(VkvgContext ctx) { VkvgContext vkvg_create(VkvgSurface surf) { LOG(VKVG_LOG_INFO, "CREATE Context\n"); if (vkvg_surface_status(surf)) { - LOG(VKVG_LOG_ERR, "CREATE Context failed, invalid surface\n"); + LOGE("CREATE Context failed, invalid surface\n"); return (VkvgContext)&_vkvg_status_invalid_surface; } VkvgDevice dev = surf->dev; if (vkvg_device_status(dev)) { - LOG(VKVG_LOG_ERR, "CREATE Context failed, invalid device\n"); + LOGE("CREATE Context failed, invalid device\n"); return (VkvgContext)&_vkvg_status_device_error; } VkvgContext ctx = NULL; @@ -103,7 +103,7 @@ VkvgContext vkvg_create(VkvgSurface surf) { ctx = (vkvg_context*)calloc(1, sizeof(vkvg_context)); if (!ctx) { - LOG(VKVG_LOG_ERR, "CREATE context failed, no memory\n"); + LOGE("CREATE context failed, no memory\n"); return (VkvgContext)&_vkvg_status_no_memory; } @@ -133,7 +133,7 @@ VkvgContext vkvg_create(VkvgSurface surf) { if (ctx->indexCache) free(ctx->indexCache); free(ctx); - LOG(VKVG_LOG_ERR, "CREATE context failed, no memory\n"); + LOGE("CREATE context failed, no memory\n"); return (VkvgContext)&_vkvg_status_no_memory; } @@ -1246,14 +1246,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_init_text_run(ctx, text, -1, tr); + text_run_init(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_init_text_run(ctx, text, length, tr); + text_run_init(ctx, text, length, tr); return tr; } uint32_t vkvg_text_run_get_glyph_count(VkvgText textRun) { return textRun->glyph_count; } @@ -1269,13 +1269,13 @@ void vkvg_text_run_get_glyph_position(VkvgText textRun, uint32_t index, vkvg #endif } void vkvg_text_run_destroy(VkvgText textRun) { - _font_cache_term_text_run(textRun); + text_run_term(textRun); free(textRun); } void vkvg_show_text_run(VkvgContext ctx, VkvgText textRun) { if (vkvg_status(ctx)) return; - _font_cache_show_text_run(ctx, textRun); + text_run_show_text(ctx, textRun); } void vkvg_text_run_get_extents(VkvgText textRun, vkvg_text_extents_t* extents) { *extents = textRun->extents; } @@ -1302,7 +1302,7 @@ void vkvg_save(VkvgContext ctx) { ctx->pSavedCtxs, newSize * sizeof(vkvg_context_save_t)); if (tmp == NULL) { ctx->status = VKVG_STATUS_NO_MEMORY; - LOG(VKVG_LOG_ERR, "resize context save stack failed.\n"); + LOGE("resize context save stack failed.\n"); return; } ctx->pSavedCtxs = tmp; diff --git a/src/vkvg_context_internal.c b/src/vkvg_context_internal.c index effc40b..a8b8853 100644 --- a/src/vkvg_context_internal.c +++ b/src/vkvg_context_internal.c @@ -46,7 +46,7 @@ void _resize_vertex_cache(VkvgContext ctx, uint32_t newSize) { ctx->vertCount, ctx->sizeVertices, newSize, (size_t)newSize * sizeof(Vertex), ctx->vertexCache, tmp); if (tmp == NULL) { ctx->status = VKVG_STATUS_NO_MEMORY; - LOG(VKVG_LOG_ERR, "resize vertex cache failed: vert count: %u byte size: %zu\n", newSize, + LOGE("resize vertex cache failed: vert count: %u byte size: %zu\n", newSize, newSize * sizeof(Vertex)); return; } @@ -60,7 +60,7 @@ void _resize_index_cache(VkvgContext ctx, uint32_t newSize) { ctx->indexCache, tmp); if (tmp == NULL) { ctx->status = VKVG_STATUS_NO_MEMORY; - LOG(VKVG_LOG_ERR, "resize IBO failed: idx count: %u size(byte): %zu\n", newSize, + LOGE("resize IBO failed: idx count: %u size(byte): %zu\n", newSize, (size_t)newSize * sizeof(VKVG_IBO_INDEX_TYPE)); return; } @@ -106,7 +106,7 @@ bool _check_pathes_array(VkvgContext ctx) { LOG(VKVG_LOG_DBG_ARRAYS, "resize PATH: new size: %u Ptr: %p -> %p\n", ctx->sizePathes, ctx->pathes, tmp); if (tmp == NULL) { ctx->status = VKVG_STATUS_NO_MEMORY; - LOG(VKVG_LOG_ERR, "resize PATH failed: new size(byte): %zu\n", ctx->sizePathes * sizeof(uint32_t)); + LOGE("resize PATH failed: new size(byte): %zu\n", ctx->sizePathes * sizeof(uint32_t)); _clear_path(ctx); return true; } @@ -122,7 +122,7 @@ bool _check_point_array(VkvgContext ctx) { LOG(VKVG_LOG_DBG_ARRAYS, "resize Points: new size(point): %u Ptr: %p -> %p\n", ctx->sizePoints, ctx->points, tmp); if (tmp == NULL) { ctx->status = VKVG_STATUS_NO_MEMORY; - LOG(VKVG_LOG_ERR, "resize PATH failed: new size(byte): %zu\n", ctx->sizePoints * sizeof(vec2)); + LOGE("resize PATH failed: new size(byte): %zu\n", ctx->sizePoints * sizeof(vec2)); _clear_path(ctx); return true; } @@ -689,7 +689,7 @@ void _update_cur_pattern(VkvgContext ctx, VkvgPattern pat) { if (lastPat == NULL) // solid return; // solid to solid transition, no extra action requested } else if (pat->status) { - LOG(VKVG_LOG_ERR, "Error: Update pattern fails, pattern status: %d\n", pat->status); + LOGE("Error: Update pattern fails, pattern status: %d\n", pat->status); return; } else newPatternType = pat->type; diff --git a/src/vkvg_device.c b/src/vkvg_device.c index f9ed284..2922df3 100644 --- a/src/vkvg_device.c +++ b/src/vkvg_device.c @@ -224,7 +224,7 @@ vkvg_status_t vkvg_get_required_device_extensions(VkPhysicalDevice phy, const ch #ifdef VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT if (!scalarBlockLayoutSupport.scalarBlockLayout) { - LOG(VKVG_LOG_ERR, "CREATE Device failed, vkvg compiled with VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT and feature is " + LOGE("CREATE Device failed, vkvg compiled with VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT and feature is " "not implemented for physical device.\n"); free(pExtensionProperties); return VKVG_STATUS_DEVICE_ERROR; @@ -233,7 +233,7 @@ vkvg_status_t vkvg_get_required_device_extensions(VkPhysicalDevice phy, const ch #endif #ifdef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE if (!timelineSemaphoreSupport.timelineSemaphore) { - LOG(VKVG_LOG_ERR, "CREATE Device failed, VK_SEMAPHORE_TYPE_TIMELINE not supported.\n"); + LOGE("CREATE Device failed, VK_SEMAPHORE_TYPE_TIMELINE not supported.\n"); free(pExtensionProperties); return VKVG_STATUS_DEVICE_ERROR; } @@ -289,12 +289,12 @@ const void* vkvg_get_device_requirements(VkPhysicalDeviceFeatures* pEnabledFeatu VkvgDevice vkvg_device_create(vkvg_device_create_info_t* info) { LOG(VKVG_LOG_INFO, "CREATE Device\n"); if (!info) { - LOG(VKVG_LOG_ERR, "CREATE Device failed, provided vkvg_device_create_info_t is null\n"); + LOGE("CREATE Device failed, provided vkvg_device_create_info_t is null\n"); return (VkvgDevice)&_vkvg_status_invalid_dev_ci; } VkvgDevice dev = (vkvg_device*)calloc(1, sizeof(vkvg_device)); if (!dev) { - LOG(VKVG_LOG_ERR, "CREATE Device failed, no memory\n"); + LOGE("CREATE Device failed, no memory\n"); return (VkvgDevice)&_vkvg_status_no_memory; } @@ -354,7 +354,7 @@ VkvgDevice vkvg_device_create(vkvg_device_create_info_t* info) { info->samples = VK_SAMPLE_COUNT_1_BIT; if (!(pi->properties.limits.framebufferColorSampleCounts & info->samples)) { - LOG(VKVG_LOG_ERR, "CREATE Device failed: sample count not supported: %d\n", info->samples); + LOGE("CREATE Device failed: sample count not supported: %d\n", info->samples); dev->status = VKVG_STATUS_DEVICE_ERROR; vkh_app_free_phyinfos(phyCount, phys); vkh_app_destroy(app); @@ -405,7 +405,7 @@ VkvgDevice vkvg_device_create(vkvg_device_create_info_t* info) { void vkvg_device_destroy(VkvgDevice dev) { if (vkvg_device_status(dev)) { - LOG(VKVG_LOG_ERR, "DESTROY Device failed, see Status for info"); + LOGE("DESTROY Device failed, see Status for info"); return; } LOCK_DEVICE diff --git a/src/vkvg_device_internal.c b/src/vkvg_device_internal.c index a86715f..497e911 100644 --- a/src/vkvg_device_internal.c +++ b/src/vkvg_device_internal.c @@ -492,7 +492,7 @@ void _device_submit_cmd(VkvgDevice dev, VkCommandBuffer* cmd, VkFence fence) { bool _device_init_function_pointers(VkvgDevice dev) { #if defined(DEBUG) && defined(VKVG_DBG_UTILS) if (vkGetInstanceProcAddr(dev->instance, "vkSetDebugUtilsObjectNameEXT") == VK_NULL_HANDLE) { - LOG(VKVG_LOG_ERR, "vkvg create device failed: 'VK_EXT_debug_utils' has to be loaded for Debug build\n"); + LOGE("vkvg create device failed: 'VK_EXT_debug_utils' has to be loaded for Debug build\n"); return false; } vkh_device_init_debug_utils((VkhDevice)&dev->vkDev); @@ -590,7 +590,7 @@ void _device_check_best_image_tiling(VkvgDevice dev, VkFormat format) { } } dev->status = VKVG_STATUS_INVALID_FORMAT; - LOG(VKVG_LOG_ERR, "vkvg create device failed: image format not supported: %d\n", format); + LOGE("vkvg create device failed: image format not supported: %d\n", format); } void _dump_image_format_properties(VkvgDevice dev, VkFormat format) { diff --git a/src/vkvg_fonts.c b/src/vkvg_fonts.c index 2a48645..80e15a1 100644 --- a/src/vkvg_fonts.c +++ b/src/vkvg_fonts.c @@ -3,7 +3,6 @@ // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) #include "vkvg_fonts.h" #include "vkvg_context_internal.h" -#include "vkvg_surface_internal.h" #include "vkvg_device_internal.h" #include "vkh.h" @@ -27,7 +26,7 @@ void _fonts_cache_create(VkvgDevice dev, const char* fontDirs) { #ifdef VKVG_USE_FONTCONFIG if (!FcInit()) { - LOG(VKVG_LOG_ERR, "Font config initialisation failed\n"); + LOGE("Font config initialisation failed\n"); dev->status = VKVG_STATUS_DEVICE_ERROR; free(cache); return; @@ -35,7 +34,7 @@ void _fonts_cache_create(VkvgDevice dev, const char* fontDirs) { if (fontDirs) { cache->config = FcConfigCreate(); if (!cache->config) { - LOG(VKVG_LOG_ERR, "Failed to create custom Fontconfig configuration.\n"); + LOGE("Failed to create custom Fontconfig configuration.\n"); dev->status = VKVG_STATUS_DEVICE_ERROR; free(cache); return; @@ -58,7 +57,7 @@ void _fonts_cache_create(VkvgDevice dev, const char* fontDirs) { } else { cache->config = FcConfigGetCurrent(); if (!cache->config) { - LOG(VKVG_LOG_ERR, + LOGE( "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; @@ -265,19 +264,26 @@ void _init_next_line_in_tex_cache(VkvgDevice dev, vkvg_font_t* f) { void _font_buffer_destroy (vkvg_font_face_t* face) { array_destroy_VkvgFont(&face->sizes); array_destroy_uint64_t(&face->queryHashes); +#ifdef VKVG_USE_FREETYPE FT_Done_Face(face->face); mtx_destroy(&face->mutex); +#endif } void _font_face_destroy (FontFace face) { array_destroy_VkvgFont(&face->sizes); array_destroy_uint64_t(&face->queryHashes); - mtx_destroy(&face->mutex); - array_del_FontFace(&face->fontBuffer->faces, face); + +#ifdef VKVG_USE_FREETYPE + mtx_destroy(&face->mutex); if (face->fontBuffer->faces.count == 0) { LOG(VKVG_LOG_FONT, "Done face: %s\n", face->face->family_name); + #ifdef VKVG_USE_HARFBUZZ + hb_font_destroy(face->hb_font); + #endif FT_Done_Face(face->face); } +#endif free(face); } void _font_cache_destroy(VkvgDevice dev) { @@ -348,21 +354,29 @@ 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) { - VkvgFont f = tr->font; + +glyph_ref _put_glyph_in_cache(VkvgDevice dev, VkvgFont font, uint32_t gindex) { #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_Error res = FT_Load_Glyph(f->face->face, gindex, FT_LOAD_RENDER); + FT_Error res = FT_Load_Glyph(font->face->face, gindex, FT_LOAD_RENDER); if (res) { - LOG(VKVG_LOG_WARN, "FT_Load_Glyph error 0x%x for glyph index %d\n", res, gindex); - FT_Load_Glyph(f->face->face, 0, FT_LOAD_RENDER); + LOGW("FT_Load_Glyph error 0x%x for glyph index %d\n", res, gindex); + if (font->charLookup[0].texRef) // return default glyph ref 0 + return font->charLookup[0]; + // try upload default glyph 0 in cache + res = FT_Load_Glyph(font->face->face, 0, FT_LOAD_RENDER); + if (res) { + LOGE("Fatal: FT_Load_Glyph error 0x%x for default glyph 0\n", res); + font->status = VKVG_STATUS_INVALID_FONT; + return (glyph_ref) {0}; + } } #endif - FT_GlyphSlot slot = f->face->face->glyph; + FT_GlyphSlot slot = font->face->face->glyph; FT_Bitmap bmp = slot->bitmap; uint32_t bmpByteWidth = bmp.width; uint32_t bmpPixelWidth = bmp.width; @@ -373,7 +387,7 @@ char_ref _prepare_char(VkvgDevice dev, VkvgText tr, uint32_t gindex) { bmpPixelWidth /= 3; #endif #else - stbtt_fontinfo* pStbInfo = &tr->fontId->stbInfo; + stbtt_fontinfo* pStbInfo = &tr->font->face->stbInfo; int c_x1, c_y1, c_x2, c_y2; stbtt_GetGlyphBitmapBox(pStbInfo, gindex, f->scale, f->scale, &c_x1, &c_y1, &c_x2, &c_y2); uint32_t bmpByteWidth = c_x2 - c_x1; @@ -381,12 +395,12 @@ char_ref _prepare_char(VkvgDevice dev, VkvgText tr, uint32_t gindex) { uint32_t bmpRows = c_y2 - c_y1; #endif uint8_t* data = dev->fontCache->hostBuff; - if (dev->fontCache->stagingX + f->penX + bmpPixelWidth > FONT_PAGE_SIZE) { - _flush_chars_to_tex(dev, f); - _init_next_line_in_tex_cache(dev, f); + if (dev->fontCache->stagingX + font->penX + bmpPixelWidth > FONT_PAGE_SIZE) { + _flush_chars_to_tex(dev, font); + _init_next_line_in_tex_cache(dev, font); } - //cr.texRefIdx = f->texLines.count - 1; - char_ref cr = {f->texLines.elements[f->texLines.count - 1]}; + + glyph_ref cr = {font->texLines.elements[font->texLines.count - 1]}; int penX = dev->fontCache->stagingX; #ifdef VKVG_USE_FREETYPE @@ -418,64 +432,16 @@ char_ref _prepare_char(VkvgDevice dev, VkvgText tr, uint32_t gindex) { cr.bmpDiff.y = (int16_t)-c_y1; cr.advance = (vec2){(uint32_t)roundf(f->scale * advance) << 6, 0}; #endif - vec4 uvBounds = {{(float)(penX + f->penX) / (float)FONT_PAGE_SIZE}, + vec4 uvBounds = {{(float)(penX + font->penX) / (float)FONT_PAGE_SIZE}, {0.f}, {(float)bmpPixelWidth}, {(float)bmpRows}}; cr.bounds = uvBounds; - - f->charLookup[gindex] = cr; dev->fontCache->stagingX += bmpPixelWidth; return cr; } -/*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); - - 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*); - } - - - } - 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, FontFace *const face) { +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; for (int i = 0; i < cache->fontBuffers.count; ++i) { @@ -492,7 +458,7 @@ bool tryGetFontFaceFromQueryHash(_font_cache_t* cache, uint64_t queryHash, FontF } return false; } -bool tryGetFontBufferFromPathHash(_font_cache_t* cache, uint64_t pathHash, FontBuffer *const fontBuff) { +bool try_get_font_buffer_from_path_hash(_font_cache_t* cache, uint64_t pathHash, FontBuffer *const fontBuff) { *fontBuff = NULL; for (int i = 0; i < cache->fontBuffers.count; ++i) { if (cache->fontBuffers.elements[i]->fontPathHash == pathHash) { @@ -503,25 +469,28 @@ bool tryGetFontBufferFromPathHash(_font_cache_t* cache, uint64_t pathHash, FontB } return false; } - -#ifdef VKVG_USE_FONTCONFIG -bool _tryResolveFontNameWithFontConfig(VkvgDevice dev, const char* fontString, FontFace *const face) { +bool try_get_or_create_font_face_from_query (VkvgDevice dev, const char* queryString, FontFace *const face) { _font_cache_t* cache = dev->fontCache; - uint64_t queryHash = fnv1a_64_str(fontString); + uint64_t queryHash = fnv1a_64_str(queryString); *face = NULL; - if (tryGetFontFaceFromQueryHash(cache, queryHash, face)) + if (try_get_font_face_from_query_hash(cache, queryHash, face)) return true; char *fontFile = NULL; + FontBuffer buffPtr = NULL; + +#ifdef VKVG_USE_FONTCONFIG + FcPattern* request = FcNameParse((const FcChar8*)queryString); + + FcPatternAddBool(request, FC_SCALABLE, FcTrue); + FcPatternAddString(request, FC_FONTFORMAT, (const FcChar8 *)"TrueType"); - 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; @@ -529,7 +498,7 @@ bool _tryResolveFontNameWithFontConfig(VkvgDevice dev, const char* fontString, F FcPatternGetInteger (font, FC_INDEX, 0, &index); uint64_t fontFileHash = fnv1a_64_str(fontFile); - if (tryGetFontBufferFromPathHash(cache, fontFileHash, &buffPtr)) { + if (try_get_font_buffer_from_path_hash(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]; @@ -539,26 +508,30 @@ bool _tryResolveFontNameWithFontConfig(VkvgDevice dev, const char* fontString, F } LOG(VKVG_LOG_FONT, "Font buffer without a face\n"); } else { - buffPtr = (FontBuffer)calloc(1, sizeof(vkvg_font_buffer_t)); - buffPtr->fontPathHash = fontFileHash; FILE *file = fopen(fontFile, "rb"); if (!file) { - LOG(VKVG_LOG_ERR, "Error opening font file: %s\n", fontFile); + LOGE("Error opening font file: %s\n", fontFile); return false; } + buffPtr = (FontBuffer)calloc(1, sizeof(vkvg_font_buffer_t)); + buffPtr->fontPathHash = fontFileHash; + fseek(file, 0, SEEK_END); buffPtr->bufferSize = ftell(file); fseek(file, 0, SEEK_SET); buffPtr->buffer = malloc(buffPtr->bufferSize); + if (!buffPtr->buffer) { - LOG(VKVG_LOG_ERR, "Error opening font file: %s\n", fontFile); + LOGE("Error opening font file: %s\n", fontFile); fclose(file); + free(buffPtr); return false; } unsigned long readBytes = fread(buffPtr->buffer, 1, buffPtr->bufferSize, file); fclose(file); if (readBytes != buffPtr->bufferSize) { - LOG(VKVG_LOG_ERR, "Error reading font file: %s\n", fontFile); + LOGE("Error reading font file: %s\n", fontFile); + free(buffPtr); return false; } @@ -573,16 +546,14 @@ bool _tryResolveFontNameWithFontConfig(VkvgDevice dev, const char* fontString, F mtx_init(&newFace->mutex, mtx_plain); #ifdef VKVG_USE_FREETYPE - /*if (cache->fontBuffers.count > 654 && cache->fontBuffers.elements[654] == buffPtr && buffPtr->faces.count > 0) { - printf("= FontBuffer 654: face count = %d\n", buffPtr->faces.count); - fflush(stdout); - }*/ - FT_Error res = FT_New_Memory_Face(cache->library, buffPtr->buffer, buffPtr->bufferSize, index, &newFace->face); if (res) { - LOG(VKVG_LOG_ERR, "FreeType error 0x%x creating memory face in %s at line %d\n", res, __FILE__, __LINE__); + LOGE("FreeType error 0x%x creating memory face in %s at line %d\n", res, __FILE__, __LINE__); return false; } +#ifdef VKVG_USE_HARFBUZZ + newFace->hb_font = hb_ft_font_create(newFace->face, NULL); +#endif #else int result = stbtt_InitFont(&font->stbInfo, font->fontBuffer, 0); assert(result && "stbtt_initFont failed"); @@ -602,53 +573,69 @@ bool _tryResolveFontNameWithFontConfig(VkvgDevice dev, const char* fontString, F array_add_FontFace(&buffPtr->faces, newFace); array_add_uint64_t(&newFace->queryHashes, queryHash); *face = newFace; + FcPatternDestroy(request); return true; } +#endif 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(VkvgFont font, const char* text, int length) { hb_buffer_t* buf = hb_buffer_create(); + hb_buffer_add_utf8 (buf, text, length, 0, length); + hb_buffer_guess_segment_properties (buf); + hb_shape(font->face->hb_font, buf, NULL, 0); - hb_script_t script = HB_SCRIPT_LATIN; - hb_unicode_funcs_t* ucfunc = hb_unicode_funcs_get_default(); - 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); - + if (hb_buffer_get_content_type(buf) != HB_BUFFER_CONTENT_TYPE_GLYPHS) { + LOGW("HarfBuzz shaping failed: Buffer was not converted to glyphs."); + } return buf; } #endif +void _font_lock_size (VkvgContext ctx, VkvgFont font) { +#ifdef VKVG_USE_FREETYPE + FT_Face face = font->face->face; + if (face->size != font->ftSize) { + if (ctx->dev->threadAware) + mtx_lock(&font->face->mutex); + FT_CHECK_RESULT(FT_Activate_Size(font->ftSize)); +#ifdef VKVG_USE_HARFBUZZ + hb_ft_font_changed(font->face->hb_font); +#endif + } +#endif +} // 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) { - /*_update_current_font(ctx); - - if (ctx->status) - return; // TODO: ensure correct metrics are returned (scalled/unscalled, etc..) - vkvg_font_t* font = ctx->currentFontSize; + VkvgFont font = ctx->currentFont; #ifdef VKVG_USE_FREETYPE - FT_BBox* bbox = &font->face->bbox; - FT_Size_Metrics* metrics = &font->face->size->metrics; + FT_Face face = font->face->face; + /*_font_lock_size(ctx, font); + if (face->size != font->ftSize) { + mtx_lock(&font->face->mutex); + FT_CHECK_RESULT(FT_Activate_Size(font->ftSize)); + }*/ + //FT_BBox* bbox = &face->bbox; + //FT_Size_Metrics* metrics = &face->size->metrics; + FT_Size_Metrics* metrics = &font->ftSize->metrics; + + extents->ascent = (float)(FT_MulFix(face->ascender, metrics->y_scale) >> 6); // metrics->ascender >> 6; + extents->descent = -(float)(FT_MulFix(face->descender, metrics->y_scale) >> 6); // metrics->descender >> 6; + extents->height = (float)(FT_MulFix(face->height, metrics->y_scale) >> 6); // metrics->height >> 6; + /*extents->max_x_advance = (float)(bbox->xMax >> 6); + extents->max_y_advance = (float)(bbox->yMax >> 6);*/ + extents->max_x_advance = (float)(FT_MulFix(face->max_advance_width, metrics->x_scale) >> 6); + extents->max_y_advance = (float)(FT_MulFix(face->max_advance_height, metrics->y_scale) >> 6); - extents->ascent = (float)(FT_MulFix(font->face->ascender, metrics->y_scale) >> 6); // metrics->ascender >> 6; - extents->descent = -(float)(FT_MulFix(font->face->descender, metrics->y_scale) >> 6); // metrics->descender >> 6; - extents->height = (float)(FT_MulFix(font->face->height, metrics->y_scale) >> 6); // metrics->height >> 6; - extents->max_x_advance = (float)(bbox->xMax >> 6); - extents->max_y_advance = (float)(bbox->yMax >> 6); + /*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); @@ -656,7 +643,7 @@ void _font_cache_font_extents(VkvgContext ctx, vkvg_font_extents_t* extents) { roundf(font->scale * (ctx->currentFont->ascent - ctx->currentFont->descent + ctx->currentFont->lineGap)); extents->max_x_advance = 0; // TODO extents->max_y_advance = 0; -#endif*/ +#endif } // compute text extends for provided string. void _font_cache_text_extents(VkvgContext ctx, const char* text, int length, vkvg_text_extents_t* extents) { @@ -666,23 +653,23 @@ void _font_cache_text_extents(VkvgContext ctx, const char* text, int length, vkv } vkvg_text_run_t tr = {0}; - _font_cache_init_text_run(ctx, text, length, &tr); + text_run_init(ctx, text, length, &tr); if (ctx->status) return; *extents = tr.extents; - _font_cache_term_text_run(&tr); + text_run_term(&tr); } // text is expected as utf8 encoded // if length is < 0, text must be null terminated, else it contains glyph count -void _font_cache_init_text_run(VkvgContext ctx, const char* text, int length, VkvgText textRun) { +void text_run_init(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) + _font_lock_size(ctx, textRun->font); #ifdef VKVG_USE_HARFBUZZ textRun->hbBuf = _get_hb_buffer(textRun->font, text, length); @@ -717,7 +704,8 @@ void _font_cache_init_text_run(VkvgContext ctx, const char* text, int length, Vk free(tmp); #endif - UNLOCK_FONTCACHE(ctx->dev) + if (ctx->dev->threadAware) + mtx_unlock(&textRun->font->face->mutex); unsigned int string_width_in_pixels = 0; for (uint32_t i = 0; i < textRun->glyph_count; ++i) @@ -738,7 +726,7 @@ void _font_cache_init_text_run(VkvgContext ctx, const char* text, int length, Vk textRun->extents.width = textRun->extents.x_advance; } -void _font_cache_term_text_run(VkvgText textRun) { +void text_run_term(VkvgText textRun) { #ifdef VKVG_USE_HARFBUZZ hb_buffer_destroy(textRun->hbBuf); #else @@ -747,6 +735,7 @@ void _font_cache_term_text_run(VkvgText textRun) { #endif vkvg_font_destroy(textRun->font); } + #ifdef DEBUG void _show_texture(vkvg_context* ctx) { Vertex vs[] = {{{0, 0}, 0, {0, 0, 0}}, @@ -764,7 +753,21 @@ void _show_texture(vkvg_context* ctx) { _add_tri_indices_for_rect(ctx, firstIdx); } #endif -void _font_cache_show_text_run(VkvgContext ctx, VkvgText tr) { + +vkvg_inline glyph_ref get_cached_glyph (VkvgDevice dev, VkvgFont font, uint32_t gIndex) { + glyph_ref gr; + if (vkvg_unlikely (gIndex > font->face->face->num_glyphs)) { + LOGW("glyph index out of bounds: idx:%d glyphsCount: %ld\n", gIndex, font->face->face->num_glyphs); + if (!font->charLookup[0].texRef) // load default glyph 0 + font->charLookup[0] = _put_glyph_in_cache(dev, font, 0); + return font->charLookup[0]; // set default glyph for index + } + if (!font->charLookup[gIndex].texRef) + font->charLookup[gIndex] = _put_glyph_in_cache(dev, font, gIndex); + return font->charLookup[gIndex]; +} + +void text_run_show_text(VkvgContext ctx, VkvgText tr) { unsigned int glyph_count; #ifdef VKVG_USE_HARFBUZZ hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(tr->hbBuf, &glyph_count); @@ -772,9 +775,7 @@ void _font_cache_show_text_run(VkvgContext ctx, VkvgText tr) { vkvg_glyph_info_t* glyph_info = tr->glyphs; glyph_count = tr->glyph_count; #endif - VkvgFont f = tr->font; - Vertex v = {{0}, ctx->curColor, {0, 0, -1}}; vec2 pen = {0, 0}; @@ -784,12 +785,10 @@ 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]; + glyph_ref cr = get_cached_glyph(tr->dev, f, glyph_info[i].codepoint); -#ifdef VKVG_USE_HARFBUZZ - if (!cr.texRef) - cr = _prepare_char(tr->dev, tr, glyph_info[i].codepoint); -#endif + if (vkvg_unlikely (f->status)) + break; float uvWidth = cr.bounds.width / (float)FONT_PAGE_SIZE; float uvHeight = cr.bounds.height / (float)FONT_PAGE_SIZE; @@ -841,14 +840,14 @@ 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_init_text_run(ctx, text, -1, &tr); + text_run_init(ctx, text, -1, &tr); if (ctx->status) return; - _font_cache_show_text_run(ctx, &tr); + text_run_show_text(ctx, &tr); - _font_cache_term_text_run(&tr); + text_run_term(&tr); //_show_texture(ctx); return; } @@ -909,6 +908,7 @@ void _clear_cache() { void* vkvg_font_get_face(VkvgFont font) { return font->face->face; } + #ifdef VKVG_USE_FREETYPE #define CHECK_FACE_FLAG(flags, flag, buffer) \ do { \ @@ -918,10 +918,10 @@ do { \ } \ } while (0) - /** - * Converts face_flags bitmask to a human-readable string. - * Make sure the output buffer is large enough (e.g., 512 bytes). - */ +/** + * Converts face_flags bitmask to a human-readable string. + * Make sure the output buffer is large enough (e.g., 512 bytes). + */ void ft_face_flags_to_string(FT_Long face_flags, char* out_str, size_t max_len) { if (!out_str || max_len == 0) return; out_str[0] = '\0'; @@ -951,7 +951,7 @@ void ft_face_flags_to_string(FT_Long face_flags, char* out_str, size_t max_len) if (res) { \ char tmp[1024]; \ ft_face_flags_to_string (face->face->face_flags, tmp, 1024); \ - LOG(VKVG_LOG_ERR, "FreeType error is 0x%x in %s at line %d, flags: %s\n", res, __FILE__, __LINE__, tmp);\ + LOGE("FreeType error is 0x%x in %s at line %d, flags: %s\n", res, __FILE__, __LINE__, tmp);\ free(font); \ return (VkvgFont)&_vkvg_status_null_pointer; \ } \ @@ -962,7 +962,7 @@ 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_or_create_font_face_from_query(dev, query, &face)) { if (_try_get_font_size(face, pt26_6, &font)) { vkvg_font_reference(font); return font; @@ -973,6 +973,7 @@ VkvgFont vkvg_font_create (VkvgDevice dev, const char* query, float pointSize) { font = (vkvg_font_t*)calloc(1, sizeof(vkvg_font_t)); if (!font) { + LOGE("Create font failed, no memory\n"); return (VkvgFont)&_vkvg_status_no_memory; } @@ -983,7 +984,7 @@ VkvgFont vkvg_font_create (VkvgDevice dev, const char* query, float pointSize) { if (!(face->face->face_flags & FT_FACE_FLAG_SCALABLE) || !(face->face->face_flags & FT_FACE_FLAG_SFNT)) { char tmp[1024]; ft_face_flags_to_string (face->face->face_flags, tmp, 1024); - LOG(VKVG_LOG_WARN, "Unsuported font flags: %s\n", tmp); + LOGW("Unsuported font flags: %s\n", tmp); free(font); return (VkvgFont)&_vkvg_status_null_pointer; } else { @@ -999,7 +1000,7 @@ VkvgFont vkvg_font_create (VkvgDevice dev, const char* query, float pointSize) { FT_CHECK_RES(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)); + font->charLookup = (glyph_ref*)calloc(face->face->num_glyphs, sizeof(glyph_ref)); if (FT_IS_SCALABLE(face->face)) font->height = face->face->size->metrics.height >> 6; @@ -1017,9 +1018,6 @@ VkvgFont vkvg_font_create (VkvgDevice dev, const char* query, float pointSize) { 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 array_add_VkvgFont(&face->sizes, font); _init_next_line_in_tex_cache(dev, font); @@ -1048,9 +1046,6 @@ void vkvg_font_destroy(VkvgFont font) { #ifdef VKVG_USE_FREETYPE FT_Done_Size(font->ftSize); #endif -#ifdef VKVG_USE_HARFBUZZ - hb_font_destroy(font->hb_font); -#endif for (int i = 0; i < font->texLines.count; ++i) { font->texLines.elements[i]->released = true; diff --git a/src/vkvg_fonts.h b/src/vkvg_fonts.h index 43a4604..453f219 100644 --- a/src/vkvg_fonts.h +++ b/src/vkvg_fonts.h @@ -60,13 +60,11 @@ static const uint8_t utf8d[] = { 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 + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 }; /** @@ -86,6 +84,15 @@ static inline uint32_t decode_utf8_byte(uint32_t *const restrict state, uint32_t *state = utf8d[256 + *state * 16 + type]; return *state; } +uint32_t inline decode(uint32_t* state, uint32_t* codep, uint32_t byte) { + uint32_t type = utf8d[byte]; + *codep = (*state != UTF8_ACCEPT) ? + (byte & 0x3fu) | (*codep << 6) : + (0xff >> 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] = { @@ -156,7 +163,7 @@ typedef struct _char_ref { #else vec2 advance; #endif -} char_ref; +} glyph_ref; // Current location in font cache texture array for new character addition. Each font holds such structure to locate // where to upload new chars. @@ -194,6 +201,9 @@ typedef struct _vkvg_font_face_t{ FT_Face face; /* FreeType face*/ mtx_t mutex; /* Only one font size at a time may use this face */ #endif +#ifdef VKVG_USE_HARFBUZZ + hb_font_t* hb_font; /* HarfBuzz font instance*/ +#endif array_VkvgFont sizes; /* loaded font size array */ #ifndef VKVG_USE_FREETYPE @@ -234,10 +244,7 @@ typedef struct _vkvg_font_t { 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*/ + glyph_ref* charLookup;/* Lookup table of characteres in cache, if not found, upload is queued*/ array_TexRef texLines; /* texture reference where to add new glyph bmp's in cache */ int penX; /* Current X in cache for next char addition */ float height; /* Height in pixel */ @@ -293,24 +300,16 @@ typedef struct _vkvg_text_run_t { #endif } vkvg_text_run_t; -// Create font cache. + void _fonts_cache_create(VkvgDevice dev, const char *fontDirs); -// Release all ressources of font cache. -void _font_cache_destroy(VkvgDevice dev); -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_destroy(VkvgDevice dev); +bool _font_cache_load_font_file_in_memory(vkvg_font_face_t* fontId); void _font_cache_show_text(VkvgContext ctx, const char* text); -// Get text dimmensions void _font_cache_text_extents(VkvgContext ctx, const char* text, int length, vkvg_text_extents_t* extents); -// 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_init_text_run(VkvgContext ctx, const char* text, int length, VkvgText textRun); -// Release ressources held by a text run. -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 void _font_cache_update_context_descset(VkvgContext ctx); + +void text_run_init(VkvgContext ctx, const char* text, int length, VkvgText textRun); +void text_run_term(VkvgText textRun); +void text_run_show_text(VkvgContext ctx, VkvgText tr); // Draw text run #endif diff --git a/src/vkvg_internal.h b/src/vkvg_internal.h index 8cc3db2..ff592f0 100644 --- a/src/vkvg_internal.h +++ b/src/vkvg_internal.h @@ -30,6 +30,8 @@ #endif #include <assert.h> +#include <stdio.h> // needed before stdarg.h on Windows +#include <stdatomic.h> #include "vkh.h" #include "vkvg.h" @@ -37,9 +39,10 @@ #include <float.h> #include <stdbool.h> #include <stdint.h> -#include <stdio.h> // needed before stdarg.h on Windows + +#include <stdlib.h> #include <stdarg.h> -#include <stdatomic.h> +#include <string.h> // should be supported by c11 // #include <threads.h> @@ -81,6 +84,14 @@ static inline uint64_t fnv1a_64_str(const char *const restrict str) { #define M_2_PI 0.63661977236758134308 // 2/pi #endif*/ +// 1. Define ANSI Color Codes +#define CLR_RESET "\x1b[0m" +#define CLR_RED "\x1b[31m" +#define CLR_GREEN "\x1b[32m" +#define CLR_YELLOW "\x1b[33m" +#define CLR_BLUE "\x1b[34m" + + /*#ifdef DEBUG #define LOG(level, ...) \ { \ @@ -121,6 +132,21 @@ static inline uint64_t fnv1a_64_str(const char *const restrict str) { #include "deps/tinycthread.h" #include "cross_os.h" + +#ifdef LOG +#undef LOG +#endif + +#define DEBUG_LOG + +#ifdef DEBUG_LOG +#define LOG(level, ...) { if (vkvg_unlikely(vkvg_log_level & level)) { fprintf(stdout, CLR_RESET "[VKVG] " __VA_ARGS__); fflush(stdout); }} +#else +#define LOG +#endif +#define LOGE(...) { fprintf(stdout, CLR_RED "[VKVG] " __VA_ARGS__); fflush(stdout); } +#define LOGW(...) { fprintf(stdout, CLR_YELLOW "[VKVG] " __VA_ARGS__); fflush(stdout); } + // width of the stencil buffer will determine the number of context saving/restore layers // the two first bits of the stencil are the FILL and the CLIP bits, all other bits are // used to store clipping bit on context saving. 8 bit stencil will allow 6 save/restore layer diff --git a/src/vkvg_surface.c b/src/vkvg_surface.c index f99a061..8a7c728 100644 --- a/src/vkvg_surface.c +++ b/src/vkvg_surface.c @@ -186,7 +186,7 @@ VkvgSurface vkvg_surface_create_from_image(VkvgDevice dev, const char* filePath) int w = 0, h = 0, channels = 0; unsigned char* img = stbi_load(filePath, &w, &h, &channels, 4); // force 4 components per pixel if (!img) { - LOG(VKVG_LOG_ERR, "Could not load texture from %s, %s\n", filePath, stbi_failure_reason()); + LOGE("Could not load texture from %s, %s\n", filePath, stbi_failure_reason()); return (VkvgSurface)&_vkvg_status_null_pointer; } @@ -199,11 +199,11 @@ VkvgSurface vkvg_surface_create_from_image(VkvgDevice dev, const char* filePath) void vkvg_surface_destroy(VkvgSurface surf) { if (vkvg_surface_status(surf)) { - LOG(VKVG_LOG_ERR, "DESTROY surface failed, invalid surface\n"); + LOGE("DESTROY surface failed, invalid surface\n"); return; } if (vkvg_device_status(surf->dev)) { - LOG(VKVG_LOG_ERR, "DESTROY surface failed, device error\n"); + LOGE("DESTROY surface failed, device error\n"); return; } @@ -286,20 +286,20 @@ uint32_t vkvg_surface_get_height(VkvgSurface surf) { vkvg_status_t vkvg_surface_write_to_png(VkvgSurface surf, const char* path) { if (vkvg_surface_status(surf)) { - LOG(VKVG_LOG_ERR, "vkvg_surface_write_to_png failed, invalid status: %d\n", vkvg_surface_status(surf)); + LOGE("vkvg_surface_write_to_png failed, invalid status: %d\n", vkvg_surface_status(surf)); return VKVG_STATUS_INVALID_STATUS; } if (vkvg_device_status(surf->dev)) { - LOG(VKVG_LOG_ERR, "vkvg_surface_write_to_png failed, invalid device status: %d\n", + LOGE("vkvg_surface_write_to_png failed, invalid device status: %d\n", vkvg_device_status(surf->dev)); return VKVG_STATUS_INVALID_STATUS; } if (surf->dev->pngStagFormat == VK_FORMAT_UNDEFINED) { - LOG(VKVG_LOG_ERR, "no suitable image format for png write\n"); + LOGE("no suitable image format for png write\n"); return VKVG_STATUS_INVALID_FORMAT; } if (!path) { - LOG(VKVG_LOG_ERR, "vkvg_surface_write_to_png failed, null path\n"); + LOGE("vkvg_surface_write_to_png failed, null path\n"); return VKVG_STATUS_WRITE_ERROR; } LOCK_SURFACE(surf) @@ -397,11 +397,11 @@ vkvg_status_t vkvg_surface_write_to_png(VkvgSurface surf, const char* path) { vkvg_status_t vkvg_surface_write_to_memory(VkvgSurface surf, unsigned char* const bitmap) { if (vkvg_surface_status(surf)) { - LOG(VKVG_LOG_ERR, "vkvg_surface_write_to_memory failed, invalid status: %d\n", vkvg_surface_status(surf)); + LOGE("vkvg_surface_write_to_memory failed, invalid status: %d\n", vkvg_surface_status(surf)); return VKVG_STATUS_INVALID_STATUS; } if (!bitmap) { - LOG(VKVG_LOG_ERR, "vkvg_surface_write_to_memory failed, null path\n"); + LOGE("vkvg_surface_write_to_memory failed, null path\n"); return VKVG_STATUS_WRITE_ERROR; } diff --git a/src/vkvg_surface_internal.c b/src/vkvg_surface_internal.c index 537a97c..8dcddac 100644 --- a/src/vkvg_surface_internal.c +++ b/src/vkvg_surface_internal.c @@ -214,13 +214,13 @@ void _create_surface_images(VkvgSurface surf) { VkvgSurface _create_surface(VkvgDevice dev, VkFormat format) { LOG(VKVG_LOG_INFO, "CREATE Surface\n"); if (vkvg_device_status(dev)) { - LOG(VKVG_LOG_ERR, "CREATE Surface failed, invalid Device\n"); + LOGE("CREATE Surface failed, invalid Device\n"); return (VkvgSurface)&_vkvg_status_device_error; } VkvgSurface surf = (vkvg_surface*)calloc(1, sizeof(vkvg_surface)); if (!surf) { - LOG(VKVG_LOG_ERR, "CREATE Surface failed, no memory\n"); + LOGE("CREATE Surface failed, no memory\n"); return (VkvgSurface)&_vkvg_status_no_memory; } diff --git a/tests/fonts/allfont.c b/tests/fonts/allfont.c index b71b2a4..c858f8b 100644 --- a/tests/fonts/allfont.c +++ b/tests/fonts/allfont.c @@ -7,7 +7,7 @@ #include FT_TRUETYPE_IDS_H #include FT_MULTIPLE_MASTERS_H -float fontSize = 16.0f; +float fontSize = 32.0f; int curFontStartIdx = 0; bool redraw = true; @@ -31,7 +31,7 @@ static const LocaleSample script_samples[] = { { "he", "דג אכל גזר באשחט" }, // Hebrew { "hi", "ऋषियों को तपस्या करते देख, दुष्टों के मन में डर पैदा हुआ।" }, // Hindi / Devanagari { "el", "διαφυλάξτε γενικά τη ζωή σας από τον φόβο" }, // Greek - { "th", "เป็นมนุษย์สุดประเสริฐเลิศคุณค่า" }, // Thai + { "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) @@ -62,7 +62,7 @@ static const LocaleSample script_samples[] = { { "mn-cn", "Хүн бүр төрөхDiscussion эрх чөлөөтэй, нэр төр, эрхийн хувьд адилхан." }, // Mongolian - Inner Mongolia (Cyrillic fallback) }; -void get_sample_by_fc_lang(const char* fc_lang, char* str) { +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++) { @@ -73,12 +73,12 @@ void get_sample_by_fc_lang(const char* fc_lang, char* str) { } if (!sample) { sample = script_samples[0].sample_text; - printf("missing lang sample: %s\n", fc_lang); + return false; } } - // Check if the fontconfig language string starts with a known code strncpy(str, sample, strlen(sample)+1); + return true; } @@ -143,12 +143,12 @@ bool try_get_sample_string(FcPattern* font_entry, FT_Face face, char* str) { if (!lang_list) return false; - lang = FcStrListNext(lang_list); - - if (!lang) - return false; - //printf("lang: %s\n", str); - get_sample_by_fc_lang((char*)lang, str); + while ((lang = FcStrListNext(lang_list))) { + if (!lang) + continue; + if (try_get_sample_by_fc_lang((char*)lang, str)) + break; + } FcStrListDone(lang_list); return true; @@ -162,7 +162,6 @@ void draw(VkvgSurface surfFont) { return; VkvgContext ctx = vkvg_create(surfFont); vkvg_clear(ctx); - vkvg_set_source_rgb(ctx,1,1,1); float penX = 10.f; float penY = 50.f; @@ -190,13 +189,30 @@ void draw(VkvgSurface surfFont) { continue; } vkvg_set_font (ctx, font); - vkvg_move_to (ctx, penX, penY); + FT_Face face = (FT_Face)vkvg_font_get_face(font); if (!try_get_sample_string(font_entry, face, tmp)) sprintf(tmp, "%s : %s", family_name, style_variant); - vkvg_show_text (ctx,tmp); + 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_font_destroy(font); @@ -229,12 +245,14 @@ 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) { + const int step = 5; if (y > 0.f) { if (curFontStartIdx > 0) - curFontStartIdx --; + curFontStartIdx -= 5; + curFontStartIdx = MAX(curFontStartIdx, 0); } else if (font_database) { if (curFontStartIdx < font_database->nfont) - curFontStartIdx ++; + curFontStartIdx +=5; } redraw = true; } @@ -249,7 +267,8 @@ static void mouse_button_callback(GLFWwindow* window, int but, int state, int mo } int main(int argc, char* argv[]) { - vkh_log_level = VKVG_LOG_ERR | VKVG_LOG_WARN | VKVG_LOG_FONT; + 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 ( @@ -279,9 +298,12 @@ int main(int argc, char* argv[]) { return -1; } FcConfig* config = FcConfigGetCurrent(); - FcPattern* blank_pattern = FcPatternCreate(); + FcPattern* request = FcPatternCreate(); FcObjectSet* requested_elements = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_LANG, (char *)0); - font_database = FcFontList(config, blank_pattern, requested_elements); + FcPatternAddBool(request, FC_SCALABLE, FcTrue); + FcPatternAddString(request, FC_FONTFORMAT, (const FcChar8 *)"TrueType"); + + font_database = FcFontList(config, request, requested_elements); while (!vkengine_should_close(e)) { glfwPollEvents(); @@ -319,7 +341,7 @@ int main(int argc, char* argv[]) { FcFontSetDestroy(font_database); FcObjectSetDestroy(requested_elements); - FcPatternDestroy(blank_pattern); + FcPatternDestroy(request); FcFini(); return 0; diff --git a/tests/svg-parser-new/vkvg_svg_internal.h b/tests/svg-parser-new/vkvg_svg_internal.h index c8d4ff1..749d5f7 100644 --- a/tests/svg-parser-new/vkvg_svg_internal.h +++ b/tests/svg-parser-new/vkvg_svg_internal.h @@ -30,6 +30,7 @@ #define LOG #endif #define LOGE(...) { fprintf(stdout, CLR_RED "[SVG] " __VA_ARGS__); fflush(stdout); } +#define LOGW(...) { fprintf(stdout, CLR_YELLOW "[SVG] " __VA_ARGS__); fflush(stdout); } #ifndef M_PIF #define M_PIF ((float)3.14159265358979323846) |