diff options
Diffstat (limited to 'source/blender/blenfont')
-rw-r--r-- | source/blender/blenfont/BLF_api.h | 2 | ||||
-rw-r--r-- | source/blender/blenfont/intern/blf.c | 1 | ||||
-rw-r--r-- | source/blender/blenfont/intern/blf_font.c | 859 | ||||
-rw-r--r-- | source/blender/blenfont/intern/blf_glyph.c | 61 | ||||
-rw-r--r-- | source/blender/blenfont/intern/blf_internal.h | 4 | ||||
-rw-r--r-- | source/blender/blenfont/intern/blf_internal_types.h | 40 |
6 files changed, 448 insertions, 519 deletions
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 7e92f79a523..eb3f9805240 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -271,7 +271,7 @@ void BLF_state_print(int fontid); #define BLF_ROTATION (1 << 0) #define BLF_CLIPPING (1 << 1) #define BLF_SHADOW (1 << 2) -#define BLF_KERNING_DEFAULT (1 << 3) +// #define BLF_FLAG_UNUSED_3 (1 << 3) /* dirty */ #define BLF_MATRIX (1 << 4) #define BLF_ASPECT (1 << 5) #define BLF_WORD_WRAP (1 << 6) diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 9168e7aa19c..2f9eb0753ac 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -108,7 +108,6 @@ void BLF_cache_clear(void) FontBLF *font = global_font[i]; if (font) { blf_glyph_cache_clear(font); - blf_kerning_cache_clear(font); } } } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 53c4135254a..75a2e893119 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -72,6 +72,38 @@ static SpinLock ft_lib_mutex; static SpinLock blf_glyph_cache_mutex; /* -------------------------------------------------------------------- */ +/** \name FreeType Utilities (Internal) + * \{ */ + +/** + * Convert a FreeType 26.6 value representing an unscaled design size to pixels. + * This is an exact copy of the scaling done inside FT_Get_Kerning when called + * with #FT_KERNING_DEFAULT, including arbitrary resizing for small fonts. + */ +static int blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) +{ + /* Scale value by font size using integer-optimized multiplication. */ + FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale); + + /* FreeType states that this '25' has been determined heuristically. */ + if (font->face->size->metrics.x_ppem < 25) { + scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25); + } + + /* Copies of internal FreeType macros needed here. */ +#define FT_PIX_FLOOR(x) ((x) & ~63) +#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32) + + /* Round to even 64ths, then divide by 64. */ + return (int)FT_PIX_ROUND(scaled) >> 6; + +#undef FT_PIX_FLOOR +#undef FT_PIX_ROUND +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Glyph Batching * \{ */ @@ -257,151 +289,78 @@ static void blf_batch_draw_end(void) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Glyph Stepping Utilities (Internal) + * \{ */ -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); -} - -void blf_font_exit(void) -{ - FT_Done_FreeType(ft_lib); - BLI_spin_end(&ft_lib_mutex); - BLI_spin_end(&blf_glyph_cache_mutex); - blf_batch_draw_exit(); -} +/* Fast path for runs of ASCII characters. Given that common UTF-8 + * input will consist of an overwhelming majority of ASCII + * characters. + */ -void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) +BLI_INLINE GlyphBLF *blf_utf8_next_fast( + FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t *i_p, uint *r_c) { - GlyphCacheBLF *gc; - FT_Error err; - - blf_glyph_cache_acquire(font); - - gc = blf_glyph_cache_find(font, size, dpi); - if (gc) { - /* Optimization: do not call FT_Set_Char_Size if size did not change. */ - if (font->size == size && font->dpi == dpi) { - blf_glyph_cache_release(font); - return; + GlyphBLF *g; + if ((*r_c = str[*i_p]) < GLYPH_ASCII_TABLE_SIZE) { + g = (gc->glyph_ascii_table)[*r_c]; + if (UNLIKELY(g == NULL)) { + g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c); + gc->glyph_ascii_table[*r_c] = g; } + (*i_p)++; } - - err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi); - if (err) { - /* FIXME: here we can go through the fixed size and choice a close one */ - printf("The current font don't support the size, %u and dpi, %u\n", size, dpi); - - blf_glyph_cache_release(font); - return; + else if ((*r_c = BLI_str_utf8_as_unicode_step(str, i_p)) != BLI_UTF8_ERR) { + g = blf_glyph_search(gc, *r_c); + if (UNLIKELY(g == NULL)) { + g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c); + } } - - font->size = size; - font->dpi = dpi; - - if (!gc) { - blf_glyph_cache_new(font); + else { + g = NULL; } - blf_glyph_cache_release(font); + return g; } -static GlyphBLF **blf_font_ensure_ascii_table(FontBLF *font, GlyphCacheBLF *gc) +BLI_INLINE void blf_kerning_step_fast(FontBLF *font, + const GlyphBLF *g_prev, + const GlyphBLF *g, + const uint c_prev, + const uint c, + int *pen_x_p) { - GlyphBLF **glyph_ascii_table; - - glyph_ascii_table = gc->glyph_ascii_table; - - /* build ascii on demand */ - if (glyph_ascii_table['0'] == NULL) { - GlyphBLF *g; - for (uint i = 0; i < 256; i++) { - g = blf_glyph_search(gc, i); - if (!g) { - FT_UInt glyph_index = FT_Get_Char_Index(font->face, i); - g = blf_glyph_add(font, gc, glyph_index, i); - } - glyph_ascii_table[i] = g; - } + if (!FT_HAS_KERNING(font->face) || g_prev == NULL) { + return; } - return glyph_ascii_table; -} + FT_Vector delta = {KERNING_ENTRY_UNSET}; -static void blf_font_ensure_ascii_kerning(FontBLF *font, - GlyphCacheBLF *gc, - const FT_UInt kern_mode) -{ - KerningCacheBLF *kc = font->kerning_cache; + /* Get unscaled kerning value from our cache if ASCII. */ + if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { + delta.x = font->kerning_cache->ascii_table[c][c_prev]; + } - font->kerning_mode = kern_mode; + /* If not ASCII or not found in cache, ask FreeType for kerning. */ + if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { + /* Note that this function sets delta values to zero on any error. */ + FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); + } - if (!kc || kc->mode != kern_mode) { - font->kerning_cache = kc = blf_kerning_cache_find(font); - if (!kc) { - font->kerning_cache = kc = blf_kerning_cache_new(font, gc); - } + /* If ASCII we save this value to our cache for quicker access next time. */ + if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { + font->kerning_cache->ascii_table[c][c_prev] = (int)delta.x; + } + + if (delta.x != 0) { + /* Convert unscaled design units to pixels and move pen. */ + *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x); } } -/* Fast path for runs of ASCII characters. Given that common UTF-8 - * input will consist of an overwhelming majority of ASCII - * characters. - */ +/** \} */ -/* NOTE: `blf_font_ensure_ascii_table(font, gc);` must be called before this macro. */ - -#define BLF_UTF8_NEXT_FAST(_font, _gc, _g, _str, _i, _c, _glyph_ascii_table) \ - if (((_c) = (_str)[_i]) < 0x80) { \ - _g = (_glyph_ascii_table)[_c]; \ - _i++; \ - } \ - else if ((_c = BLI_str_utf8_as_unicode_step(_str, &(_i))) != BLI_UTF8_ERR) { \ - if ((_g = blf_glyph_search(_gc, _c)) == NULL) { \ - _g = blf_glyph_add(_font, _gc, FT_Get_Char_Index((_font)->face, _c), _c); \ - } \ - } \ - else { \ - _g = NULL; \ - } \ - (void)0 - -#define BLF_KERNING_VARS(_font, _has_kerning, _kern_mode) \ - const bool _has_kerning = FT_HAS_KERNING((_font)->face) != 0; \ - const FT_UInt _kern_mode = (_has_kerning == 0) ? 0 : \ - (((_font)->flags & BLF_KERNING_DEFAULT) ? \ - ft_kerning_default : \ - (FT_UInt)FT_KERNING_UNFITTED) - -/* NOTE: `blf_font_ensure_ascii_kerning(font, gc, kern_mode);` must be called before this macro. */ - -#define BLF_KERNING_STEP_FAST(_font, _kern_mode, _g_prev, _g, _c_prev, _c, _pen_x) \ - { \ - if (_g_prev) { \ - FT_Vector _delta; \ - if (_c_prev < 0x80 && _c < 0x80) { \ - _pen_x += (_font)->kerning_cache->table[_c][_c_prev]; \ - } \ - else if (FT_Get_Kerning((_font)->face, (_g_prev)->idx, (_g)->idx, _kern_mode, &(_delta)) == \ - 0) { \ - _pen_x += (int)_delta.x >> 6; \ - } \ - } \ - } \ - (void)0 - -#define BLF_KERNING_STEP(_font, _kern_mode, _g_prev, _g, _delta, _pen_x) \ - { \ - if (_g_prev) { \ - _delta.x = _delta.y = 0; \ - if (FT_Get_Kerning((_font)->face, (_g_prev)->idx, (_g)->idx, _kern_mode, &(_delta)) == 0) { \ - _pen_x += (int)_delta.x >> 6; \ - } \ - } \ - } \ - (void)0 +/* -------------------------------------------------------------------- */ +/** \name Text Drawing: GPU + * \{ */ static void blf_font_draw_ex(FontBLF *font, GlyphCacheBLF *gc, @@ -420,16 +379,10 @@ static void blf_font_draw_ex(FontBLF *font, return; } - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - blf_batch_draw_begin(font); while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -437,9 +390,7 @@ static void blf_font_draw_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); /* do not return this loop if clipped, we want every character tested */ blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); @@ -472,22 +423,20 @@ static void blf_font_draw_ascii_ex( int pen_x = 0; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); blf_batch_draw_begin(font); while ((c = *(str++)) && len--) { - BLI_assert(c < 128); - if ((g = glyph_ascii_table[c]) == NULL) { - continue; - } - if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); + BLI_assert(c < GLYPH_ASCII_TABLE_SIZE); + g = gc->glyph_ascii_table[c]; + if (UNLIKELY(g == NULL)) { + g = blf_glyph_add(font, gc, FT_Get_Char_Index((font)->face, c), c); + gc->glyph_ascii_table[c] = g; + if (UNLIKELY(g == NULL)) { + continue; + } } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); /* do not return this loop if clipped, we want every character tested */ blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); @@ -522,12 +471,11 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) size_t i = 0; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); blf_batch_draw_begin(font); while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -554,6 +502,12 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) return columns; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Drawgin: Buffer + * \{ */ + /* Sanity checks are done by BLF_draw_buffer() */ static void blf_font_draw_buffer_ex(FontBLF *font, GlyphCacheBLF *gc, @@ -568,8 +522,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font, int pen_y_basis = (int)font->pos[1] + pen_y; size_t i = 0; - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - /* buffer specific vars */ FontBufInfoBLF *buf_info = &font->buf_info; const float *b_col_float = buf_info->col_float; @@ -577,14 +529,10 @@ static void blf_font_draw_buffer_ex(FontBLF *font, int chx, chy; int y, x; - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - /* another buffer specific call for color conversion */ while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -592,9 +540,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); chx = pen_x + ((int)g->pos[0]); chy = pen_y_basis + g->dims[1]; @@ -707,9 +653,17 @@ void blf_font_draw_buffer(FontBLF *font, const char *str, size_t len, struct Res blf_glyph_cache_release(font); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Width to Sting Length + * + * Use to implement exported functions: + * - #BLF_width_to_strlen + * - #BLF_width_to_rstrlen + * \{ */ + static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, - const bool has_kerning, - const FT_UInt kern_mode, const uint c_prev, const uint c, GlyphBLF *g_prev, @@ -723,9 +677,7 @@ static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, if (UNLIKELY(g == NULL)) { return false; /* continue the calling loop. */ } - if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, *pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, pen_x); *pen_x += g->advance_i; @@ -741,21 +693,13 @@ size_t blf_font_width_to_strlen( size_t i, i_prev; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); const int width_i = (int)width; - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - if (has_kerning) { - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - } - for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < len) && str[i]; i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i, &c); - if (blf_font_width_to_strlen_glyph_process( - font, has_kerning, kern_mode, c_prev, c, g_prev, g, &pen_x, width_i)) { + if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) { break; } } @@ -778,15 +722,8 @@ size_t blf_font_width_to_rstrlen( char *s, *s_prev; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); const int width_i = (int)width; - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - if (has_kerning) { - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - } - i = BLI_strnlen(str, len); s = BLI_str_find_prev_char_utf8(str, &str[i]); i = (size_t)((s != NULL) ? s - str : 0); @@ -794,7 +731,7 @@ size_t blf_font_width_to_rstrlen( i_prev = (size_t)((s_prev != NULL) ? s_prev - str : 0); i_tmp = i; - BLF_UTF8_NEXT_FAST(font, gc, g, str, i_tmp, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i_tmp, &c); for (width_new = pen_x = 0; (s != NULL); i = i_prev, s = s_prev, c = c_prev, g = g_prev, g_prev = NULL, width_new = pen_x) { s_prev = BLI_str_find_prev_char_utf8(str, s); @@ -802,12 +739,11 @@ size_t blf_font_width_to_rstrlen( if (s_prev != NULL) { i_tmp = i_prev; - BLF_UTF8_NEXT_FAST(font, gc, g_prev, str, i_tmp, c_prev, glyph_ascii_table); + g_prev = blf_utf8_next_fast(font, gc, str, &i_tmp, &c_prev); BLI_assert(i_tmp == i); } - if (blf_font_width_to_strlen_glyph_process( - font, has_kerning, kern_mode, c_prev, c, g_prev, g, &pen_x, width_i)) { + if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) { break; } } @@ -820,6 +756,12 @@ size_t blf_font_width_to_rstrlen( return i; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Glyph Bound Box with Callback + * \{ */ + static void blf_font_boundbox_ex(FontBLF *font, GlyphCacheBLF *gc, const char *str, @@ -832,22 +774,15 @@ static void blf_font_boundbox_ex(FontBLF *font, GlyphBLF *g, *g_prev = NULL; int pen_x = 0; size_t i = 0; - - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - rctf gbox; - BLF_KERNING_VARS(font, has_kerning, kern_mode); - box->xmin = 32000.0f; box->xmax = -32000.0f; box->ymin = 32000.0f; box->ymax = -32000.0f; - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -855,9 +790,7 @@ static void blf_font_boundbox_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); gbox.xmin = (float)pen_x; gbox.xmax = (float)pen_x + g->advance; @@ -903,8 +836,165 @@ void blf_font_boundbox( blf_glyph_cache_release(font); } +void blf_font_width_and_height(FontBLF *font, + const char *str, + size_t len, + float *r_width, + float *r_height, + struct ResultBLF *r_info) +{ + float xa, ya; + rctf box; + + if (font->flags & BLF_ASPECT) { + xa = font->aspect[0]; + ya = font->aspect[1]; + } + else { + xa = 1.0f; + ya = 1.0f; + } + + if (font->flags & BLF_WORD_WRAP) { + blf_font_boundbox__wrap(font, str, len, &box, r_info); + } + else { + blf_font_boundbox(font, str, len, &box, r_info); + } + *r_width = (BLI_rctf_size_x(&box) * xa); + *r_height = (BLI_rctf_size_y(&box) * ya); +} + +float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +{ + float xa; + rctf box; + + if (font->flags & BLF_ASPECT) { + xa = font->aspect[0]; + } + else { + xa = 1.0f; + } + + if (font->flags & BLF_WORD_WRAP) { + blf_font_boundbox__wrap(font, str, len, &box, r_info); + } + else { + blf_font_boundbox(font, str, len, &box, r_info); + } + return BLI_rctf_size_x(&box) * xa; +} + +float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +{ + float ya; + rctf box; + + if (font->flags & BLF_ASPECT) { + ya = font->aspect[1]; + } + else { + ya = 1.0f; + } + + if (font->flags & BLF_WORD_WRAP) { + blf_font_boundbox__wrap(font, str, len, &box, r_info); + } + else { + blf_font_boundbox(font, str, len, &box, r_info); + } + return BLI_rctf_size_y(&box) * ya; +} + +float blf_font_fixed_width(FontBLF *font) +{ + const unsigned int c = ' '; + + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + GlyphBLF *g = blf_glyph_search(gc, c); + if (!g) { + g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c); + + /* if we don't find the glyph. */ + if (!g) { + blf_glyph_cache_release(font); + return 0.0f; + } + } + + blf_glyph_cache_release(font); + return g->advance; +} + +static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, + GlyphCacheBLF *gc, + const char *str, + size_t len, + BLF_GlyphBoundsFn user_fn, + void *user_data, + struct ResultBLF *r_info, + int pen_y) +{ + unsigned int c, c_prev = BLI_UTF8_ERR; + GlyphBLF *g, *g_prev = NULL; + int pen_x = 0; + size_t i = 0, i_curr; + rcti gbox; + + if (len == 0) { + /* early output. */ + return; + } + + while ((i < len) && str[i]) { + i_curr = i; + g = blf_utf8_next_fast(font, gc, str, &i, &c); + + if (UNLIKELY(c == BLI_UTF8_ERR)) { + break; + } + if (UNLIKELY(g == NULL)) { + continue; + } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + + gbox.xmin = pen_x; + gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); + gbox.ymin = pen_y; + gbox.ymax = gbox.ymin - g->dims[1]; + + pen_x += g->advance_i; + + if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) { + break; + } + + g_prev = g; + c_prev = c; + } + + if (r_info) { + r_info->lines = 1; + r_info->width = pen_x; + } +} +void blf_font_boundbox_foreach_glyph(FontBLF *font, + const char *str, + size_t len, + BLF_GlyphBoundsFn user_fn, + void *user_data, + struct ResultBLF *r_info) +{ + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + blf_font_boundbox_foreach_glyph_ex(font, gc, str, len, user_fn, user_data, r_info, 0); + blf_glyph_cache_release(font); +} + +/** \} */ + /* -------------------------------------------------------------------- */ -/** \name Word-Wrap Support +/** \name Text Evaluation: Word-Wrap with Callback * \{ */ /** @@ -928,18 +1018,14 @@ static void blf_font_wrap_apply(FontBLF *font, void *userdata), void *userdata) { - unsigned int c; + unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; - FT_Vector delta; int pen_x = 0, pen_y = 0; size_t i = 0; int lines = 0; int pen_x_next = 0; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - - BLF_KERNING_VARS(font, has_kerning, kern_mode); struct WordWrapVars { int wrap_width; @@ -953,7 +1039,7 @@ static void blf_font_wrap_apply(FontBLF *font, size_t i_curr = i; bool do_draw = false; - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -961,9 +1047,7 @@ static void blf_font_wrap_apply(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - BLF_KERNING_STEP(font, kern_mode, g_prev, g, delta, pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); /** * Implementation Detail (utf8). @@ -1003,12 +1087,14 @@ static void blf_font_wrap_apply(FontBLF *font, pen_x = 0; pen_y -= gc->glyph_height_max; g_prev = NULL; + c_prev = BLI_UTF8_ERR; lines += 1; continue; } pen_x = pen_x_next; g_prev = g; + c_prev = c; } // printf("done! lines: %d, width, %d\n", lines, pen_x_next); @@ -1078,224 +1164,120 @@ void blf_font_draw_buffer__wrap(FontBLF *font, /** \} */ -void blf_font_width_and_height(FontBLF *font, - const char *str, - size_t len, - float *r_width, - float *r_height, - struct ResultBLF *r_info) +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Count Missing Characters + * \{ */ + +int blf_font_count_missing_chars(FontBLF *font, + const char *str, + const size_t len, + int *r_tot_chars) { - float xa, ya; - rctf box; + int missing = 0; + size_t i = 0; - if (font->flags & BLF_ASPECT) { - xa = font->aspect[0]; - ya = font->aspect[1]; - } - else { - xa = 1.0f; - ya = 1.0f; - } + *r_tot_chars = 0; + while (i < len) { + unsigned int c; - if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); - } - else { - blf_font_boundbox(font, str, len, &box, r_info); + if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) { + i++; + } + else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) { + if (FT_Get_Char_Index((font)->face, c) == 0) { + missing++; + } + } + (*r_tot_chars)++; } - *r_width = (BLI_rctf_size_x(&box) * xa); - *r_height = (BLI_rctf_size_y(&box) * ya); + return missing; } -float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) -{ - float xa; - rctf box; - - if (font->flags & BLF_ASPECT) { - xa = font->aspect[0]; - } - else { - xa = 1.0f; - } +/** \} */ - if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); - } - else { - blf_font_boundbox(font, str, len, &box, r_info); - } - return BLI_rctf_size_x(&box) * xa; -} +/* -------------------------------------------------------------------- */ +/** \name Font Query: Attributes + * \{ */ -float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +int blf_font_height_max(FontBLF *font) { - float ya; - rctf box; + int height_max; - if (font->flags & BLF_ASPECT) { - ya = font->aspect[1]; - } - else { - ya = 1.0f; - } + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + height_max = gc->glyph_height_max; - if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); - } - else { - blf_font_boundbox(font, str, len, &box, r_info); - } - return BLI_rctf_size_y(&box) * ya; + blf_glyph_cache_release(font); + return height_max; } -float blf_font_fixed_width(FontBLF *font) +int blf_font_width_max(FontBLF *font) { - const unsigned int c = ' '; + int width_max; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); - - GlyphBLF *g = blf_glyph_search(gc, c); - if (!g) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c); - - /* if we don't find the glyph. */ - if (!g) { - blf_glyph_cache_release(font); - return 0.0f; - } - } + width_max = gc->glyph_width_max; blf_glyph_cache_release(font); - return g->advance; + return width_max; } -/* -------------------------------------------------------------------- */ -/** \name Glyph Bound Box with Callback - * \{ */ - -static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, - GlyphCacheBLF *gc, - const char *str, - size_t len, - BLF_GlyphBoundsFn user_fn, - void *user_data, - struct ResultBLF *r_info, - int pen_y) +float blf_font_descender(FontBLF *font) { - unsigned int c, c_prev = BLI_UTF8_ERR; - GlyphBLF *g, *g_prev = NULL; - int pen_x = 0; - size_t i = 0, i_curr; - rcti gbox; - - if (len == 0) { - /* early output. */ - return; - } - - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - - while ((i < len) && str[i]) { - i_curr = i; - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); - - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } - if (UNLIKELY(g == NULL)) { - continue; - } - if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); - } + float descender; - gbox.xmin = pen_x; - gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); - gbox.ymin = pen_y; - gbox.ymax = gbox.ymin - g->dims[1]; + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + descender = gc->descender; - pen_x += g->advance_i; + blf_glyph_cache_release(font); + return descender; +} - if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) { - break; - } +float blf_font_ascender(FontBLF *font) +{ + float ascender; - g_prev = g; - c_prev = c; - } + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + ascender = gc->ascender; - if (r_info) { - r_info->lines = 1; - r_info->width = pen_x; - } + blf_glyph_cache_release(font); + return ascender; } -void blf_font_boundbox_foreach_glyph(FontBLF *font, - const char *str, - size_t len, - BLF_GlyphBoundsFn user_fn, - void *user_data, - struct ResultBLF *r_info) + +char *blf_display_name(FontBLF *font) { - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_boundbox_foreach_glyph_ex(font, gc, str, len, user_fn, user_data, r_info, 0); - blf_glyph_cache_release(font); + if (!font->face->family_name) { + return NULL; + } + return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); } /** \} */ -int blf_font_count_missing_chars(FontBLF *font, - const char *str, - const size_t len, - int *r_tot_chars) -{ - int missing = 0; - size_t i = 0; - - *r_tot_chars = 0; - while (i < len) { - unsigned int c; +/* -------------------------------------------------------------------- */ +/** \name Font Subsystem Init/Exit + * \{ */ - if ((c = str[i]) < 0x80) { - i++; - } - else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) { - if (FT_Get_Char_Index((font)->face, c) == 0) { - missing++; - } - } - (*r_tot_chars)++; - } - return missing; +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); } -void blf_font_free(FontBLF *font) +void blf_font_exit(void) { - BLI_spin_lock(&blf_glyph_cache_mutex); - GlyphCacheBLF *gc; - - while ((gc = BLI_pophead(&font->cache))) { - blf_glyph_cache_free(gc); - } - - blf_kerning_cache_clear(font); + FT_Done_FreeType(ft_lib); + BLI_spin_end(&ft_lib_mutex); + BLI_spin_end(&blf_glyph_cache_mutex); + blf_batch_draw_exit(); +} - FT_Done_Face(font->face); - if (font->filename) { - MEM_freeN(font->filename); - } - if (font->name) { - MEM_freeN(font->name); - } - MEM_freeN(font); +/** \} */ - BLI_spin_unlock(&blf_glyph_cache_mutex); -} +/* -------------------------------------------------------------------- */ +/** \name Font New/Free + * \{ */ static void blf_font_fill(FontBLF *font) { @@ -1324,7 +1306,6 @@ static void blf_font_fill(FontBLF *font) font->dpi = 0; font->size = 0; BLI_listbase_clear(&font->cache); - BLI_listbase_clear(&font->kerning_caches); font->kerning_cache = NULL; #if BLF_BLUR_ENABLE font->blur = 0; @@ -1359,9 +1340,15 @@ FontBLF *blf_font_new(const char *name, const char *filename) return NULL; } - err = FT_Select_Charmap(font->face, ft_encoding_unicode); + err = FT_Select_Charmap(font->face, FT_ENCODING_UNICODE); if (err) { - printf("Can't set the unicode character map!\n"); + err = FT_Select_Charmap(font->face, FT_ENCODING_APPLE_ROMAN); + } + if (err && font->face->num_charmaps > 0) { + err = FT_Select_Charmap(font->face, font->face->charmaps[0]->encoding); + } + if (err) { + printf("Can't set a character map!\n"); FT_Done_Face(font->face); MEM_freeN(font); return NULL; @@ -1379,6 +1366,17 @@ FontBLF *blf_font_new(const char *name, const char *filename) font->name = BLI_strdup(name); font->filename = BLI_strdup(filename); blf_font_fill(font); + + if (FT_HAS_KERNING(font->face)) { + /* 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++) { + for (uint j = 0; j < KERNING_CACHE_TABLE_SIZE; j++) { + font->kerning_cache->ascii_table[i][j] = KERNING_ENTRY_UNSET; + } + } + } + return font; } @@ -1418,58 +1416,61 @@ FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int m return font; } -int blf_font_height_max(FontBLF *font) +void blf_font_free(FontBLF *font) { - int height_max; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); - height_max = gc->glyph_height_max; + BLI_spin_lock(&blf_glyph_cache_mutex); + GlyphCacheBLF *gc; - blf_glyph_cache_release(font); - return height_max; -} + while ((gc = BLI_pophead(&font->cache))) { + blf_glyph_cache_free(gc); + } -int blf_font_width_max(FontBLF *font) -{ - int width_max; + if (font->kerning_cache) { + MEM_freeN(font->kerning_cache); + } - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); - width_max = gc->glyph_width_max; + FT_Done_Face(font->face); + if (font->filename) { + MEM_freeN(font->filename); + } + if (font->name) { + MEM_freeN(font->name); + } + MEM_freeN(font); - blf_glyph_cache_release(font); - return width_max; + BLI_spin_unlock(&blf_glyph_cache_mutex); } -float blf_font_descender(FontBLF *font) -{ - float descender; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); - descender = gc->descender; +/** \} */ - blf_glyph_cache_release(font); - return descender; -} +/* -------------------------------------------------------------------- */ +/** \name Font Configure + * \{ */ -float blf_font_ascender(FontBLF *font) +void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) { - float ascender; + blf_glyph_cache_acquire(font); - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); - ascender = gc->ascender; + GlyphCacheBLF *gc = blf_glyph_cache_find(font, size, dpi); + if (gc && (font->size == size && font->dpi == dpi)) { + /* Optimization: do not call FT_Set_Char_Size if size did not change. */ + } + else { + const FT_Error err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi); + if (err) { + /* FIXME: here we can go through the fixed size and choice a close one */ + printf("The current font don't support the size, %u and dpi, %u\n", size, dpi); + } + else { + font->size = size; + font->dpi = dpi; + if (gc == NULL) { + blf_glyph_cache_new(font); + } + } + } blf_glyph_cache_release(font); - return ascender; } -char *blf_display_name(FontBLF *font) -{ - if (!font->face->family_name) { - return NULL; - } - return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); -} +/** \} */ diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 3f01501fda4..6cdf5fc5996 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -55,64 +55,6 @@ #include "BLI_math_vector.h" #include "BLI_strict_flags.h" -KerningCacheBLF *blf_kerning_cache_find(FontBLF *font) -{ - KerningCacheBLF *p; - - p = (KerningCacheBLF *)font->kerning_caches.first; - while (p) { - if (p->mode == font->kerning_mode) { - return p; - } - p = p->next; - } - return NULL; -} - -/* Create a new glyph cache for the current kerning mode. */ -KerningCacheBLF *blf_kerning_cache_new(FontBLF *font, GlyphCacheBLF *gc) -{ - KerningCacheBLF *kc; - - kc = (KerningCacheBLF *)MEM_callocN(sizeof(KerningCacheBLF), "blf_kerning_cache_new"); - kc->next = NULL; - kc->prev = NULL; - kc->mode = font->kerning_mode; - - unsigned int i, j; - for (i = 0; i < 0x80; i++) { - for (j = 0; j < 0x80; j++) { - GlyphBLF *g = blf_glyph_search(gc, i); - if (!g) { - FT_UInt glyph_index = FT_Get_Char_Index(font->face, i); - g = blf_glyph_add(font, gc, glyph_index, i); - } - /* Can fail on certain fonts */ - GlyphBLF *g_prev = blf_glyph_search(gc, j); - - FT_Vector delta = { - .x = 0, - .y = 0, - }; - if (g && g_prev && FT_Get_Kerning(font->face, g_prev->idx, g->idx, kc->mode, &delta) == 0) { - kc->table[i][j] = (int)delta.x >> 6; - } - else { - kc->table[i][j] = 0; - } - } - } - - BLI_addhead(&font->kerning_caches, kc); - return kc; -} - -void blf_kerning_cache_clear(FontBLF *font) -{ - font->kerning_cache = NULL; - BLI_freelistN(&font->kerning_caches); -} - GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, unsigned int size, unsigned int dpi) { GlyphCacheBLF *p; @@ -144,8 +86,6 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table)); memset(gc->bucket, 0, sizeof(gc->bucket)); - gc->glyphs_len_max = (int)font->face->num_glyphs; - gc->glyphs_len_free = (int)font->face->num_glyphs; gc->ascender = ((float)font->face->size->metrics.ascender) / 64.0f; gc->descender = ((float)font->face->size->metrics.descender) / 64.0f; @@ -514,7 +454,6 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl memcpy(&gc->bitmap_result[gc->bitmap_len], g->bitmap, (size_t)buff_size); gc->bitmap_len = bitmap_len; - gc->glyphs_len_free--; g->glyph_cache = gc; } diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 35a6d019eac..ab2a26b1e06 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -121,10 +121,6 @@ int blf_font_count_missing_chars(struct FontBLF *font, void blf_font_free(struct FontBLF *font); -struct KerningCacheBLF *blf_kerning_cache_find(struct FontBLF *font); -struct KerningCacheBLF *blf_kerning_cache_new(struct FontBLF *font, struct GlyphCacheBLF *gc); -void blf_kerning_cache_clear(struct FontBLF *font); - struct GlyphCacheBLF *blf_glyph_cache_find(struct FontBLF *font, unsigned int size, unsigned int dpi); diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 36bb8769306..38d7d7b6e21 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -28,6 +28,15 @@ #define BLF_BATCH_DRAW_LEN_MAX 2048 /* in glyph */ +/* Number of characters in GlyphCacheBLF.glyph_ascii_table. */ +#define GLYPH_ASCII_TABLE_SIZE 128 + +/* 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. */ +#define KERNING_ENTRY_UNSET INT_MAX + typedef struct BatchBLF { struct FontBLF *font; /* can only batch glyph from the same font */ struct GPUBatch *batch; @@ -44,14 +53,11 @@ typedef struct BatchBLF { extern BatchBLF g_batch; typedef struct KerningCacheBLF { - struct KerningCacheBLF *next, *prev; - - /* kerning mode. */ - FT_UInt mode; - - /* only cache a ascii glyph pairs. Only store the x - * offset we are interested in, instead of the full FT_Vector. */ - int table[0x80][0x80]; + /** + * Cache a ascii glyph pairs. Only store the x offset we are interested in, + * instead of the full #FT_Vector since it's not used for drawing at the moment. + */ + int ascii_table[KERNING_CACHE_TABLE_SIZE][KERNING_CACHE_TABLE_SIZE]; } KerningCacheBLF; typedef struct GlyphCacheBLF { @@ -71,7 +77,7 @@ typedef struct GlyphCacheBLF { ListBase bucket[257]; /* fast ascii lookup */ - struct GlyphBLF *glyph_ascii_table[256]; + struct GlyphBLF *glyph_ascii_table[GLYPH_ASCII_TABLE_SIZE]; /* texture array, to draw the glyphs. */ GPUTexture *texture; @@ -84,12 +90,6 @@ typedef struct GlyphCacheBLF { int glyph_width_max; int glyph_height_max; - /* number of glyphs in the font. */ - int glyphs_len_max; - - /* number of glyphs not yet loaded (decreases every glyph loaded). */ - int glyphs_len_free; - /* ascender and descender value. */ float ascender; float descender; @@ -99,7 +99,7 @@ typedef struct GlyphBLF { struct GlyphBLF *next; struct GlyphBLF *prev; - /* and the character, as UTF8 */ + /* and the character, as UTF-32 */ unsigned int c; /* freetype2 index, to speed-up the search. */ @@ -225,10 +225,7 @@ typedef struct FontBLF { */ ListBase cache; - /* list of kerning cache for this font. */ - ListBase kerning_caches; - - /* current kerning cache for this font and kerning mode. */ + /* Cache of unscaled kerning values. Will be NULL if font does not have kerning. */ KerningCacheBLF *kerning_cache; /* freetype2 lib handle. */ @@ -240,9 +237,6 @@ typedef struct FontBLF { /* freetype2 face. */ FT_Face face; - /* freetype kerning */ - FT_UInt kerning_mode; - /* data for buffer usage (drawing into a texture buffer) */ FontBufInfoBLF buf_info; |