BLF: Fonts with FT_Face Optional
Allow FontBLFs to exist with NULL FT_Face, added only when actually needed. Speeds up startup and unused fonts are not loaded. See D15258 for more details. Differential Revision: https://developer.blender.org/D15258 Reviewed by Brecht Van Lommel
This commit is contained in:
parent
848dd4a40a
commit
c0845abd89
Notes:
blender-bot
2023-12-22 20:14:11 +01:00
Referenced by commit 09640ab291
, Fix T99872: Crash Loading Embedded Fonts - Master
Referenced by issue #99872, Regression: Crashes when using packed text font in VSE
|
@ -351,6 +351,8 @@ enum {
|
|||
BLF_DEFAULT = 1 << 14,
|
||||
/** Must only be used as last font in the stack. */
|
||||
BLF_LAST_RESORT = 1 << 15,
|
||||
/** Failure to load this font. Don't try again. */
|
||||
BLF_BAD_FONT = 1 << 16,
|
||||
};
|
||||
|
||||
#define BLF_DRAW_STR_DUMMY_MAX 1024
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
|
||||
#include FT_FREETYPE_H
|
||||
#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 /* Codepoint coverage constants. */
|
||||
#include FT_TRUETYPE_TABLES_H /* For TT_OS2 */
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
|
@ -28,6 +29,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"
|
||||
|
@ -1157,7 +1159,7 @@ int blf_font_ascender(FontBLF *font)
|
|||
|
||||
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);
|
||||
|
@ -1244,14 +1246,28 @@ static void blf_font_fill(FontBLF *font)
|
|||
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;
|
||||
FT_Error err;
|
||||
char *mfile;
|
||||
if (font->face) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (font->flags & BLF_BAD_FONT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FT_Error err;
|
||||
|
||||
if (font->filepath) {
|
||||
err = FT_New_Face(ft_lib, font->filepath, 0, &font->face);
|
||||
}
|
||||
if (font->mem) {
|
||||
err = FT_New_Memory_Face(ft_lib, font->mem, (FT_Long)font->mem_size, 0, &font->face);
|
||||
}
|
||||
|
||||
font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new");
|
||||
err = FT_New_Face(ft_lib, filepath, 0, &font->face);
|
||||
if (err) {
|
||||
if (ELEM(err, FT_Err_Unknown_File_Format, FT_Err_Unimplemented_Feature)) {
|
||||
printf("Format of this font file is not supported\n");
|
||||
|
@ -1259,8 +1275,8 @@ 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;
|
||||
}
|
||||
|
||||
err = FT_Select_Charmap(font->face, FT_ENCODING_UNICODE);
|
||||
|
@ -1272,28 +1288,28 @@ 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);
|
||||
}
|
||||
|
||||
if (FT_HAS_MULTIPLE_MASTERS(font->face)) {
|
||||
FT_Get_MM_Var(font->face, &(font->variations));
|
||||
}
|
||||
|
||||
font->name = BLI_strdup(name);
|
||||
font->filepath = BLI_strdup(filepath);
|
||||
blf_font_fill(font);
|
||||
|
||||
/* 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) {
|
||||
|
@ -1303,17 +1319,11 @@ FontBLF *blf_font_new(const char *name, const char *filepath)
|
|||
font->UnicodeRanges[3] = (uint)os2_table->ulUnicodeRange4;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (FT_IS_FIXED_WIDTH(font->face)) {
|
||||
font->flags |= BLF_MONOSPACED;
|
||||
}
|
||||
|
||||
if (FT_HAS_KERNING(font->face)) {
|
||||
if (FT_HAS_KERNING(font->face) && !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++) {
|
||||
|
@ -1323,9 +1333,104 @@ FontBLF *blf_font_new(const char *name, const char *filepath)
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct eFaceDetails {
|
||||
char name[50];
|
||||
unsigned int coverage1;
|
||||
unsigned int coverage2;
|
||||
unsigned int coverage3;
|
||||
unsigned int coverage4;
|
||||
} eFaceDetails;
|
||||
|
||||
/* Details about the fallback fonts we ship, so that we can load only when needed. */
|
||||
static const eFaceDetails 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, 0, 0, 0},
|
||||
{"NotoSansArmenian-VariableFont_wdth,wght.woff2",
|
||||
TT_UCR_ARMENIAN,
|
||||
TT_UCR_ALPHABETIC_PRESENTATION_FORMS,
|
||||
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-VariableFont_wdth,wght.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 from passed memory pointer. */
|
||||
FontBLF *blf_font_new_ex(const char *name,
|
||||
const char *filepath,
|
||||
const unsigned char *mem,
|
||||
int mem_size)
|
||||
{
|
||||
FontBLF *font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new");
|
||||
|
||||
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);
|
||||
|
||||
/* If we have static details about this font we don't need to load the Face. */
|
||||
const eFaceDetails *static_details = NULL;
|
||||
char filename[256];
|
||||
for (int i = 0; i < 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->UnicodeRanges[0] = static_details->coverage1;
|
||||
font->UnicodeRanges[1] = static_details->coverage2;
|
||||
font->UnicodeRanges[2] = static_details->coverage3;
|
||||
font->UnicodeRanges[3] = static_details->coverage4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!static_details) {
|
||||
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->UnicodeRanges[0] == 0xffffffffU && font->UnicodeRanges[1] == 0xffffffffU &&
|
||||
font->UnicodeRanges[2] == 0xffffffffU && font->UnicodeRanges[3] >= 0x7FFFFFFU) {
|
||||
font->flags |= BLF_LAST_RESORT;
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
FontBLF *blf_font_new(const char *name, const char *filename)
|
||||
{
|
||||
return blf_font_new_ex(name, filename, NULL, 0);
|
||||
}
|
||||
|
||||
FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size)
|
||||
{
|
||||
return blf_font_new_ex(name, NULL, mem, mem_size);
|
||||
}
|
||||
|
||||
void blf_font_attach_from_mem(FontBLF *font, const unsigned char *mem, int mem_size)
|
||||
{
|
||||
FT_Open_Args open;
|
||||
|
@ -1336,36 +1441,6 @@ void blf_font_attach_from_mem(FontBLF *font, const unsigned char *mem, int mem_s
|
|||
FT_Attach_Stream(font->face, &open);
|
||||
}
|
||||
|
||||
FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size)
|
||||
{
|
||||
FontBLF *font;
|
||||
FT_Error err;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 (FT_HAS_MULTIPLE_MASTERS(font->face)) {
|
||||
FT_Get_MM_Var(font->face, &(font->variations));
|
||||
}
|
||||
|
||||
font->name = BLI_strdup(name);
|
||||
font->filepath = NULL;
|
||||
blf_font_fill(font);
|
||||
return font;
|
||||
}
|
||||
|
||||
void blf_font_free(FontBLF *font)
|
||||
{
|
||||
blf_glyph_cache_clear(font);
|
||||
|
@ -1378,7 +1453,9 @@ void blf_font_free(FontBLF *font)
|
|||
FT_Done_MM_Var(ft_lib, font->variations);
|
||||
}
|
||||
|
||||
FT_Done_Face(font->face);
|
||||
if (font->face) {
|
||||
FT_Done_Face(font->face);
|
||||
}
|
||||
if (font->filepath) {
|
||||
MEM_freeN(font->filepath);
|
||||
}
|
||||
|
@ -1396,6 +1473,10 @@ void blf_font_free(FontBLF *font)
|
|||
|
||||
bool blf_font_size(FontBLF *font, float size, unsigned int dpi)
|
||||
{
|
||||
if (!blf_ensure_face(font)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* FreeType uses fixed-point integers in 64ths. */
|
||||
FT_F26Dot6 ft_size = lroundf(size * 64.0f);
|
||||
/* Adjust our new size to be on even 64ths. */
|
||||
|
|
|
@ -66,8 +66,6 @@ void BLF_load_font_stack()
|
|||
}
|
||||
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. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -584,16 +584,18 @@ 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);
|
||||
if (glyph_index) {
|
||||
*font = f;
|
||||
return glyph_index;
|
||||
if (blf_ensure_face(f)) {
|
||||
glyph_index = FT_Get_Char_Index(f->face, charcode);
|
||||
if (glyph_index) {
|
||||
*font = f;
|
||||
return glyph_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found in the stack, return from Last Resort if there is one. */
|
||||
if (last_resort) {
|
||||
if (last_resort && blf_ensure_face(last_resort)) {
|
||||
glyph_index = FT_Get_Char_Index(last_resort->face, charcode);
|
||||
if (glyph_index) {
|
||||
*font = last_resort;
|
||||
|
|
|
@ -39,6 +39,8 @@ void blf_font_exit(void);
|
|||
|
||||
bool blf_font_id_is_valid(int fontid);
|
||||
|
||||
bool blf_ensure_face(struct FontBLF *font);
|
||||
|
||||
void blf_draw_buffer__start(struct FontBLF *font);
|
||||
void blf_draw_buffer__end(void);
|
||||
|
||||
|
|
|
@ -240,9 +240,13 @@ typedef struct FontBLF {
|
|||
/* # 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;
|
||||
|
||||
/* 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 */
|
||||
|
|
Loading…
Reference in New Issue