Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenfont/intern')
-rw-r--r--source/blender/blenfont/intern/blf.c56
-rw-r--r--source/blender/blenfont/intern/blf_font.c511
-rw-r--r--source/blender/blenfont/intern/blf_font_default.c64
-rw-r--r--source/blender/blenfont/intern/blf_glyph.c739
-rw-r--r--source/blender/blenfont/intern/blf_internal.h31
-rw-r--r--source/blender/blenfont/intern/blf_internal_types.h166
-rw-r--r--source/blender/blenfont/intern/blf_thumbs.c29
7 files changed, 1316 insertions, 280 deletions
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c
index a944ab332bd..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"
@@ -34,13 +35,6 @@
#include "blf_internal.h"
#include "blf_internal_types.h"
-/* Max number of font in memory.
- * Take care that now every font have 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 16
-
#define BLF_RESULT_CHECK_INIT(r_info) \
if (r_info) { \
memset(r_info, 0, sizeof(*(r_info))); \
@@ -48,7 +42,7 @@
((void)0)
/* Font array. */
-static FontBLF *global_font[BLF_MAX_FONT] = {NULL};
+FontBLF *global_font[BLF_MAX_FONT] = {NULL};
/* XXX: should these be made into global_font_'s too? */
@@ -105,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;
}
@@ -129,11 +123,16 @@ 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;
}
+bool BLF_is_loaded(const char *name)
+{
+ return blf_search(name) >= 0;
+}
+
int BLF_load(const char *name)
{
/* check if we already load this font. */
@@ -255,6 +254,20 @@ void BLF_unload_id(int fontid)
}
}
+void BLF_unload_all(void)
+{
+ for (int i = 0; i < BLF_MAX_FONT; i++) {
+ FontBLF *font = global_font[i];
+ if (font) {
+ blf_font_free(font);
+ global_font[i] = NULL;
+ }
+ }
+ blf_mono_font = -1;
+ blf_mono_font_render = -1;
+ BLF_default_set(-1);
+}
+
void BLF_enable(int fontid, int option)
{
FontBLF *font = blf_get(fontid);
@@ -471,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
@@ -499,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();
@@ -873,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 a170f27d247..b96c01e704d 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -17,7 +17,11 @@
#include <ft2build.h>
#include FT_FREETYPE_H
+#include FT_CACHE_H /* FreeType Cache. */
#include FT_GLYPH_H
+#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"
@@ -26,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"
@@ -50,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;
@@ -61,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;
@@ -142,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);
}
@@ -150,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;
@@ -172,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));
}
}
@@ -198,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;
@@ -292,7 +373,7 @@ 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. */
@@ -301,7 +382,7 @@ BLI_INLINE ft_pix blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const Glyph
}
/* 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);
}
@@ -331,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;
@@ -432,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;
@@ -521,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];
@@ -834,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;
}
@@ -921,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);
@@ -1079,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++;
}
}
@@ -1103,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)
@@ -1124,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)
@@ -1145,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);
@@ -1170,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();
}
@@ -1236,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");
@@ -1257,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);
@@ -1270,25 +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;
+ }
+
+ font->face_flags = font->face->face_flags;
+
+ if (FT_HAS_MULTIPLE_MASTERS(font)) {
+ FT_Get_MM_Var(font->face, &(font->variations));
+ }
- if (FT_HAS_KERNING(font->face)) {
+ /* 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)) {
+ font->flags |= BLF_MONOSPACED;
+ }
+
+ 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++) {
@@ -1298,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);
@@ -1345,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);
}
@@ -1361,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 3a68423e64e..d35692f6eae 100644
--- a/source/blender/blenfont/intern/blf_font_default.c
+++ b/source/blender/blenfont/intern/blf_font_default.c
@@ -11,13 +11,18 @@
#include "BLF_api.h"
+#include "BLI_fileops.h"
#include "BLI_path_util.h"
#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, "fonts");
+ const char *dir = BKE_appdir_folder_id(BLENDER_DATAFILES, BLF_DATAFILES_FONTS_DIR);
if (dir == NULL) {
fprintf(stderr,
"%s: 'fonts' data path not found for '%s', will not be able to display text\n",
@@ -34,10 +39,63 @@ static int blf_load_font_default(const char *filename, const bool unique)
int BLF_load_default(const bool unique)
{
- return blf_load_font_default("droidsans.ttf", unique);
+ int font_id = blf_load_font_default(BLF_DEFAULT_PROPORTIONAL_FONT, unique);
+ BLF_enable(font_id, BLF_DEFAULT);
+ return font_id;
}
int BLF_load_mono_default(const bool unique)
{
- return blf_load_font_default("bmonofont-i18n.ttf", unique);
+ int font_id = blf_load_font_default(BLF_DEFAULT_MONOSPACED_FONT, unique);
+ BLF_enable(font_id, BLF_MONOSPACED | BLF_DEFAULT);
+ 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);
+ blf_load_datafiles_dir();
}
diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c
index 2694b179a11..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,13 +30,20 @@
#include "BLF_api.h"
#include "GPU_capabilities.h"
-#include "GPU_immediate.h"
#include "blf_internal.h"
#include "blf_internal_types.h"
#include "BLI_math_vector.h"
#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
@@ -58,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;
@@ -81,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;
@@ -107,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);
@@ -120,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)
@@ -144,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);
}
/**
@@ -158,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];
@@ -174,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.
*/
@@ -209,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;
@@ -222,17 +280,402 @@ static GlyphBLF *blf_glyph_cache_add_glyph(
return g;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \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 */
+};
+
+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. */
+ {0x100, 0x17F, 2}, /* Latin Extended-A. */
+ {0x180, 0x24F, 3}, /* Latin Extended-B. */
+ {0x250, 0x2AF, 4}, /* IPA Extensions. */
+ {0x2B0, 0x2FF, 5}, /* Spacing Modifier Letters. */
+ {0x300, 0x36F, 6}, /* Combining Diacritical Marks. */
+ {0x370, 0x3FF, 7}, /* Greek. */
+ {0x400, 0x52F, 9}, /* Cyrillic. */
+ {0x530, 0x58F, 10}, /* Armenian. */
+ {0x590, 0x5FF, 11}, /* Hebrew. */
+ {0x600, 0x6FF, 13}, /* Arabic. */
+ {0x700, 0x74F, 71}, /* Syriac. */
+ {0x750, 0x77F, 13}, /* Arabic Supplement. */
+ {0x780, 0x7BF, 72}, /* Thaana. */
+ {0x7C0, 0x7FF, 14}, /* NKo. */
+ {0x800, 0x83F, -1}, /* Samaritan. */
+ {0x840, 0x85F, -1}, /* Mandaic. */
+ {0x900, 0x97F, 15}, /* Devanagari. */
+ {0x980, 0x9FF, 16}, /* Bengali. */
+ {0xA00, 0xA7F, 17}, /* Gurmukhi. */
+ {0xA80, 0xAFF, 18}, /* Gujarati. */
+ {0xB00, 0xB7F, 19}, /* Oriya. */
+ {0xB80, 0xBFF, 20}, /* Tamil. */
+ {0xC00, 0xC7F, 21}, /* Telugu. */
+ {0xC80, 0xCFF, 22}, /* Kannada. */
+ {0xD00, 0xD7F, 23}, /* Malayalam. */
+ {0xD80, 0xDFF, 73}, /* Sinhala. */
+ {0xE00, 0xE7F, 24}, /* Thai. */
+ {0xE80, 0xEFF, 25}, /* Lao. */
+ {0xF00, 0xFFF, 70}, /* Tibetan. */
+ {0x1000, 0x109F, 74}, /* Myanmar. */
+ {0x10A0, 0x10FF, 26}, /* Georgian. */
+ {0x1100, 0x11FF, 28}, /* Hangul Jamo. */
+ {0x1200, 0x139F, 75}, /* Ethiopic. */
+ {0x13A0, 0x13FF, 76}, /* Cherokee. */
+ {0x1400, 0x167F, 77}, /* Canadian Aboriginal. */
+ {0x1680, 0x169F, 78}, /* Ogham. */
+ {0x16A0, 0x16FF, 79}, /* unic. */
+ {0x1700, 0x171F, 84}, /* Tagalog. */
+ {0x1720, 0x173F, 84}, /* Hanunoo. */
+ {0x1740, 0x175F, 84}, /* Buhid. */
+ {0x1760, 0x177F, 84}, /* Tagbanwa. */
+ {0x1780, 0x17FF, 80}, /* Khmer. */
+ {0x1800, 0x18AF, 81}, /* Mongolian. */
+ {0x1900, 0x194F, 93}, /* Limbu. */
+ {0x1950, 0x197F, 94}, /* Tai Le. */
+ {0x1980, 0x19DF, 95}, /* New Tai Lue". */
+ {0x19E0, 0x19FF, 80}, /* Khmer. */
+ {0x1A00, 0x1A1F, 96}, /* Buginese. */
+ {0x1A20, 0x1AAF, -1}, /* Tai Tham. */
+ {0x1B00, 0x1B7F, 27}, /* Balinese. */
+ {0x1B80, 0x1BBF, 112}, /* Sundanese. */
+ {0x1BC0, 0x1BFF, -1}, /* Batak. */
+ {0x1C00, 0x1C4F, 113}, /* Lepcha. */
+ {0x1C50, 0x1C7F, 114}, /* Ol Chiki. */
+ {0x1D00, 0x1DBF, 4}, /* IPA Extensions. */
+ {0x1DC0, 0x1DFF, 6}, /* Combining Diacritical Marks. */
+ {0x1E00, 0x1EFF, 29}, /* Latin Extended Additional. */
+ {0x1F00, 0x1FFF, 30}, /* Greek Extended. */
+ {0x2000, 0x206F, 31}, /* General Punctuation. */
+ {0x2070, 0x209F, 32}, /* Superscripts And Subscripts. */
+ {0x20A0, 0x20CF, 33}, /* Currency Symbols. */
+ {0x20D0, 0x20FF, 34}, /* Combining Diacritical Marks For Symbols. */
+ {0x2100, 0x214F, 35}, /* Letterlike Symbols. */
+ {0x2150, 0x218F, 36}, /* Number Forms. */
+ {0x2190, 0x21FF, 37}, /* Arrows. */
+ {0x2200, 0x22FF, 38}, /* Mathematical Operators. */
+ {0x2300, 0x23FF, 39}, /* Miscellaneous Technical. */
+ {0x2400, 0x243F, 40}, /* Control Pictures. */
+ {0x2440, 0x245F, 41}, /* Optical Character Recognition. */
+ {0x2460, 0x24FF, 42}, /* Enclosed Alphanumerics. */
+ {0x2500, 0x257F, 43}, /* Box Drawing. */
+ {0x2580, 0x259F, 44}, /* Block Elements. */
+ {0x25A0, 0x25FF, 45}, /* Geometric Shapes. */
+ {0x2600, 0x26FF, 46}, /* Miscellaneous Symbols. */
+ {0x2700, 0x27BF, 47}, /* Dingbats. */
+ {0x27C0, 0x27EF, 38}, /* Mathematical Operators. */
+ {0x27F0, 0x27FF, 37}, /* Arrows. */
+ {0x2800, 0x28FF, 82}, /* Braille. */
+ {0x2900, 0x297F, 37}, /* Arrows. */
+ {0x2980, 0x2AFF, 38}, /* Mathematical Operators. */
+ {0x2B00, 0x2BFF, 37}, /* Arrows. */
+ {0x2C00, 0x2C5F, 97}, /* Glagolitic. */
+ {0x2C60, 0x2C7F, 29}, /* Latin Extended Additional. */
+ {0x2C80, 0x2CFF, 8}, /* Coptic. */
+ {0x2D00, 0x2D2F, 26}, /* Georgian. */
+ {0x2D30, 0x2D7F, 98}, /* Tifinagh. */
+ {0x2D80, 0x2DDF, 75}, /* Ethiopic. */
+ {0x2DE0, 0x2DFF, 9}, /* Cyrillic. */
+ {0x2E00, 0x2E7F, 31}, /* General Punctuation. */
+ {0x2E80, 0x2FFF, 59}, /* CJK Unified Ideographs. */
+ {0x3000, 0x303F, 48}, /* CJK Symbols And Punctuation. */
+ {0x3040, 0x309F, 49}, /* Hiragana. */
+ {0x30A0, 0x30FF, 50}, /* Katakana. */
+ {0x3100, 0x312F, 51}, /* Bopomofo. */
+ {0x3130, 0x318F, 52}, /* Hangul Compatibility Jamo. */
+ {0x3190, 0x319F, 59}, /* CJK Unified Ideographs. */
+ {0x31A0, 0x31BF, 51}, /* Bopomofo. */
+ {0x31C0, 0x31EF, 59}, /* CJK Unified Ideographs. */
+ {0x31F0, 0x31FF, 50}, /* Katakana. */
+ {0x3200, 0x32FF, 54}, /* Enclosed CJK Letters And Months. */
+ {0x3300, 0x33FF, 55}, /* CJK Compatibility. */
+ {0x3400, 0x4DBF, 59}, /* CJK Unified Ideographs. */
+ {0x4DC0, 0x4DFF, 99}, /* Yijing. */
+ {0x4E00, 0x9FFF, 59}, /* CJK Unified Ideographs. */
+ {0xA000, 0xA4CF, 83}, /* Yi. */
+ {0xA4D0, 0xA4FF, -1}, /* Lisu. */
+ {0xA500, 0xA63F, 12}, /* Vai. */
+ {0xA640, 0xA69F, 9}, /* Cyrillic. */
+ {0xA6A0, 0xA6FF, -1}, /* Bamum. */
+ {0xA700, 0xA71F, 5}, /* Spacing Modifier Letters. */
+ {0xA720, 0xA7FF, 29}, /* Latin Extended Additional. */
+ {0xA800, 0xA82F, 100}, /* Syloti Nagri. */
+ {0xA840, 0xA87F, 53}, /* Phags-pa. */
+ {0xA880, 0xA8DF, 115}, /* Saurashtra. */
+ {0xA900, 0xA92F, 116}, /* Kayah Li. */
+ {0xA930, 0xA95F, 117}, /* Rejang. */
+ {0xA960, 0xA97F, 56}, /* Hangul Syllables. */
+ {0xA980, 0xA9DF, -1}, /* Javanese. */
+ {0xA9E0, 0xA9FF, 74}, /* Myanmar. */
+ {0xAA00, 0xAA5F, 118}, /* Cham. */
+ {0xAA60, 0xAA7F, 74}, /* Myanmar. */
+ {0xAA80, 0xAADF, -1}, /* Tai Viet. */
+ {0xAAE0, 0xAAFF, -1}, /* Meetei Mayek. */
+ {0xAB00, 0xAB2F, 75}, /* Ethiopic. */
+ {0xAB70, 0xABBF, 76}, /* Cherokee. */
+ {0xABC0, 0xABFF, -1}, /* Meetei Mayek. */
+ {0xAC00, 0xD7AF, 56}, /* Hangul Syllables. */
+ {0xD800, 0xDFFF, 57}, /* Non-Plane 0. */
+ {0xE000, 0xF6FF, 60}, /* Private Use Area. */
+ {0xE700, 0xEFFF, -1}, /* MS Wingdings. */
+ {0xF000, 0xF8FF, -1}, /* MS Symbols. */
+ {0xF900, 0xFAFF, 61}, /* CJK Compatibility Ideographs. */
+ {0xFB00, 0xFB4F, 62}, /* Alphabetic Presentation Forms. */
+ {0xFB50, 0xFDFF, 63}, /* Arabic Presentation Forms-A. */
+ {0xFE00, 0xFE0F, 91}, /* Variation Selectors. */
+ {0xFE10, 0xFE1F, 65}, /* CJK Compatibility Forms. */
+ {0xFE20, 0xFE2F, 64}, /* Combining Half Marks. */
+ {0xFE30, 0xFE4F, 65}, /* CJK Compatibility Forms. */
+ {0xFE50, 0xFE6F, 66}, /* Small Form Variants. */
+ {0xFE70, 0xFEFF, 67}, /* Arabic Presentation Forms-B. */
+ {0xFF00, 0xFFEF, 68}, /* Half-width And Full-width Forms. */
+ {0xFFF0, 0xFFFF, 69}, /* Specials. */
+ {0x10000, 0x1013F, 101}, /* Linear B. */
+ {0x10140, 0x1018F, 102}, /* Ancient Greek Numbers. */
+ {0x10190, 0x101CF, 119}, /* Ancient Symbols. */
+ {0x101D0, 0x101FF, 120}, /* Phaistos Disc. */
+ {0x10280, 0x1029F, 121}, /* Lycian. */
+ {0x102A0, 0x102DF, 121}, /* Carian. */
+ {0x10300, 0x1032F, 85}, /* Old Italic. */
+ {0x10330, 0x1034F, 86}, /* Gothic. */
+ {0x10350, 0x1037F, -1}, /* Old Permic. */
+ {0x10380, 0x1039F, 103}, /* Ugaritic. */
+ {0x103A0, 0x103DF, 104}, /* Old Persian. */
+ {0x10400, 0x1044F, 87}, /* Deseret. */
+ {0x10450, 0x1047F, 105}, /* Shavian. */
+ {0x10480, 0x104AF, 106}, /* Osmanya. */
+ {0x104B0, 0x104FF, -1}, /* Osage. */
+ {0x10500, 0x1052F, -1}, /* Elbasan. */
+ {0x10530, 0x1056F, -1}, /* Caucasian Albanian. */
+ {0x10570, 0x105BF, -1}, /* Vithkuqi. */
+ {0x10600, 0x1077F, -1}, /* Linear A. */
+ {0x10780, 0x107BF, 3}, /* Latin Extended-B. */
+ {0x10800, 0x1083F, 107}, /* Cypriot Syllabary. */
+ {0x10840, 0x1085F, -1}, /* Imperial Aramaic. */
+ {0x10860, 0x1087F, -1}, /* Palmyrene. */
+ {0x10880, 0x108AF, -1}, /* Nabataean. */
+ {0x108E0, 0x108FF, -1}, /* Hatran. */
+ {0x10900, 0x1091F, 58}, /* Phoenician. */
+ {0x10920, 0x1093F, 121}, /* Lydian. */
+ {0x10980, 0x1099F, -1}, /* Meroitic Hieroglyphs. */
+ {0x109A0, 0x109FF, -1}, /* Meroitic Cursive. */
+ {0x10A00, 0x10A5F, 108}, /* Kharoshthi. */
+ {0x10A60, 0x10A7F, -1}, /* Old South Arabian. */
+ {0x10A80, 0x10A9F, -1}, /* Old North Arabian. */
+ {0x10AC0, 0x10AFF, -1}, /* Manichaean. */
+ {0x10B00, 0x10B3F, -1}, /* Avestan. */
+ {0x10B40, 0x10B5F, -1}, /* Inscriptional Parthian. */
+ {0x10B60, 0x10B7F, -1}, /* Inscriptional Pahlavi. */
+ {0x10B80, 0x10BAF, -1}, /* Psalter Pahlavi. */
+ {0x10C00, 0x10C4F, -1}, /* Old Turkic. */
+ {0x10C80, 0x10CFF, -1}, /* Old Hungarian. */
+ {0x10D00, 0x10D3F, -1}, /* Hanifi Rohingya. */
+ {0x108E0, 0x10E7F, -1}, /* Rumi Numeral Symbols. */
+ {0x10E80, 0x10EBF, -1}, /* Yezidi. */
+ {0x10F00, 0x10F2F, -1}, /* Old Sogdian. */
+ {0x10F30, 0x10F6F, -1}, /* Sogdian. */
+ {0x10F70, 0x10FAF, -1}, /* Old Uyghur. */
+ {0x10FB0, 0x10FDF, -1}, /* Chorasmian. */
+ {0x10FE0, 0x10FFF, -1}, /* Elymaic. */
+ {0x11000, 0x1107F, -1}, /* Brahmi. */
+ {0x11080, 0x110CF, -1}, /* Kaithi. */
+ {0x110D0, 0x110FF, -1}, /* Sora Sompeng. */
+ {0x11100, 0x1114F, -1}, /* Chakma. */
+ {0x11150, 0x1117F, -1}, /* Mahajani. */
+ {0x11180, 0x111DF, -1}, /* Sharada. */
+ {0x111E0, 0x111FF, -1}, /* Sinhala Archaic Numbers. */
+ {0x11200, 0x1124F, -1}, /* Khojki. */
+ {0x11280, 0x112AF, -1}, /* Multani. */
+ {0x112B0, 0x112FF, -1}, /* Khudawadi. */
+ {0x11300, 0x1137F, -1}, /* Grantha. */
+ {0x11400, 0x1147F, -1}, /* Newa. */
+ {0x11480, 0x114DF, -1}, /* Tirhuta. */
+ {0x11580, 0x115FF, -1}, /* Siddham. */
+ {0x11600, 0x1165F, -1}, /* Modi. */
+ {0x11660, 0x1167F, 81}, /* Mongolian. */
+ {0x11680, 0x116CF, -1}, /* Takri. */
+ {0x11700, 0x1174F, -1}, /* Ahom. */
+ {0x11800, 0x1184F, -1}, /* Dogra. */
+ {0x118A0, 0x118FF, -1}, /* Warang Citi. */
+ {0x11900, 0x1195F, -1}, /* Dives Akuru. */
+ {0x119A0, 0x119FF, -1}, /* Nandinagari. */
+ {0x11A00, 0x11A4F, -1}, /* Zanabazar Square. */
+ {0x11A50, 0x11AAF, -1}, /* Soyombo. */
+ {0x11AB0, 0x11ABF, 77}, /* Canadian Aboriginal Syllabics. */
+ {0x11AC0, 0x11AFF, -1}, /* Pau Cin Hau. */
+ {0x11C00, 0x11C6F, -1}, /* Bhaiksuki. */
+ {0x11C70, 0x11CBF, -1}, /* Marchen. */
+ {0x11D00, 0x11D5F, -1}, /* Masaram Gondi. */
+ {0x11D60, 0x11DAF, -1}, /* Gunjala Gondi. */
+ {0x11EE0, 0x11EFF, -1}, /* Makasar. */
+ {0x11FB0, 0x11FBF, -1}, /* Lisu. */
+ {0x11FC0, 0x11FFF, 20}, /* Tamil. */
+ {0x12000, 0x1254F, 110}, /* Cuneiform. */
+ {0x12F90, 0x12FFF, -1}, /* Cypro-Minoan. */
+ {0x13000, 0x1343F, -1}, /* Egyptian Hieroglyphs. */
+ {0x14400, 0x1467F, -1}, /* Anatolian Hieroglyphs. */
+ {0x16800, 0x16A3F, -1}, /* Bamum. */
+ {0x16A40, 0x16A6F, -1}, /* Mro. */
+ {0x16A70, 0x16ACF, -1}, /* Tangsa. */
+ {0x16AD0, 0x16AFF, -1}, /* Bassa Vah. */
+ {0x16B00, 0x16B8F, -1}, /* Pahawh Hmong. */
+ {0x16E40, 0x16E9F, -1}, /* Medefaidrin. */
+ {0x16F00, 0x16F9F, -1}, /* Miao. */
+ {0x16FE0, 0x16FFF, -1}, /* Ideographic Symbols. */
+ {0x17000, 0x18AFF, -1}, /* Tangut. */
+ {0x1B170, 0x1B2FF, -1}, /* Nushu. */
+ {0x1BC00, 0x1BC9F, -1}, /* Duployan. */
+ {0x1D000, 0x1D24F, 88}, /* Musical Symbols. */
+ {0x1D2E0, 0x1D2FF, -1}, /* Mayan Numerals. */
+ {0x1D300, 0x1D35F, 109}, /* Tai Xuan Jing. */
+ {0x1D360, 0x1D37F, 111}, /* Counting Rod Numerals. */
+ {0x1D400, 0x1D7FF, 89}, /* Mathematical Alphanumeric Symbols. */
+ {0x1E2C0, 0x1E2FF, -1}, /* Wancho. */
+ {0x1E800, 0x1E8DF, -1}, /* Mende Kikakui. */
+ {0x1E900, 0x1E95F, -1}, /* Adlam. */
+ {0x1EC70, 0x1ECBF, -1}, /* Indic Siyaq Numbers. */
+ {0x1F000, 0x1F02F, 122}, /* Mahjong Tiles. */
+ {0x1F030, 0x1F09F, 122}, /* Domino Tiles. */
+ {0x1F600, 0x1F64F, -1}, /* Emoticons. */
+ {0x20000, 0x2A6DF, 59}, /* CJK Unified Ideographs. */
+ {0x2F800, 0x2FA1F, 61}, /* CJK Compatibility Ideographs. */
+ {0xE0000, 0xE007F, 92}, /* Tags. */
+ {0xE0100, 0xE01EF, 91}, /* Variation Selectors. */
+ {0xF0000, 0x10FFFD, 90}}; /* Private Use Supplementary. */
+
+/**
+ * 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. */
+ return &unicode_blocks[0];
+ }
+
+ /* Binary search for other blocks. */
+
+ int min = 0;
+ int max = ARRAY_SIZE(unicode_blocks) - 1;
+
+ if (charcode < unicode_blocks[0].first || charcode > unicode_blocks[max].last) {
+ return NULL;
+ }
+
+ while (max >= min) {
+ const int mid = (min + max) / 2;
+ if (charcode > unicode_blocks[mid].last) {
+ min = mid + 1;
+ }
+ else if (charcode < unicode_blocks[mid].first) {
+ max = mid - 1;
+ }
+ else {
+ return &unicode_blocks[mid];
+ }
+ }
+
+ return NULL;
+}
+
+static int blf_charcode_to_coverage_bit(uint charcode)
+{
+ int coverage_bit = -1;
+ 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 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(const FontBLF *font, int coverage_bit)
+{
+ if (coverage_bit < 0) {
+ return false;
+ }
+ return (font->unicode_ranges[(uint)coverage_bit >> 5] & (1u << ((uint)coverage_bit % 32)));
+}
+
/**
* Return a glyph index from `charcode`. Not found returns zero, which is a valid
* printable character (`.notdef` or `tofu`). Font is allowed to change here.
*/
static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode)
{
- FT_UInt glyph_index = FT_Get_Char_Index((*font)->face, charcode);
- /* TODO: If not found in this font, check others, update font pointer. */
- return glyph_index;
+ 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);
+ for (int i = 0; i < BLF_MAX_FONT; i++) {
+ FontBLF *f = global_font[i];
+ if (!f || f == *font || !(f->flags & BLF_DEFAULT)) {
+ continue;
+ }
+
+ if (f->flags & BLF_LAST_RESORT) {
+ last_resort = f;
+ continue;
+ }
+ if (coverage_bit < 0 || blf_font_has_coverage_bit(f, coverage_bit)) {
+ glyph_index = blf_get_char_index(f, charcode);
+ if (glyph_index) {
+ *font = f;
+ return glyph_index;
+ }
+ }
+ }
+
+#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 = blf_get_char_index(last_resort, charcode);
+ if (glyph_index) {
+ *font = last_resort;
+ return glyph_index;
+ }
+ }
+
+ return 0;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Glyph Load
+ * \{ */
+
/**
* Load a glyph into the glyph slot of a font's face object.
*/
@@ -267,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);
@@ -310,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
* \{ */
@@ -322,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) {
@@ -362,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 */
@@ -375,26 +910,42 @@ 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 UNUSED_FUNCTION(blf_glyph_transform_monospace)(FT_GlyphSlot glyph, int width)
+static bool blf_glyph_transform_monospace(FT_GlyphSlot glyph, int width)
{
if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
- int gwidth = (int)(glyph->linearHoriAdvance >> 16);
- if (gwidth > width) {
- float scale = (float)width / (float)gwidth;
+ FT_Fixed current = glyph->linearHoriAdvance;
+ FT_Fixed target = width << 16; /* Do math in 16.16 values. */
+ if (target < current) {
+ const FT_Pos embolden = (FT_Pos)((current - target) >> 13);
+ /* Horizontally widen strokes to counteract narrowing. */
+ FT_Outline_EmboldenXY(&glyph->outline, embolden, 0);
+ const float scale = (float)(target - (embolden << 9)) / (float)current;
FT_Matrix matrix = {to_16dot16(scale), 0, 0, to_16dot16(1)};
- /* Narrowing all points also thins vertical strokes. */
FT_Outline_Transform(&glyph->outline, &matrix);
- const FT_Pos extra_x = (int)((float)(gwidth - width) * 5.65f);
- /* Horizontally widen strokes to counteract narrowing. */
- FT_Outline_EmboldenXY(&glyph->outline, extra_x, 0);
}
- else if (gwidth < width) {
- /* Narrow glyphs only need to be centered. */
- int nudge = (width - gwidth) / 2;
- FT_Outline_Translate(&glyph->outline, (FT_Pos)nudge * 64, 0);
+ else if (target > current) {
+ /* Center narrow glyphs. */
+ FT_Outline_Translate(&glyph->outline, (FT_Pos)((target - current) >> 11), 0);
}
+ glyph->advance.x = width << 6;
return true;
}
return false;
@@ -411,16 +962,56 @@ static bool UNUSED_FUNCTION(blf_glyph_transform_monospace)(FT_GlyphSlot glyph, i
*/
static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font,
FontBLF *glyph_font,
- FT_UInt glyph_index)
+ FT_UInt glyph_index,
+ uint charcode,
+ 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);
@@ -428,19 +1019,23 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font,
return NULL;
}
- 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);
+ if ((settings_font->flags & BLF_MONOSPACED) && (settings_font != glyph_font)) {
+ blf_glyph_transform_monospace(glyph, BLI_wcwidth((char32_t)charcode) * fixed_width);
}
- 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);
+ /* Fallback glyph transforms, but only if required and not yet done. */
+
+ 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)) {
@@ -449,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) {
@@ -460,20 +1055,18 @@ 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);
+ FT_GlyphSlot glyph = blf_glyph_render(
+ font, font_with_glyph, glyph_index, charcode, gc->fixed_width);
if (glyph) {
/* Save this glyph in the initial font's cache. */
g = blf_glyph_cache_add_glyph(font, gc, glyph, charcode, glyph_index);
}
- BLI_spin_unlock(font_with_glyph->ft_lib_mutex);
return g;
}
@@ -522,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,
@@ -548,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,
@@ -564,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 7754f960043..39d3af22562 100644
--- a/source/blender/blenfont/intern/blf_internal.h
+++ b/source/blender/blenfont/intern/blf_internal.h
@@ -14,6 +14,19 @@ struct ResultBLF;
struct rctf;
struct rcti;
+/* 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 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];
+
void blf_batch_draw_begin(struct FontBLF *font);
void blf_batch_draw(void);
@@ -33,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 9dfcb1a4ad6..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,47 +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;
- /* aspect ratio or scale. */
+ /** 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 unicode_ranges[4];
+
+ /** 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 */
@@ -240,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;
/**
@@ -282,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);
}