diff options
Diffstat (limited to 'source/blender/blenfont/intern')
-rw-r--r-- | source/blender/blenfont/intern/blf.c | 28 | ||||
-rw-r--r-- | source/blender/blenfont/intern/blf_font.c | 521 | ||||
-rw-r--r-- | source/blender/blenfont/intern/blf_font_default.c | 71 | ||||
-rw-r--r-- | source/blender/blenfont/intern/blf_glyph.c | 374 | ||||
-rw-r--r-- | source/blender/blenfont/intern/blf_internal.h | 29 | ||||
-rw-r--r-- | source/blender/blenfont/intern/blf_internal_types.h | 167 | ||||
-rw-r--r-- | source/blender/blenfont/intern/blf_thumbs.c | 29 |
7 files changed, 902 insertions, 317 deletions
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index a1fcc17ca3f..9d9cc51ebcc 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -22,6 +22,7 @@ #include "MEM_guardedalloc.h" #include "BLI_math.h" +#include "BLI_string.h" #include "BLI_threads.h" #include "BLF_api.h" @@ -98,7 +99,7 @@ bool blf_font_id_is_valid(int fontid) static int blf_search(const char *name) { for (int i = 0; i < BLF_MAX_FONT; i++) { - FontBLF *font = global_font[i]; + const FontBLF *font = global_font[i]; if (font && (STREQ(font->name, name))) { return i; } @@ -122,7 +123,7 @@ bool BLF_has_glyph(int fontid, unsigned int unicode) { FontBLF *font = blf_get(fontid); if (font) { - return FT_Get_Char_Index(font->face, unicode) != FT_Err_Ok; + return blf_get_char_index(font, unicode) != FT_Err_Ok; } return false; } @@ -483,7 +484,7 @@ void BLF_batch_draw_end(void) g_batch.enabled = false; } -static void blf_draw_gl__start(FontBLF *font) +static void blf_draw_gl__start(const FontBLF *font) { /* * The pixmap alignment hack is handle @@ -511,7 +512,7 @@ static void blf_draw_gl__start(FontBLF *font) } } -static void blf_draw_gl__end(FontBLF *font) +static void blf_draw_gl__end(const FontBLF *font) { if ((font->flags & (BLF_ROTATION | BLF_MATRIX | BLF_ASPECT)) != 0) { GPU_matrix_pop(); @@ -885,12 +886,21 @@ void BLF_draw_buffer(int fontid, const char *str, const size_t str_len) char *BLF_display_name_from_file(const char *filepath) { - FontBLF *font = blf_font_new("font_name", filepath); - if (!font) { - return NULL; + /* While listing font directories this function can be called simultaneously from a greater + * number of threads than we want the FreeType cache to keep open at a time. Therefore open + * with own FT_Library object and use FreeType calls directly to avoid any contention. */ + char *name = NULL; + FT_Library ft_library; + if (FT_Init_FreeType(&ft_library) == FT_Err_Ok) { + FT_Face face; + if (FT_New_Face(ft_library, filepath, 0, &face) == FT_Err_Ok) { + if (face->family_name) { + name = BLI_sprintfN("%s %s", face->family_name, face->style_name); + } + FT_Done_Face(face); + } + FT_Done_FreeType(ft_library); } - char *name = blf_display_name(font); - blf_font_free(font); return name; } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 26a72fcb95a..eaea88be9ae 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -17,8 +17,11 @@ #include <ft2build.h> #include FT_FREETYPE_H +#include FT_CACHE_H /* FreeType Cache. */ #include FT_GLYPH_H -#include FT_TRUETYPE_TABLES_H /* For TT_OS2 */ +#include FT_MULTIPLE_MASTERS_H /* Variable font support. */ +#include FT_TRUETYPE_IDS_H /* Code-point coverage constants. */ +#include FT_TRUETYPE_TABLES_H /* For TT_OS2 */ #include "MEM_guardedalloc.h" @@ -27,6 +30,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" +#include "BLI_path_util.h" #include "BLI_rect.h" #include "BLI_string.h" #include "BLI_string_utf8.h" @@ -51,9 +55,12 @@ BatchBLF g_batch; /* freetype2 handle ONLY for this file! */ -static FT_Library ft_lib; -static SpinLock ft_lib_mutex; -static SpinLock blf_glyph_cache_mutex; +static FT_Library ft_lib = NULL; +static FTC_Manager ftc_manager = NULL; +static FTC_CMapCache ftc_charmap_cache = NULL; + +/* Lock for FreeType library, used around face creation and deletion. */ +static ThreadMutex ft_lib_mutex; /* May be set to #UI_widgetbase_draw_cache_flush. */ static void (*blf_draw_cache_flush)(void) = NULL; @@ -62,19 +69,92 @@ static ft_pix blf_font_height_max_ft_pix(struct FontBLF *font); static ft_pix blf_font_width_max_ft_pix(struct FontBLF *font); /* -------------------------------------------------------------------- */ + +/** \name FreeType Caching + * \{ */ + +/** + * Called when a face is removed by the cache. FreeType will call #FT_Done_Face. + */ +static void blf_face_finalizer(void *object) +{ + FT_Face face = object; + FontBLF *font = (FontBLF *)face->generic.data; + font->face = NULL; +} + +/** + * Called in response to #FTC_Manager_LookupFace. Now add a face to our font. + * + * \note Unused arguments are kept to match #FTC_Face_Requester function signature. + */ +static FT_Error blf_cache_face_requester(FTC_FaceID faceID, + FT_Library lib, + FT_Pointer UNUSED(reqData), + FT_Face *face) +{ + FontBLF *font = (FontBLF *)faceID; + int err = FT_Err_Cannot_Open_Resource; + + BLI_mutex_lock(&ft_lib_mutex); + if (font->filepath) { + err = FT_New_Face(lib, font->filepath, 0, face); + } + else if (font->mem) { + err = FT_New_Memory_Face(lib, font->mem, (FT_Long)font->mem_size, 0, face); + } + BLI_mutex_unlock(&ft_lib_mutex); + + if (err == FT_Err_Ok) { + font->face = *face; + font->face->generic.data = font; + font->face->generic.finalizer = blf_face_finalizer; + } + else { + /* Clear this on error to avoid exception in FTC_Manager_LookupFace. */ + *face = NULL; + } + + return err; +} + +/** + * Called when the FreeType cache is removing a font size. + */ +static void blf_size_finalizer(void *object) +{ + FT_Size size = object; + FontBLF *font = (FontBLF *)size->generic.data; + font->ft_size = NULL; +} + +/* -------------------------------------------------------------------- */ /** \name FreeType Utilities (Internal) * \{ */ -/* Convert a FreeType 26.6 value representing an unscaled design size to factional pixels. */ +uint blf_get_char_index(FontBLF *font, uint charcode) +{ + if (font->flags & BLF_CACHED) { + /* Use char-map cache for much faster lookup. */ + return FTC_CMapCache_Lookup(ftc_charmap_cache, font, -1, charcode); + } + /* Fonts that are not cached need to use the regular lookup function. */ + return blf_ensure_face(font) ? FT_Get_Char_Index(font->face, charcode) : 0; +} + +/* Convert a FreeType 26.6 value representing an unscaled design size to fractional pixels. */ static ft_pix blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) { + /* Make sure we have a valid font->ft_size. */ + blf_ensure_size(font); + /* Scale value by font size using integer-optimized multiplication. */ - FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale); + FT_Long scaled = FT_MulFix(value, font->ft_size->metrics.x_scale); /* Copied from FreeType's FT_Get_Kerning (with FT_KERNING_DEFAULT), scaling down */ - /* kerning distances at small ppem values so that they don't become too big. */ - if (font->face->size->metrics.x_ppem < 25) { - scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25); + /* kerning distances at small PPEM values so that they don't become too big. */ + if (font->ft_size->metrics.x_ppem < 25) { + scaled = FT_MulDiv(scaled, font->ft_size->metrics.x_ppem, 25); } return (ft_pix)scaled; @@ -143,7 +223,7 @@ void blf_batch_draw_begin(FontBLF *font) g_batch.ofs[1] = font->pos[1]; } else { - /* Offset is baked in modelview mat. */ + /* Offset is baked in model-view matrix. */ zero_v2_int(g_batch.ofs); } @@ -151,16 +231,16 @@ void blf_batch_draw_begin(FontBLF *font) float gpumat[4][4]; GPU_matrix_model_view_get(gpumat); - bool mat_changed = (memcmp(gpumat, g_batch.mat, sizeof(g_batch.mat)) != 0); + bool mat_changed = equals_m4m4(gpumat, g_batch.mat) == false; if (mat_changed) { - /* Modelviewmat is no longer the same. - * Flush cache but with the previous mat. */ + /* Model view matrix is no longer the same. + * Flush cache but with the previous matrix. */ GPU_matrix_push(); GPU_matrix_set(g_batch.mat); } - /* flush cache if config is not the same. */ + /* Flush cache if configuration is not the same. */ if (mat_changed || font_changed || shader_changed) { blf_batch_draw(); g_batch.simple_shader = simple_shader; @@ -173,7 +253,7 @@ void blf_batch_draw_begin(FontBLF *font) if (mat_changed) { GPU_matrix_pop(); - /* Save for next memcmp. */ + /* Save for next `memcmp`. */ memcpy(g_batch.mat, gpumat, sizeof(g_batch.mat)); } } @@ -199,7 +279,7 @@ static GPUTexture *blf_batch_cache_texture_load(void) int offset_x = bitmap_len_landed % tex_width; int offset_y = bitmap_len_landed / tex_width; - /* TODO(germano): Update more than one row in a single call. */ + /* TODO(@germano): Update more than one row in a single call. */ while (remain) { int remain_row = tex_width - offset_x; int width = remain > remain_row ? remain_row : remain; @@ -293,22 +373,22 @@ BLI_INLINE ft_pix blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const Glyph /* Small adjust if there is hinting. */ adjustment += g->lsb_delta - ((g_prev) ? g_prev->rsb_delta : 0); - if (FT_HAS_KERNING(font->face) && g_prev) { + if (FT_HAS_KERNING(font) && g_prev) { FT_Vector delta = {KERNING_ENTRY_UNSET}; /* Get unscaled kerning value from our cache if ASCII. */ - if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) { + if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < KERNING_CACHE_TABLE_SIZE)) { delta.x = font->kerning_cache->ascii_table[g->c][g_prev->c]; } /* If not ASCII or not found in cache, ask FreeType for kerning. */ - if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { + if (UNLIKELY(font->face && delta.x == KERNING_ENTRY_UNSET)) { /* Note that this function sets delta values to zero on any error. */ FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); } /* If ASCII we save this value to our cache for quicker access next time. */ - if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) { + if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < KERNING_CACHE_TABLE_SIZE)) { font->kerning_cache->ascii_table[g->c][g_prev->c] = (int)delta.x; } @@ -332,7 +412,7 @@ static void blf_font_draw_ex(FontBLF *font, const char *str, const size_t str_len, struct ResultBLF *r_info, - ft_pix pen_y) + const ft_pix pen_y) { GlyphBLF *g, *g_prev = NULL; ft_pix pen_x = 0; @@ -433,7 +513,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, /* buffer specific vars */ FontBufInfoBLF *buf_info = &font->buf_info; const float *b_col_float = buf_info->col_float; - const unsigned char *b_col_char = buf_info->col_char; + const uchar *b_col_char = buf_info->col_char; int chx, chy; int y, x; @@ -522,7 +602,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, const size_t buf_ofs = (((size_t)(chx + x) + ((size_t)(pen_y_px + y) * (size_t)buf_info->dims[0])) * (size_t)buf_info->ch); - unsigned char *cbuf = buf_info->cbuf + buf_ofs; + uchar *cbuf = buf_info->cbuf + buf_ofs; uchar font_pixel[4]; font_pixel[0] = b_col_char[0]; @@ -835,7 +915,7 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, size_t i = 0, i_curr; rcti gbox_px; - if (str_len == 0) { + if (str_len == 0 || str[0] == 0) { /* early output. */ return; } @@ -922,8 +1002,7 @@ static void blf_font_wrap_apply(FontBLF *font, int lines = 0; ft_pix pen_x_next = 0; - /* Space between lines needs to be aligned to the pixel grid (T97310). */ - ft_pix line_height = FT_PIX_FLOOR(blf_font_height_max_ft_pix(font)); + ft_pix line_height = blf_font_height_max_ft_pix(font); GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); @@ -1080,14 +1159,14 @@ int blf_font_count_missing_chars(FontBLF *font, *r_tot_chars = 0; while (i < str_len) { - unsigned int c; + uint c; if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) { i++; } else { c = BLI_str_utf8_as_unicode_step(str, str_len, &i); - if (FT_Get_Char_Index((font)->face, c) == 0) { + if (blf_get_char_index(font, c) == 0) { missing++; } } @@ -1104,18 +1183,9 @@ int blf_font_count_missing_chars(FontBLF *font, static ft_pix blf_font_height_max_ft_pix(FontBLF *font) { - ft_pix height_max; - FT_Face face = font->face; - if (FT_IS_SCALABLE(face)) { - height_max = ft_pix_from_int((int)(face->ascender - face->descender) * - (int)face->size->metrics.y_ppem) / - (ft_pix)face->units_per_EM; - } - else { - height_max = (ft_pix)face->size->metrics.height; - } - /* can happen with size 1 fonts */ - return MAX2(height_max, ft_pix_from_int(1)); + blf_ensure_size(font); + /* Metrics.height is rounded to pixel. Force minimum of one pixel. */ + return MAX2((ft_pix)font->ft_size->metrics.height, ft_pix_from_int(1)); } int blf_font_height_max(FontBLF *font) @@ -1125,18 +1195,9 @@ int blf_font_height_max(FontBLF *font) static ft_pix blf_font_width_max_ft_pix(FontBLF *font) { - ft_pix width_max; - const FT_Face face = font->face; - if (FT_IS_SCALABLE(face)) { - width_max = ft_pix_from_int((int)(face->bbox.xMax - face->bbox.xMin) * - (int)face->size->metrics.x_ppem) / - (ft_pix)face->units_per_EM; - } - else { - width_max = (ft_pix)face->size->metrics.max_advance; - } - /* can happen with size 1 fonts */ - return MAX2(width_max, ft_pix_from_int(1)); + blf_ensure_size(font); + /* Metrics.max_advance is rounded to pixel. Force minimum of one pixel. */ + return MAX2((ft_pix)font->ft_size->metrics.max_advance, ft_pix_from_int(1)); } int blf_font_width_max(FontBLF *font) @@ -1146,17 +1207,19 @@ int blf_font_width_max(FontBLF *font) int blf_font_descender(FontBLF *font) { - return ft_pix_to_int((ft_pix)font->face->size->metrics.descender); + blf_ensure_size(font); + return ft_pix_to_int((ft_pix)font->ft_size->metrics.descender); } int blf_font_ascender(FontBLF *font) { - return ft_pix_to_int((ft_pix)font->face->size->metrics.ascender); + blf_ensure_size(font); + return ft_pix_to_int((ft_pix)font->ft_size->metrics.ascender); } char *blf_display_name(FontBLF *font) { - if (!font->face->family_name) { + if (!blf_ensure_face(font) || !font->face->family_name) { return NULL; } return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); @@ -1171,16 +1234,34 @@ char *blf_display_name(FontBLF *font) int blf_font_init(void) { memset(&g_batch, 0, sizeof(g_batch)); - BLI_spin_init(&ft_lib_mutex); - BLI_spin_init(&blf_glyph_cache_mutex); - return FT_Init_FreeType(&ft_lib); + BLI_mutex_init(&ft_lib_mutex); + int err = FT_Init_FreeType(&ft_lib); + if (err == FT_Err_Ok) { + /* Create a FreeType cache manager. */ + err = FTC_Manager_New(ft_lib, + BLF_CACHE_MAX_FACES, + BLF_CACHE_MAX_SIZES, + BLF_CACHE_BYTES, + blf_cache_face_requester, + NULL, + &ftc_manager); + if (err == FT_Err_Ok) { + /* Create a charmap cache to speed up glyph index lookups. */ + err = FTC_CMapCache_New(ftc_manager, &ftc_charmap_cache); + } + } + return err; } void blf_font_exit(void) { - FT_Done_FreeType(ft_lib); - BLI_spin_end(&ft_lib_mutex); - BLI_spin_end(&blf_glyph_cache_mutex); + BLI_mutex_end(&ft_lib_mutex); + if (ftc_manager) { + FTC_Manager_Done(ftc_manager); + } + if (ft_lib) { + FT_Done_FreeType(ft_lib); + } blf_batch_draw_exit(); } @@ -1237,20 +1318,38 @@ static void blf_font_fill(FontBLF *font) font->buf_info.col_init[1] = 0; font->buf_info.col_init[2] = 0; font->buf_info.col_init[3] = 0; - - font->ft_lib = ft_lib; - font->ft_lib_mutex = &ft_lib_mutex; - font->glyph_cache_mutex = &blf_glyph_cache_mutex; } -FontBLF *blf_font_new(const char *name, const char *filepath) +/** + * Create an FT_Face for this font if not already existing. + */ +bool blf_ensure_face(FontBLF *font) { - FontBLF *font; + if (font->face) { + return true; + } + + if (font->flags & BLF_BAD_FONT) { + return false; + } + FT_Error err; - char *mfile; - font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new"); - err = FT_New_Face(ft_lib, filepath, 0, &font->face); + if (font->flags & BLF_CACHED) { + err = FTC_Manager_LookupFace(ftc_manager, font, &font->face); + } + else { + BLI_mutex_lock(&ft_lib_mutex); + if (font->filepath) { + err = FT_New_Face(font->ft_lib, font->filepath, 0, &font->face); + } + if (font->mem) { + err = FT_New_Memory_Face(font->ft_lib, font->mem, (FT_Long)font->mem_size, 0, &font->face); + } + font->face->generic.data = font; + BLI_mutex_unlock(&ft_lib_mutex); + } + if (err) { if (ELEM(err, FT_Err_Unknown_File_Format, FT_Err_Unimplemented_Feature)) { printf("Format of this font file is not supported\n"); @@ -1258,8 +1357,13 @@ FontBLF *blf_font_new(const char *name, const char *filepath) else { printf("Error encountered while opening font file\n"); } - MEM_freeN(font); - return NULL; + font->flags |= BLF_BAD_FONT; + return false; + } + + if (font->face && !(font->face->face_flags & FT_FACE_FLAG_SCALABLE)) { + printf("Font is not scalable\n"); + return false; } err = FT_Select_Charmap(font->face, FT_ENCODING_UNICODE); @@ -1271,44 +1375,49 @@ FontBLF *blf_font_new(const char *name, const char *filepath) } if (err) { printf("Can't set a character map!\n"); - FT_Done_Face(font->face); - MEM_freeN(font); - return NULL; + font->flags |= BLF_BAD_FONT; + return false; } - mfile = blf_dir_metrics_search(filepath); - if (mfile) { - err = FT_Attach_File(font->face, mfile); - if (err) { - fprintf(stderr, "FT_Attach_File failed to load '%s' with error %d\n", filepath, (int)err); + if (font->filepath) { + char *mfile = blf_dir_metrics_search(font->filepath); + if (mfile) { + err = FT_Attach_File(font->face, mfile); + if (err) { + fprintf(stderr, + "FT_Attach_File failed to load '%s' with error %d\n", + font->filepath, + (int)err); + } + MEM_freeN(mfile); } - MEM_freeN(mfile); } - font->name = BLI_strdup(name); - font->filepath = BLI_strdup(filepath); - blf_font_fill(font); + if (!(font->flags & BLF_CACHED)) { + /* Not cached so point at the face's size for convenience. */ + font->ft_size = font->face->size; + } - /* Save TrueType table with bits to quickly test most unicode block coverage. */ - TT_OS2 *os2_table = (TT_OS2 *)FT_Get_Sfnt_Table(font->face, FT_SFNT_OS2); - if (os2_table) { - font->UnicodeRanges[0] = (uint)os2_table->ulUnicodeRange1; - font->UnicodeRanges[1] = (uint)os2_table->ulUnicodeRange2; - font->UnicodeRanges[2] = (uint)os2_table->ulUnicodeRange3; - font->UnicodeRanges[3] = (uint)os2_table->ulUnicodeRange4; + font->face_flags = font->face->face_flags; + + if (FT_HAS_MULTIPLE_MASTERS(font)) { + FT_Get_MM_Var(font->face, &(font->variations)); } - /* Detect "Last resort" fonts. They have everything. Usually except last 5 bits. */ - if (font->UnicodeRanges[0] == 0xffffffffU && font->UnicodeRanges[1] == 0xffffffffU && - font->UnicodeRanges[2] == 0xffffffffU && font->UnicodeRanges[3] >= 0x7FFFFFFU) { - font->flags |= BLF_LAST_RESORT; + /* Save TrueType table with bits to quickly test most unicode block coverage. */ + TT_OS2 *os2_table = (TT_OS2 *)FT_Get_Sfnt_Table(font->face, FT_SFNT_OS2); + if (os2_table) { + font->unicode_ranges[0] = (uint)os2_table->ulUnicodeRange1; + font->unicode_ranges[1] = (uint)os2_table->ulUnicodeRange2; + font->unicode_ranges[2] = (uint)os2_table->ulUnicodeRange3; + font->unicode_ranges[3] = (uint)os2_table->ulUnicodeRange4; } - if (FT_IS_FIXED_WIDTH(font->face)) { + if (FT_IS_FIXED_WIDTH(font)) { font->flags |= BLF_MONOSPACED; } - if (FT_HAS_KERNING(font->face)) { + if (FT_HAS_KERNING(font) && !font->kerning_cache) { /* Create kerning cache table and fill with value indicating "unset". */ font->kerning_cache = MEM_mallocN(sizeof(KerningCacheBLF), __func__); for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { @@ -1318,45 +1427,138 @@ FontBLF *blf_font_new(const char *name, const char *filepath) } } - return font; + return true; } -void blf_font_attach_from_mem(FontBLF *font, const unsigned char *mem, int mem_size) +struct FaceDetails { + char name[50]; + uint coverage1; + uint coverage2; + uint coverage3; + uint coverage4; +}; + +/* Details about the fallback fonts we ship, so that we can load only when needed. */ +static const struct FaceDetails static_face_details[] = { + {"lastresort.woff2", UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX}, + {"Noto Sans CJK Regular.woff2", 0x30000083L, 0x2BDF3C10L, 0x16L, 0}, + {"NotoEmoji-VariableFont_wght.woff2", 0x80000003L, 0x241E4ACL, 0x14000000L, 0x4000000L}, + {"NotoSansArabic-VariableFont_wdth,wght.woff2", + TT_UCR_ARABIC, + (uint)TT_UCR_ARABIC_PRESENTATION_FORMS_A, + TT_UCR_ARABIC_PRESENTATION_FORMS_B, + 0}, + {"NotoSansArmenian-VariableFont_wdth,wght.woff2", TT_UCR_ARMENIAN, 0, 0, 0}, + {"NotoSansBengali-VariableFont_wdth,wght.woff2", TT_UCR_BENGALI, 0, 0, 0}, + {"NotoSansDevanagari-Regular.woff2", TT_UCR_DEVANAGARI, 0, 0, 0}, + {"NotoSansEthiopic-Regular.woff2", 0, 0, TT_UCR_ETHIOPIC, 0}, + {"NotoSansGeorgian-VariableFont_wdth,wght.woff2", TT_UCR_GEORGIAN, 0, 0, 0}, + {"NotoSansGujarati-Regular.woff2", TT_UCR_GUJARATI, 0, 0, 0}, + {"NotoSansGurmukhi-VariableFont_wdth,wght.woff2", TT_UCR_GURMUKHI, 0, 0, 0}, + {"NotoSansHebrew-Regular.woff2", TT_UCR_HEBREW, 0, 0, 0}, + {"NotoSansJavanese-Regular.woff2", 0x80000003L, 0x2000L, 0, 0}, + {"NotoSansKannada-VariableFont_wdth,wght.woff2", TT_UCR_KANNADA, 0, 0, 0}, + {"NotoSansMalayalam-VariableFont_wdth,wght.woff2", TT_UCR_MALAYALAM, 0, 0, 0}, + {"NotoSansMath-Regular.woff2", 0, TT_UCR_MATHEMATICAL_OPERATORS, 0, 0}, + {"NotoSansMyanmar-Regular.woff2", 0, 0, TT_UCR_MYANMAR, 0}, + {"NotoSansSymbols-VariableFont_wght.woff2", 0x3L, 0x200E4B4L, 0, 0}, + {"NotoSansSymbols2-Regular.woff2", 0x80000003L, 0x200E3E4L, 0x40020L, 0x580A048L}, + {"NotoSansTamil-VariableFont_wdth,wght.woff2", TT_UCR_TAMIL, 0, 0, 0}, + {"NotoSansTelugu-VariableFont_wdth,wght.woff2", TT_UCR_TELUGU, 0, 0, 0}, + {"NotoSansThai-VariableFont_wdth,wght.woff2", TT_UCR_THAI, 0, 0, 0}, +}; + +/** + * Create a new font from filename OR memory pointer. + * For normal operation pass NULL as FT_Library object. Pass a custom FT_Library if you + * want to use the font without its lifetime being managed by the FreeType cache subsystem. + */ +FontBLF *blf_font_new_ex(const char *name, + const char *filepath, + const uchar *mem, + const size_t mem_size, + void *ft_library) { - FT_Open_Args open; + FontBLF *font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new"); - open.flags = FT_OPEN_MEMORY; - open.memory_base = (const FT_Byte *)mem; - open.memory_size = mem_size; - FT_Attach_Stream(font->face, &open); -} + font->name = BLI_strdup(name); + font->filepath = filepath ? BLI_strdup(filepath) : NULL; + if (mem) { + font->mem = (void *)mem; + font->mem_size = mem_size; + } + blf_font_fill(font); -FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size) -{ - FontBLF *font; - FT_Error err; + if (ft_library && ((FT_Library)ft_library != ft_lib)) { + font->ft_lib = (FT_Library)ft_library; + } + else { + font->ft_lib = ft_lib; + font->flags |= BLF_CACHED; + } - font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new_from_mem"); - err = FT_New_Memory_Face(ft_lib, mem, mem_size, 0, &font->face); - if (err) { - MEM_freeN(font); - return NULL; + font->ft_lib = ft_library ? (FT_Library)ft_library : ft_lib; + + BLI_mutex_init(&font->glyph_cache_mutex); + + /* If we have static details about this font file, we don't have to load the Face yet. */ + bool face_needed = true; + + if (font->filepath) { + const struct FaceDetails *static_details = NULL; + char filename[256]; + for (int i = 0; i < (int)ARRAY_SIZE(static_face_details); i++) { + BLI_split_file_part(font->filepath, filename, sizeof(filename)); + if (STREQ(static_face_details[i].name, filename)) { + static_details = &static_face_details[i]; + font->unicode_ranges[0] = static_details->coverage1; + font->unicode_ranges[1] = static_details->coverage2; + font->unicode_ranges[2] = static_details->coverage3; + font->unicode_ranges[3] = static_details->coverage4; + face_needed = false; + break; + } + } } - err = FT_Select_Charmap(font->face, ft_encoding_unicode); - if (err) { - printf("Can't set the unicode character map!\n"); - FT_Done_Face(font->face); - MEM_freeN(font); - return NULL; + if (face_needed) { + if (!blf_ensure_face(font)) { + blf_font_free(font); + return NULL; + } + } + + /* Detect "Last resort" fonts. They have everything. Usually except last 5 bits. */ + if (font->unicode_ranges[0] == 0xffffffffU && font->unicode_ranges[1] == 0xffffffffU && + font->unicode_ranges[2] == 0xffffffffU && font->unicode_ranges[3] >= 0x7FFFFFFU) { + font->flags |= BLF_LAST_RESORT; } - font->name = BLI_strdup(name); - font->filepath = NULL; - blf_font_fill(font); return font; } +FontBLF *blf_font_new(const char *name, const char *filepath) +{ + return blf_font_new_ex(name, filepath, NULL, 0, NULL); +} + +FontBLF *blf_font_new_from_mem(const char *name, const uchar *mem, const size_t mem_size) +{ + return blf_font_new_ex(name, NULL, mem, mem_size, NULL); +} + +void blf_font_attach_from_mem(FontBLF *font, const uchar *mem, const size_t mem_size) +{ + FT_Open_Args open; + + open.flags = FT_OPEN_MEMORY; + open.memory_base = (const FT_Byte *)mem; + open.memory_size = (FT_Long)mem_size; + if (blf_ensure_face(font)) { + FT_Attach_Stream(font->face, &open); + } +} + void blf_font_free(FontBLF *font) { blf_glyph_cache_clear(font); @@ -1365,13 +1567,30 @@ void blf_font_free(FontBLF *font) MEM_freeN(font->kerning_cache); } - FT_Done_Face(font->face); + if (font->variations) { + FT_Done_MM_Var(font->ft_lib, font->variations); + } + + if (font->face) { + BLI_mutex_lock(&ft_lib_mutex); + if (font->flags & BLF_CACHED) { + FTC_Manager_RemoveFaceID(ftc_manager, font); + } + else { + FT_Done_Face(font->face); + } + BLI_mutex_unlock(&ft_lib_mutex); + font->face = NULL; + } if (font->filepath) { MEM_freeN(font->filepath); } if (font->name) { MEM_freeN(font->name); } + + BLI_mutex_end(&font->glyph_cache_mutex); + MEM_freeN(font); } @@ -1381,24 +1600,64 @@ void blf_font_free(FontBLF *font) /** \name Font Configure * \{ */ -bool blf_font_size(FontBLF *font, float size, unsigned int dpi) +void blf_ensure_size(FontBLF *font) +{ + if (font->ft_size || !(font->flags & BLF_CACHED)) { + return; + } + + FTC_ScalerRec scaler = {0}; + scaler.face_id = font; + scaler.width = 0; + scaler.height = round_fl_to_uint(font->size * 64.0f); + scaler.pixel = 0; + scaler.x_res = font->dpi; + scaler.y_res = font->dpi; + if (FTC_Manager_LookupSize(ftc_manager, &scaler, &font->ft_size) == FT_Err_Ok) { + font->ft_size->generic.data = (void *)font; + font->ft_size->generic.finalizer = blf_size_finalizer; + return; + } + + BLI_assert_unreachable(); +} + +bool blf_font_size(FontBLF *font, float size, uint dpi) { + if (!blf_ensure_face(font)) { + return false; + } + /* FreeType uses fixed-point integers in 64ths. */ - FT_F26Dot6 ft_size = lroundf(size * 64.0f); + FT_UInt ft_size = round_fl_to_uint(size * 64.0f); /* Adjust our new size to be on even 64ths. */ size = (float)ft_size / 64.0f; if (font->size != size || font->dpi != dpi) { - if (FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi) == FT_Err_Ok) { - font->size = size; - font->dpi = dpi; + if (font->flags & BLF_CACHED) { + FTC_ScalerRec scaler = {0}; + scaler.face_id = font; + scaler.width = 0; + scaler.height = ft_size; + scaler.pixel = 0; + scaler.x_res = dpi; + scaler.y_res = dpi; + if (FTC_Manager_LookupSize(ftc_manager, &scaler, &font->ft_size) != FT_Err_Ok) { + return false; + } + font->ft_size->generic.data = (void *)font; + font->ft_size->generic.finalizer = blf_size_finalizer; } else { - printf("The current font does not support the size, %f and DPI, %u\n", size, dpi); - return false; + if (FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi) != FT_Err_Ok) { + return false; + } + font->ft_size = font->face->size; } } + font->size = size; + font->dpi = dpi; return true; } diff --git a/source/blender/blenfont/intern/blf_font_default.c b/source/blender/blenfont/intern/blf_font_default.c index 1bde25b5776..d35692f6eae 100644 --- a/source/blender/blenfont/intern/blf_font_default.c +++ b/source/blender/blenfont/intern/blf_font_default.c @@ -16,6 +16,10 @@ #include "BKE_appdir.h" +#ifdef WIN32 +# include "BLI_winstuff.h" +#endif + static int blf_load_font_default(const char *filename, const bool unique) { const char *dir = BKE_appdir_folder_id(BLENDER_DATAFILES, BLF_DATAFILES_FONTS_DIR); @@ -47,34 +51,51 @@ int BLF_load_mono_default(const bool unique) return font_id; } +static void blf_load_datafiles_dir(void) +{ + const char *datafiles_fonts_dir = BLF_DATAFILES_FONTS_DIR SEP_STR; + const char *path = BKE_appdir_folder_id(BLENDER_DATAFILES, datafiles_fonts_dir); + if (UNLIKELY(!path)) { + fprintf(stderr, "Font data directory \"%s\" could not be detected!\n", datafiles_fonts_dir); + return; + } + if (UNLIKELY(!BLI_exists(path))) { + fprintf(stderr, "Font data directory \"%s\" does not exist!\n", path); + return; + } + + struct direntry *file_list; + uint file_list_num = BLI_filelist_dir_contents(path, &file_list); + for (int i = 0; i < file_list_num; i++) { + if (S_ISDIR(file_list[i].s.st_mode)) { + continue; + } + + const char *filepath = file_list[i].path; + if (!BLI_path_extension_check_n( + filepath, ".ttf", ".ttc", ".otf", ".otc", ".woff", ".woff2", NULL)) { + continue; + } + if (BLF_is_loaded(filepath)) { + continue; + } + + /* Attempt to load the font. */ + int font_id = BLF_load(filepath); + if (font_id == -1) { + fprintf(stderr, "Unable to load font: %s\n", filepath); + continue; + } + + BLF_enable(font_id, BLF_DEFAULT); + } + BLI_filelist_free(file_list, file_list_num); +} + void BLF_load_font_stack() { /* Load these if not already, might have been replaced by user custom. */ BLF_load_default(false); BLF_load_mono_default(false); - - const char *path = BKE_appdir_folder_id(BLENDER_DATAFILES, BLF_DATAFILES_FONTS_DIR SEP_STR); - if (path && BLI_exists(path)) { - struct direntry *dir; - uint num_files = BLI_filelist_dir_contents(path, &dir); - for (int f = 0; f < num_files; f++) { - if (!FILENAME_IS_CURRPAR(dir[f].relname) && !BLI_is_dir(dir[f].path)) { - if (!BLF_is_loaded(dir[f].path)) { - int font_id = BLF_load(dir[f].path); - if (font_id == -1) { - fprintf(stderr, "Unable to load font: %s\n", dir[f].path); - } - else { - BLF_enable(font_id, BLF_DEFAULT); - /* TODO: FontBLF will later load FT_Face on demand. When this is in - * place we can drop this face now since we have all needed data. */ - } - } - } - } - BLI_filelist_free(dir, num_files); - } - else { - fprintf(stderr, "Fonts not found at %s\n", path); - } + blf_load_datafiles_dir(); } diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index ed30cca4da2..c08d52307b7 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -18,13 +18,11 @@ #include FT_GLYPH_H #include FT_OUTLINE_H #include FT_BITMAP_H -#include FT_ADVANCES_H /* For FT_Get_Advance. */ +#include FT_ADVANCES_H /* For FT_Get_Advance. */ +#include FT_MULTIPLE_MASTERS_H /* Variable font support. */ #include "MEM_guardedalloc.h" -#include "DNA_userdef_types.h" -#include "DNA_vec_types.h" - #include "BLI_listbase.h" #include "BLI_rect.h" #include "BLI_threads.h" @@ -32,7 +30,6 @@ #include "BLF_api.h" #include "GPU_capabilities.h" -#include "GPU_immediate.h" #include "blf_internal.h" #include "blf_internal_types.h" @@ -41,6 +38,13 @@ #include "BLI_strict_flags.h" #include "BLI_string_utf8.h" +/** + * Convert glyph coverage amounts to lightness values. Uses a LUT that perceptually improves + * anti-aliasing and results in text that looks a bit fuller and slightly brighter. This should + * be reconsidered in some - or all - cases when we transform the entire UI. + */ +#define BLF_GAMMA_CORRECT_GLYPHS + /* -------------------------------------------------------------------- */ /** \name Internal Utilities * \{ */ @@ -59,12 +63,14 @@ static FT_Fixed to_16dot16(double val) /** \name Glyph Cache * \{ */ -static GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, float size, unsigned int dpi) +static GlyphCacheBLF *blf_glyph_cache_find(const FontBLF *font, const float size, uint dpi) { GlyphCacheBLF *gc = (GlyphCacheBLF *)font->cache.first; while (gc) { if (gc->size == size && gc->dpi == dpi && (gc->bold == ((font->flags & BLF_BOLD) != 0)) && - (gc->italic == ((font->flags & BLF_ITALIC) != 0))) { + (gc->italic == ((font->flags & BLF_ITALIC) != 0)) && + (gc->char_weight == font->char_weight) && (gc->char_slant == font->char_slant) && + (gc->char_width == font->char_width) && (gc->char_spacing == font->char_spacing)) { return gc; } gc = gc->next; @@ -82,21 +88,27 @@ static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) gc->dpi = font->dpi; gc->bold = ((font->flags & BLF_BOLD) != 0); gc->italic = ((font->flags & BLF_ITALIC) != 0); + gc->char_weight = font->char_weight; + gc->char_slant = font->char_slant; + gc->char_width = font->char_width; + gc->char_spacing = font->char_spacing; memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table)); memset(gc->bucket, 0, sizeof(gc->bucket)); + blf_ensure_size(font); + /* Determine ideal fixed-width size for monospaced output. */ - FT_UInt gindex = FT_Get_Char_Index(font->face, U'0'); - if (gindex) { + FT_UInt gindex = blf_get_char_index(font, U'0'); + if (gindex && font->face) { FT_Fixed advance = 0; FT_Get_Advance(font->face, gindex, FT_LOAD_NO_HINTING, &advance); /* Use CSS 'ch unit' width, advance of zero character. */ gc->fixed_width = (int)(advance >> 16); } else { - /* Font does not contain "0" so use CSS fallback of 1/2 of em. */ - gc->fixed_width = (int)((font->face->size->metrics.height / 2) >> 6); + /* Font does not have a face or does not contain "0" so use CSS fallback of 1/2 of em. */ + gc->fixed_width = (int)((font->ft_size->metrics.height / 2) >> 6); } if (gc->fixed_width < 1) { gc->fixed_width = 1; @@ -108,7 +120,7 @@ static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) GlyphCacheBLF *blf_glyph_cache_acquire(FontBLF *font) { - BLI_spin_lock(font->glyph_cache_mutex); + BLI_mutex_lock(&font->glyph_cache_mutex); GlyphCacheBLF *gc = blf_glyph_cache_find(font, font->size, font->dpi); @@ -121,7 +133,7 @@ GlyphCacheBLF *blf_glyph_cache_acquire(FontBLF *font) void blf_glyph_cache_release(FontBLF *font) { - BLI_spin_unlock(font->glyph_cache_mutex); + BLI_mutex_unlock(&font->glyph_cache_mutex); } static void blf_glyph_cache_free(GlyphCacheBLF *gc) @@ -145,13 +157,13 @@ void blf_glyph_cache_clear(FontBLF *font) { GlyphCacheBLF *gc; - BLI_spin_lock(font->glyph_cache_mutex); + BLI_mutex_lock(&font->glyph_cache_mutex); while ((gc = BLI_pophead(&font->cache))) { blf_glyph_cache_free(gc); } - BLI_spin_unlock(font->glyph_cache_mutex); + BLI_mutex_unlock(&font->glyph_cache_mutex); } /** @@ -159,7 +171,7 @@ void blf_glyph_cache_clear(FontBLF *font) * * \return NULL if not found. */ -static GlyphBLF *blf_glyph_cache_find_glyph(GlyphCacheBLF *gc, uint charcode) +static GlyphBLF *blf_glyph_cache_find_glyph(const GlyphCacheBLF *gc, uint charcode) { if (charcode < GLYPH_ASCII_TABLE_SIZE) { return gc->glyph_ascii_table[charcode]; @@ -175,6 +187,43 @@ static GlyphBLF *blf_glyph_cache_find_glyph(GlyphCacheBLF *gc, uint charcode) return NULL; } +#ifdef BLF_GAMMA_CORRECT_GLYPHS + +/** + * Gamma correction of glyph coverage values with widely-recommended gamma of 1.43. + * "The reasons are historical. Because so many programmers have neglected gamma blending for so + * long, people who have created fonts have tried to work around the problem of fonts looking too + * thin by just making the fonts thicker! Obviously it doesn't help the jaggedness, but it does + * make them look the proper weight, as originally intended. The obvious problem with this is + * that if we want to gamma blend correctly many older fonts will look wrong. So we compromise, + * and use a lower gamma value, so we get a bit better anti-aliasing, but the fonts don't look too + * heavy." + * https://www.puredevsoftware.com/blog/2019/01/22/sub-pixel-gamma-correct-font-rendering/ + */ +static uchar blf_glyph_gamma(uchar c) +{ + /* The following is `(char)(powf(c / 256.0f, 1.0f / 1.43f) * 256.0f)`. */ + static const uchar gamma[256] = { + 0, 5, 9, 11, 14, 16, 19, 21, 23, 25, 26, 28, 30, 32, 34, 35, 37, 38, + 40, 41, 43, 44, 46, 47, 49, 50, 52, 53, 54, 56, 57, 58, 60, 61, 62, 64, + 65, 66, 67, 69, 70, 71, 72, 73, 75, 76, 77, 78, 79, 80, 82, 83, 84, 85, + 86, 87, 88, 89, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 153, 154, 155, + 156, 157, 157, 158, 159, 160, 161, 162, 163, 163, 164, 165, 166, 167, 168, 168, 169, 170, + 171, 172, 173, 173, 174, 175, 176, 177, 178, 178, 179, 180, 181, 182, 182, 183, 184, 185, + 186, 186, 187, 188, 189, 190, 190, 191, 192, 193, 194, 194, 195, 196, 197, 198, 198, 199, + 200, 201, 201, 202, 203, 204, 205, 205, 206, 207, 208, 208, 209, 210, 211, 211, 212, 213, + 214, 214, 215, 216, 217, 217, 218, 219, 220, 220, 221, 222, 223, 223, 224, 225, 226, 226, + 227, 228, 229, 229, 230, 231, 231, 232, 233, 234, 234, 235, 236, 237, 237, 238, 239, 239, + 240, 241, 242, 242, 243, 244, 244, 245, 246, 247, 247, 248, 249, 249, 250, 251, 251, 252, + 253, 254, 254, 255}; + return gamma[c]; +} + +#endif /* BLF_GAMMA_CORRECT_GLYPHS */ + /** * Add a rendered glyph to a cache. */ @@ -210,11 +259,19 @@ static GlyphBLF *blf_glyph_cache_add_glyph( glyph->bitmap.buffer[i] = glyph->bitmap.buffer[i] ? 255 : 0; } } + else { +#ifdef BLF_GAMMA_CORRECT_GLYPHS + /* Convert coverage amounts to perceptually-improved lightness values. */ + for (int i = 0; i < buffer_size; i++) { + glyph->bitmap.buffer[i] = blf_glyph_gamma(glyph->bitmap.buffer[i]); + } +#endif /* BLF_GAMMA_CORRECT_GLYPHS */ + } g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap"); memcpy(g->bitmap, glyph->bitmap.buffer, (size_t)buffer_size); } - unsigned int key = blf_hash(g->c); + const uint key = blf_hash(g->c); BLI_addhead(&(gc->bucket[key]), g); if (charcode < GLYPH_ASCII_TABLE_SIZE) { gc->glyph_ascii_table[charcode] = g; @@ -223,18 +280,24 @@ static GlyphBLF *blf_glyph_cache_add_glyph( return g; } -/* This table can be used to find a coverage bit based on a charcode. later we can get default - * language and script from codepoint. */ +/** \} */ -typedef struct eUnicodeBlock { - unsigned int first; - unsigned int last; +/* -------------------------------------------------------------------- */ +/** \name Glyph Unicode Block Lookup + * + * This table can be used to find a coverage bit based on a charcode. + * Later we can get default language and script from `codepoint`. + */ + +struct UnicodeBlock { + uint first; + uint last; int coverage_bit; /* 0-122. -1 is N/A. */ /* Later we add primary script and language for Harfbuzz, data from * https://en.wikipedia.org/wiki/Unicode_block */ -} eUnicodeBlock; +}; -static eUnicodeBlock unicode_blocks[] = { +static const struct UnicodeBlock unicode_blocks[] = { /* Must be in ascending order by start of range. */ {0x0, 0x7F, 0}, /* Basic Latin. */ {0x80, 0xFF, 1}, /* Latin-1 Supplement. */ @@ -378,7 +441,7 @@ static eUnicodeBlock unicode_blocks[] = { {0xFE30, 0xFE4F, 65}, /* CJK Compatibility Forms. */ {0xFE50, 0xFE6F, 66}, /* Small Form Variants. */ {0xFE70, 0xFEFF, 67}, /* Arabic Presentation Forms-B. */ - {0xFF00, 0xFFEF, 68}, /* Halfwidth And Fullwidth Forms. */ + {0xFF00, 0xFFEF, 68}, /* Half-width And Full-width Forms. */ {0xFFF0, 0xFFFF, 69}, /* Specials. */ {0x10000, 0x1013F, 101}, /* Linear B. */ {0x10140, 0x1018F, 102}, /* Ancient Greek Numbers. */ @@ -493,8 +556,10 @@ static eUnicodeBlock unicode_blocks[] = { {0xE0100, 0xE01EF, 91}, /* Variation Selectors. */ {0xF0000, 0x10FFFD, 90}}; /* Private Use Supplementary. */ -/* Find a unicode block that a charcode belongs to. */ -static eUnicodeBlock *blf_charcode_to_unicode_block(uint charcode) +/** + * Find a unicode block that a `charcode` belongs to. + */ +static const struct UnicodeBlock *blf_charcode_to_unicode_block(const uint charcode) { if (charcode < 0x80) { /* Shortcut to Basic Latin. */ @@ -505,14 +570,13 @@ static eUnicodeBlock *blf_charcode_to_unicode_block(uint charcode) int min = 0; int max = ARRAY_SIZE(unicode_blocks) - 1; - int mid; if (charcode < unicode_blocks[0].first || charcode > unicode_blocks[max].last) { return NULL; } while (max >= min) { - mid = (min + max) / 2; + const int mid = (min + max) / 2; if (charcode > unicode_blocks[mid].last) { min = mid + 1; } @@ -530,26 +594,26 @@ static eUnicodeBlock *blf_charcode_to_unicode_block(uint charcode) static int blf_charcode_to_coverage_bit(uint charcode) { int coverage_bit = -1; - eUnicodeBlock *block = blf_charcode_to_unicode_block(charcode); + const struct UnicodeBlock *block = blf_charcode_to_unicode_block(charcode); if (block) { coverage_bit = block->coverage_bit; } if (coverage_bit < 0 && charcode > 0xFFFF) { /* No coverage bit, but OpenType specs v.1.3+ says bit 57 implies that there - * are codepoints supported beyond the BMP, so only check fonts with this set. */ + * are code-points supported beyond the BMP, so only check fonts with this set. */ coverage_bit = 57; } return coverage_bit; } -static bool blf_font_has_coverage_bit(FontBLF *font, int coverage_bit) +static bool blf_font_has_coverage_bit(const FontBLF *font, int coverage_bit) { if (coverage_bit < 0) { return false; } - return (font->UnicodeRanges[(uint)coverage_bit >> 5] & (1u << ((uint)coverage_bit % 32))); + return (font->unicode_ranges[(uint)coverage_bit >> 5] & (1u << ((uint)coverage_bit % 32))); } /** @@ -558,11 +622,16 @@ static bool blf_font_has_coverage_bit(FontBLF *font, int coverage_bit) */ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode) { - FT_UInt glyph_index = FT_Get_Char_Index((*font)->face, charcode); + FT_UInt glyph_index = blf_get_char_index(*font, charcode); if (glyph_index) { return glyph_index; } + /* Only fonts managed by the cache can fallback. */ + if (!((*font)->flags & BLF_CACHED)) { + return 0; + } + /* Not found in main font, so look in the others. */ FontBLF *last_resort = NULL; int coverage_bit = blf_charcode_to_coverage_bit(charcode); @@ -577,7 +646,7 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode continue; } if (coverage_bit < 0 || blf_font_has_coverage_bit(f, coverage_bit)) { - glyph_index = FT_Get_Char_Index(f->face, charcode); + glyph_index = blf_get_char_index(f, charcode); if (glyph_index) { *font = f; return glyph_index; @@ -585,9 +654,13 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode } } +#ifdef DEBUG + printf("Unicode character U+%04X not found in loaded fonts. \n", charcode); +#endif + /* Not found in the stack, return from Last Resort if there is one. */ if (last_resort) { - glyph_index = FT_Get_Char_Index(last_resort->face, charcode); + glyph_index = blf_get_char_index(last_resort, charcode); if (glyph_index) { *font = last_resort; return glyph_index; @@ -597,6 +670,12 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode return 0; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Glyph Load + * \{ */ + /** * Load a glyph into the glyph slot of a font's face object. */ @@ -631,19 +710,19 @@ static FT_GlyphSlot blf_glyph_load(FontBLF *font, FT_UInt glyph_index) return NULL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Glyph Render + * \{ */ + /** * Convert a glyph from outlines to a bitmap that we can display. */ static bool blf_glyph_render_bitmap(FontBLF *font, FT_GlyphSlot glyph) { - int render_mode; - - if (font->flags & BLF_MONOCHROME) { - render_mode = FT_RENDER_MODE_MONO; - } - else { - render_mode = FT_RENDER_MODE_NORMAL; - } + const int render_mode = (font->flags & BLF_MONOCHROME) ? FT_RENDER_MODE_MONO : + FT_RENDER_MODE_NORMAL; /* Render the glyph curves to a bitmap. */ FT_Error err = FT_Render_Glyph(glyph, render_mode); @@ -674,6 +753,98 @@ static bool blf_glyph_render_bitmap(FontBLF *font, FT_GlyphSlot glyph) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Variations (Multiple Masters) support + * \{ */ + +/** + * Return a design axis that matches an identifying tag. + * + * \param variations: Variation descriptors from `FT_Get_MM_Var`. + * \param tag: Axis tag (4-character string as uint), like 'wght' + * \param r_axis_index: returns index of axis in variations array. + */ +static const FT_Var_Axis *blf_var_axis_by_tag(const FT_MM_Var *variations, + const uint tag, + int *r_axis_index) +{ + *r_axis_index = -1; + if (!variations) { + return NULL; + } + for (int i = 0; i < (int)variations->num_axis; i++) { + if (variations->axis[i].tag == tag) { + *r_axis_index = i; + return &(variations->axis)[i]; + break; + } + } + return NULL; +} + +/** + * Convert a float factor to a fixed-point design coordinate. + * + * \param axis: Pointer to a design space axis structure. + * \param factor: -1 to 1 with 0 meaning "default" + */ +static FT_Fixed blf_factor_to_coordinate(const FT_Var_Axis *axis, const float factor) +{ + FT_Fixed value = axis->def; + if (factor > 0) { + /* Map 0-1 to axis->def - axis->maximum */ + value += (FT_Fixed)((double)(axis->maximum - axis->def) * factor); + } + else if (factor < 0) { + /* Map -1-0 to axis->minimum - axis->def */ + value += (FT_Fixed)((double)(axis->def - axis->minimum) * factor); + } + return value; +} + +/** + * Alter a face variation axis by a factor + * + * \param coords: array of design coordinates, per axis. + * \param tag: Axis tag (4-character string as uint), like 'wght' + * \param factor: -1 to 1 with 0 meaning "default" + */ +static bool blf_glyph_set_variation_normalized(const FontBLF *font, + FT_Fixed coords[], + const uint tag, + const float factor) +{ + int axis_index; + const FT_Var_Axis *axis = blf_var_axis_by_tag(font->variations, tag, &axis_index); + if (axis && (axis_index < BLF_VARIATIONS_MAX)) { + coords[axis_index] = blf_factor_to_coordinate(axis, factor); + return true; + } + return false; +} + +/** + * Set a face variation axis to an exact float value + * + * \param coords: array of design coordinates, per axis. + * \param tag: Axis tag (4-character string as uint), like 'opsz' + * \param value: New float value. Converted to 16.16 and clamped within allowed range. + */ +static bool blf_glyph_set_variation_float(FontBLF *font, FT_Fixed coords[], uint tag, float value) +{ + int axis_index; + const FT_Var_Axis *axis = blf_var_axis_by_tag(font->variations, tag, &axis_index); + if (axis && (axis_index < BLF_VARIATIONS_MAX)) { + FT_Fixed int_value = to_16dot16(value); + CLAMP(int_value, axis->minimum, axis->maximum); + coords[axis_index] = int_value; + return true; + } + return false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Glyph Transformations * \{ */ @@ -686,8 +857,8 @@ static bool blf_glyph_transform_weight(FT_GlyphSlot glyph, float factor, bool mo { if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { /* Fake bold if the font does not have this variable axis. */ - const FT_Pos average_width = FT_MulFix(glyph->face->units_per_EM, - glyph->face->size->metrics.x_scale); + const FontBLF *font = (FontBLF *)glyph->face->generic.data; + const FT_Pos average_width = font->ft_size->metrics.height; FT_Pos change = (FT_Pos)((float)average_width * factor * 0.1f); FT_Outline_EmboldenXY(&glyph->outline, change, change / 2); if (monospaced) { @@ -726,7 +897,7 @@ static bool blf_glyph_transform_slant(FT_GlyphSlot glyph, float factor) * * \param factor: -1 (min width) <= 0 (normal) => 1 (max width). */ -static bool UNUSED_FUNCTION(blf_glyph_transform_width)(FT_GlyphSlot glyph, float factor) +static bool blf_glyph_transform_width(FT_GlyphSlot glyph, float factor) { if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { float scale = (factor * 0.4f) + 1.0f; /* 0.6f - 1.4f */ @@ -739,6 +910,22 @@ static bool UNUSED_FUNCTION(blf_glyph_transform_width)(FT_GlyphSlot glyph, float } /** + * Change glyph advance to alter letter-spacing (tracking). + * + * \param factor: -1 (min tightness) <= 0 (normal) => 1 (max looseness). + */ +static bool blf_glyph_transform_spacing(FT_GlyphSlot glyph, float factor) +{ + if (glyph->advance.x > 0) { + const FontBLF *font = (FontBLF *)glyph->face->generic.data; + const long int size = font->ft_size->metrics.height; + glyph->advance.x += (FT_Pos)(factor * (float)size / 6.0f); + return true; + } + return false; +} + +/** * Transform glyph to fit nicely within a fixed column width. */ static bool blf_glyph_transform_monospace(FT_GlyphSlot glyph, int width) @@ -780,13 +967,51 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font, int fixed_width) { if (glyph_font != settings_font) { - FT_Set_Char_Size(glyph_font->face, - 0, - ((FT_F26Dot6)(settings_font->size)) * 64, - settings_font->dpi, - settings_font->dpi); - glyph_font->size = settings_font->size; - glyph_font->dpi = settings_font->dpi; + blf_font_size(glyph_font, settings_font->size, settings_font->dpi); + } + + blf_ensure_size(glyph_font); + + /* We need to keep track if changes are still needed. */ + bool weight_done = false; + bool slant_done = false; + bool width_done = false; + bool spacing_done = false; + + /* 70% of maximum weight results in the same amount of boldness and horizontal + * expansion as the bold version `DejaVuSans-Bold.ttf` of our default font. + * Worth reevaluating if we change default font. */ + float weight = (settings_font->flags & BLF_BOLD) ? 0.7f : settings_font->char_weight; + + /* 37.5% of maximum rightward slant results in 6 degree slope, matching italic + * version `DejaVuSans-Oblique.ttf` of our current font. But a nice median when + * checking others. Worth reevaluating if we change default font. We could also + * narrow the glyph slightly as most italics do, but this one does not. */ + float slant = (settings_font->flags & BLF_ITALIC) ? 0.375f : settings_font->char_slant; + + float width = settings_font->char_width; + float spacing = settings_font->char_spacing; + + /* Font variations need to be set before glyph loading. Even if new value is zero. */ + + if (glyph_font->variations) { + FT_Fixed coords[BLF_VARIATIONS_MAX]; + /* Load current design coordinates. */ + FT_Get_Var_Design_Coordinates(glyph_font->face, BLF_VARIATIONS_MAX, &coords[0]); + /* Update design coordinates with new values. */ + weight_done = blf_glyph_set_variation_normalized( + glyph_font, coords, BLF_VARIATION_AXIS_WEIGHT, weight); + slant_done = blf_glyph_set_variation_normalized( + glyph_font, coords, BLF_VARIATION_AXIS_SLANT, slant); + width_done = blf_glyph_set_variation_normalized( + glyph_font, coords, BLF_VARIATION_AXIS_WIDTH, width); + spacing_done = blf_glyph_set_variation_normalized( + glyph_font, coords, BLF_VARIATION_AXIS_SPACING, spacing); + /* Optical size, if available, is set to current font size. */ + blf_glyph_set_variation_float( + glyph_font, coords, BLF_VARIATION_AXIS_OPTSIZE, settings_font->size); + /* Save updated design coordinates. */ + FT_Set_Var_Design_Coordinates(glyph_font->face, BLF_VARIATIONS_MAX, &coords[0]); } FT_GlyphSlot glyph = blf_glyph_load(glyph_font, glyph_index); @@ -798,19 +1023,19 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font, blf_glyph_transform_monospace(glyph, BLI_wcwidth((char32_t)charcode) * fixed_width); } - if ((settings_font->flags & BLF_ITALIC) != 0) { - /* 37.5% of maximum rightward slant results in 6 degree slope, matching italic - * version (`DejaVuSans-Oblique.ttf`) of our current font. But a nice median when - * checking others. Worth reevaluating if we change default font. We could also - * narrow the glyph slightly as most italics do, but this one does not. */ - blf_glyph_transform_slant(glyph, 0.375f); - } + /* Fallback glyph transforms, but only if required and not yet done. */ - if ((settings_font->flags & BLF_BOLD) != 0) { - /* 70% of maximum weight results in the same amount of boldness and horizontal - * expansion as the bold version (`DejaVuSans-Bold.ttf`) of our default font. - * Worth reevaluating if we change default font. */ - blf_glyph_transform_weight(glyph, 0.7f, glyph->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH); + if (weight != 0.0f && !weight_done) { + blf_glyph_transform_weight(glyph, weight, FT_IS_FIXED_WIDTH(glyph_font)); + } + if (slant != 0.0f && !slant_done) { + blf_glyph_transform_slant(glyph, slant); + } + if (width != 0.0f && !width_done) { + blf_glyph_transform_width(glyph, width); + } + if (spacing != 0.0f && !spacing_done) { + blf_glyph_transform_spacing(glyph, spacing); } if (blf_glyph_render_bitmap(glyph_font, glyph)) { @@ -819,7 +1044,7 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font, return NULL; } -GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) +GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, const uint charcode) { GlyphBLF *g = blf_glyph_cache_find_glyph(gc, charcode); if (g) { @@ -830,11 +1055,9 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) FontBLF *font_with_glyph = font; FT_UInt glyph_index = blf_glyph_index_from_charcode(&font_with_glyph, charcode); - /* Glyphs are dynamically created as needed by font rendering. this means that - * to make font rendering thread safe we have to do locking here. note that this - * must be a lock for the whole library and not just per font, because the font - * renderer uses a shared buffer internally. */ - BLI_spin_lock(font_with_glyph->ft_lib_mutex); + if (!blf_ensure_face(font_with_glyph)) { + return NULL; + } FT_GlyphSlot glyph = blf_glyph_render( font, font_with_glyph, glyph_index, charcode, gc->fixed_width); @@ -844,7 +1067,6 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) g = blf_glyph_cache_add_glyph(font, gc, glyph, charcode, glyph_index); } - BLI_spin_unlock(font_with_glyph->ft_lib_mutex); return g; } @@ -893,7 +1115,7 @@ static void blf_glyph_calc_rect_shadow( /** \name Glyph Drawing * \{ */ -static void blf_texture_draw(const unsigned char color[4], +static void blf_texture_draw(const uchar color[4], const int glyph_size[2], const int offset, const int x1, @@ -919,7 +1141,7 @@ static void blf_texture_draw(const unsigned char color[4], } } -static void blf_texture5_draw(const unsigned char color_in[4], +static void blf_texture5_draw(const uchar color_in[4], const int glyph_size[2], const int offset, const int x1, @@ -935,7 +1157,7 @@ static void blf_texture5_draw(const unsigned char color_in[4], blf_texture_draw(color_in, glyph_size_flag, offset, x1, y1, x2, y2); } -static void blf_texture3_draw(const unsigned char color_in[4], +static void blf_texture3_draw(const uchar color_in[4], const int glyph_size[2], const int offset, const int x1, diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 84037ff4bd0..39d3af22562 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -14,9 +14,16 @@ struct ResultBLF; struct rctf; struct rcti; -/* Max number of fonts in memory. Take care that every font has a glyph cache per size/dpi, +/* Max number of FontBLFs in memory. Take care that every font has a glyph cache per size/dpi, * so we don't need load the same font with different size, just load one and call BLF_size. */ -#define BLF_MAX_FONT 32 +#define BLF_MAX_FONT 64 + +/* Maximum number of opened FT_Face objects managed by cache. 0 is default of 2. */ +#define BLF_CACHE_MAX_FACES 4 +/* Maximum number of opened FT_Size objects managed by cache. 0 is default of 4 */ +#define BLF_CACHE_MAX_SIZES 8 +/* Maximum number of bytes to use for cached data nodes. 0 is default of 200,000. */ +#define BLF_CACHE_BYTES 400000 extern struct FontBLF *global_font[BLF_MAX_FONT]; @@ -39,12 +46,26 @@ void blf_font_exit(void); bool blf_font_id_is_valid(int fontid); +/** + * Return glyph id from char-code. + */ +uint blf_get_char_index(struct FontBLF *font, uint charcode); + +bool blf_ensure_face(struct FontBLF *font); +void blf_ensure_size(struct FontBLF *font); + void blf_draw_buffer__start(struct FontBLF *font); void blf_draw_buffer__end(void); +struct FontBLF *blf_font_new_ex(const char *name, + const char *filepath, + const unsigned char *mem, + size_t mem_size, + void *ft_library); + struct FontBLF *blf_font_new(const char *name, const char *filepath); -struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size); -void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, int mem_size); +struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, size_t mem_size); +void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, size_t mem_size); /** * Change font's output size. Returns true if successful in changing the size. diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 998093dae70..d64bd9c5452 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -10,6 +10,20 @@ #include "GPU_texture.h" #include "GPU_vertex_buffer.h" +#include FT_MULTIPLE_MASTERS_H /* Variable font support. */ + +/** Maximum variation axes per font. */ +#define BLF_VARIATIONS_MAX 16 + +#define MAKE_DVAR_TAG(a, b, c, d) \ + (((uint32_t)a << 24u) | ((uint32_t)b << 16u) | ((uint32_t)c << 8u) | ((uint32_t)d)) + +#define BLF_VARIATION_AXIS_WEIGHT MAKE_DVAR_TAG('w', 'g', 'h', 't') /* 'wght' weight axis. */ +#define BLF_VARIATION_AXIS_SLANT MAKE_DVAR_TAG('s', 'l', 'n', 't') /* 'slnt' slant axis. */ +#define BLF_VARIATION_AXIS_WIDTH MAKE_DVAR_TAG('w', 'd', 't', 'h') /* 'wdth' width axis. */ +#define BLF_VARIATION_AXIS_SPACING MAKE_DVAR_TAG('s', 'p', 'a', 'c') /* 'spac' spacing axis. */ +#define BLF_VARIATION_AXIS_OPTSIZE MAKE_DVAR_TAG('o', 'p', 's', 'z') /* 'opsz' optical size. */ + /* -------------------------------------------------------------------- */ /** \name Sub-Pixel Offset & Utilities * @@ -25,10 +39,12 @@ typedef int32_t ft_pix; /* Macros copied from `include/freetype/internal/ftobjs.h`. */ -/* FIXME(@campbellbarton): Follow rounding from Blender 3.1x and older. +/** + * FIXME(@campbellbarton): Follow rounding from Blender 3.1x and older. * This is what users will expect and changing this creates wider spaced text. * Use this macro to communicate that rounding should be used, using floor is to avoid - * user visible changes, which can be reviewed and handled separately. */ + * user visible changes, which can be reviewed and handled separately. + */ #define USE_LEGACY_SPACING #define FT_PIX_FLOOR(x) ((x) & ~63) @@ -72,7 +88,7 @@ BLI_INLINE ft_pix ft_pix_from_float(float v) BLI_INLINE ft_pix ft_pix_round_advance(ft_pix v, ft_pix step) { - /* See #USE_LEGACY_SPACING, rounding logic could change here. */ + /** See #USE_LEGACY_SPACING, rounding logic could change here. */ return FT_PIX_DEFAULT_ROUNDING(v) + FT_PIX_DEFAULT_ROUNDING(step); } @@ -84,24 +100,27 @@ BLI_INLINE ft_pix ft_pix_round_advance(ft_pix v, ft_pix step) #define BLF_BATCH_DRAW_LEN_MAX 2048 /* in glyph */ -/* Number of characters in GlyphCacheBLF.glyph_ascii_table. */ +/** Number of characters in #GlyphCacheBLF.glyph_ascii_table. */ #define GLYPH_ASCII_TABLE_SIZE 128 -/* Number of characters in KerningCacheBLF.table. */ +/** Number of characters in #KerningCacheBLF.table. */ #define KERNING_CACHE_TABLE_SIZE 128 -/* A value in the kerning cache that indicates it is not yet set. */ +/** A value in the kerning cache that indicates it is not yet set. */ #define KERNING_ENTRY_UNSET INT_MAX typedef struct BatchBLF { - struct FontBLF *font; /* can only batch glyph from the same font */ + /** Can only batch glyph from the same font. */ + struct FontBLF *font; struct GPUBatch *batch; struct GPUVertBuf *verts; struct GPUVertBufRaw pos_step, col_step, offset_step, glyph_size_step; unsigned int pos_loc, col_loc, offset_loc, glyph_size_loc; unsigned int glyph_len; - int ofs[2]; /* copy of font->pos */ - float mat[4][4]; /* previous call modelmatrix. */ + /** Copy of `font->pos`. */ + int ofs[2]; + /* Previous call `modelmatrix`. */ + float mat[4][4]; bool enabled, active, simple_shader; struct GlyphCacheBLF *glyph_cache; } BatchBLF; @@ -120,25 +139,30 @@ typedef struct GlyphCacheBLF { struct GlyphCacheBLF *next; struct GlyphCacheBLF *prev; - /* font size. */ + /** Font size. */ float size; - /* and DPI. */ + /** DPI. */ unsigned int dpi; + float char_weight; + float char_slant; + float char_width; + float char_spacing; + bool bold; bool italic; - /* Column width when printing monospaced. */ + /** Column width when printing monospaced. */ int fixed_width; - /* and the glyphs. */ + /** The glyphs. */ ListBase bucket[257]; - /* fast ascii lookup */ + /** Fast ascii lookup */ struct GlyphBLF *glyph_ascii_table[GLYPH_ASCII_TABLE_SIZE]; - /* texture array, to draw the glyphs. */ + /** Texture array, to draw the glyphs. */ GPUTexture *texture; char *bitmap_result; int bitmap_len; @@ -151,13 +175,13 @@ typedef struct GlyphBLF { struct GlyphBLF *next; struct GlyphBLF *prev; - /* and the character, as UTF-32 */ + /** The character, as UTF-32. */ unsigned int c; - /* freetype2 index, to speed-up the search. */ + /** Freetype2 index, to speed-up the search. */ FT_UInt idx; - /* glyph box. */ + /** Glyph bounding-box. */ ft_pix box_xmin; ft_pix box_xmax; ft_pix box_ymin; @@ -165,19 +189,20 @@ typedef struct GlyphBLF { ft_pix advance_x; - /* The difference in bearings when hinting is active, zero otherwise. */ + /** The difference in bearings when hinting is active, zero otherwise. */ ft_pix lsb_delta; ft_pix rsb_delta; - /* position inside the texture where this glyph is store. */ + /** Position inside the texture where this glyph is store. */ int offset; - /* Bitmap data, from freetype. Take care that this + /** + * Bitmap data, from freetype. Take care that this * can be NULL. */ unsigned char *bitmap; - /* Glyph width and height. */ + /** Glyph width and height. */ int dims[2]; int pitch; @@ -192,52 +217,57 @@ typedef struct GlyphBLF { } GlyphBLF; typedef struct FontBufInfoBLF { - /* for draw to buffer, always set this to NULL after finish! */ + /** For draw to buffer, always set this to NULL after finish! */ float *fbuf; - /* the same but unsigned char */ + /** The same but unsigned char. */ unsigned char *cbuf; /** Buffer size, keep signed so comparisons with negative values work. */ int dims[2]; - /* number of channels. */ + /** Number of channels. */ int ch; - /* display device used for color management */ + /** Display device used for color management. */ struct ColorManagedDisplay *display; - /* and the color, the alphas is get from the glyph! - * color is sRGB space */ + /** The color, the alphas is get from the glyph! (color is sRGB space). */ float col_init[4]; - /* cached conversion from 'col_init' */ + /** Cached conversion from 'col_init'. */ unsigned char col_char[4]; float col_float[4]; } FontBufInfoBLF; typedef struct FontBLF { - /* font name. */ + /** Font name. */ char *name; - /* # of times this font was loaded */ - unsigned int reference_count; - - /** File-path or NULL. */ + /** Full path to font file or NULL if from memory. */ char *filepath; - /* Copied from the SFNT OS/2 table. Bit flags for unicode blocks and ranges + /** Pointer to in-memory font, or NULL if from file. */ + void *mem; + size_t mem_size; + + /** + * Copied from the SFNT OS/2 table. Bit flags for unicode blocks and ranges * considered "functional". Cached here because face might not always exist. - * See: https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur */ - uint UnicodeRanges[4]; + * See: https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur + */ + uint unicode_ranges[4]; - /* aspect ratio or scale. */ + /** Number of times this font was loaded. */ + unsigned int reference_count; + + /** Aspect ratio or scale. */ float aspect[3]; - /* initial position for draw the text. */ + /** Initial position for draw the text. */ int pos[3]; - /* angle in radians. */ + /** Angle in radians. */ float angle; #if 0 /* BLF_BLUR_ENABLE */ @@ -245,40 +275,50 @@ typedef struct FontBLF { int blur; #endif - /* shadow level. */ + /** Shadow level. */ int shadow; - /* and shadow offset. */ + /** And shadow offset. */ int shadow_x; int shadow_y; - /* shadow color. */ + /** Shadow color. */ unsigned char shadow_color[4]; - /* main text color. */ + /** Main text color. */ unsigned char color[4]; - /* Multiplied this matrix with the current one before - * draw the text! see blf_draw__start. + /** + * Multiplied this matrix with the current one before draw the text! + * see #blf_draw_gl__start. */ float m[16]; - /* clipping rectangle. */ + /** Clipping rectangle. */ rcti clip_rec; - /* the width to wrap the text, see BLF_WORD_WRAP */ + /** The width to wrap the text, see #BLF_WORD_WRAP. */ int wrap_width; - /* Font DPI (default 72). */ + /** Font DPI (default 72). */ unsigned int dpi; - /* font size. */ + /** Font size. */ float size; - /* max texture size. */ + /** Axes data for Adobe MM, TrueType GX, or OpenType variation fonts. */ + FT_MM_Var *variations; + + /** Character variation; 0=default, -1=min, +1=max. */ + float char_weight; + float char_slant; + float char_width; + float char_spacing; + + /** Max texture size. */ int tex_size_max; - /* font options. */ + /** Font options. */ int flags; /** @@ -287,29 +327,32 @@ typedef struct FontBLF { */ ListBase cache; - /* Cache of unscaled kerning values. Will be NULL if font does not have kerning. */ + /** Cache of unscaled kerning values. Will be NULL if font does not have kerning. */ KerningCacheBLF *kerning_cache; - /* freetype2 lib handle. */ + /** Freetype2 lib handle. */ FT_Library ft_lib; - /* Mutex lock for library */ - SpinLock *ft_lib_mutex; - - /* freetype2 face. */ + /** Freetype2 face. */ FT_Face face; - /* data for buffer usage (drawing into a texture buffer) */ + /** Point to face->size or to cache's size. */ + FT_Size ft_size; + + /** Copy of the font->face->face_flags, in case we don't have a face loaded. */ + FT_Long face_flags; + + /** Data for buffer usage (drawing into a texture buffer) */ FontBufInfoBLF buf_info; - /* Mutex lock for glyph cache. */ - SpinLock *glyph_cache_mutex; + /** Mutex lock for glyph cache. */ + ThreadMutex glyph_cache_mutex; } FontBLF; typedef struct DirBLF { struct DirBLF *next; struct DirBLF *prev; - /* full path where search fonts. */ + /** Full path where search fonts. */ char *path; } DirBLF; diff --git a/source/blender/blenfont/intern/blf_thumbs.c b/source/blender/blenfont/intern/blf_thumbs.c index a75072f854f..bafa927a440 100644 --- a/source/blender/blenfont/intern/blf_thumbs.c +++ b/source/blender/blenfont/intern/blf_thumbs.c @@ -32,26 +32,34 @@ void BLF_thumb_preview(const char *filepath, const char **draw_str, const char **i18n_draw_str, - const unsigned char draw_str_lines, + const uchar draw_str_lines, const float font_color[4], const int font_size, - unsigned char *buf, - int w, - int h, - int channels) + uchar *buf, + const int w, + const int h, + const int channels) { - const unsigned int dpi = 72; + const uint dpi = 72; const int font_size_min = 6; int font_size_curr; /* shrink 1/th each line */ int font_shrink = 4; - FontBLF *font; + /* While viewing thumbnails in font directories this function can be called simultaneously from a + * greater number of threads than we want the FreeType cache to keep open at a time. Therefore + * pass own FT_Library to font creation so that it is not managed by the FreeType cache system. + */ - /* Create a new blender font obj and fill it with default values */ - font = blf_font_new("thumb_font", filepath); + FT_Library ft_library = NULL; + if (FT_Init_FreeType(&ft_library) != FT_Err_Ok) { + return; + } + + FontBLF *font = blf_font_new_ex("thumb_font", filepath, NULL, 0, ft_library); if (!font) { printf("Info: Can't load font '%s', no preview possible\n", filepath); + FT_Done_FreeType(ft_library); return; } @@ -87,7 +95,7 @@ void BLF_thumb_preview(const char *filepath, font->pos[1] -= (int)((float)blf_font_ascender(font) * 1.1f); /* We fallback to default english strings in case not enough chars are available in current - * font for given translated string (useful in non-latin i18n context, like Chinese, + * font for given translated string (useful in non-Latin i18n context, like Chinese, * since many fonts will then show nothing but ugly 'missing char' in their preview). * Does not handle all cases, but much better than nothing. */ @@ -102,4 +110,5 @@ void BLF_thumb_preview(const char *filepath, blf_draw_buffer__end(); blf_font_free(font); + FT_Done_FreeType(ft_library); } |