diff options
Diffstat (limited to 'source/blender/blenfont/intern/blf_font.c')
-rw-r--r-- | source/blender/blenfont/intern/blf_font.c | 262 |
1 files changed, 241 insertions, 21 deletions
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index fe9769dd2d1..f7a926275a9 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -58,6 +58,12 @@ #include "BIF_gl.h" #include "BLF_api.h" +#include "UI_interface.h" + +#include "GPU_immediate.h" +#include "GPU_matrix.h" +#include "GPU_batch.h" + #include "blf_internal_types.h" #include "blf_internal.h" @@ -67,12 +73,155 @@ # define FT_New_Face FT_New_Face__win32_compat #endif +/* Batching buffer for drawing. */ +BatchBLF g_batch; + /* freetype2 handle ONLY for this file!. */ static FT_Library ft_lib; static SpinLock ft_lib_mutex; +/* -------------------------------------------------------------------- */ +/** \name Glyph Batching + * \{ */ +/** + * Drawcalls are precious! make them count! + * Since most of the Text elems are not covered by other UI elements, we can + * group some strings together and render them in one drawcall. This behaviour + * is on demand only, between BLF_batch_start() and BLF_batch_end(). + **/ +static void blf_batch_draw_init(void) +{ + GPUVertFormat format = {0}; + g_batch.pos_loc = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + g_batch.tex_loc = GPU_vertformat_attr_add(&format, "tex", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + g_batch.col_loc = GPU_vertformat_attr_add(&format, "col", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + g_batch.verts = GPU_vertbuf_create_with_format_ex(&format, GPU_USAGE_STREAM); + GPU_vertbuf_data_alloc(g_batch.verts, BLF_BATCH_DRAW_LEN_MAX); + + GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.pos_loc, &g_batch.pos_step); + GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.tex_loc, &g_batch.tex_step); + GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.col_loc, &g_batch.col_step); + g_batch.glyph_len = 0; + + g_batch.batch = GPU_batch_create_ex(GPU_PRIM_POINTS, g_batch.verts, NULL, GPU_BATCH_OWNS_VBO); +} + +static void blf_batch_draw_exit(void) +{ + GPU_BATCH_DISCARD_SAFE(g_batch.batch); +} + +void blf_batch_draw_vao_clear(void) +{ + if (g_batch.batch) { + GPU_batch_vao_cache_clear(g_batch.batch); + } +} + +void blf_batch_draw_begin(FontBLF *font) +{ + if (g_batch.batch == NULL) { + blf_batch_draw_init(); + } + + const bool font_changed = (g_batch.font != font); + const bool simple_shader = ((font->flags & (BLF_ROTATION | BLF_MATRIX | BLF_ASPECT)) == 0); + const bool shader_changed = (simple_shader != g_batch.simple_shader); + + g_batch.active = g_batch.enabled && simple_shader; + + if (simple_shader) { + /* Offset is applied to each glyph. */ + g_batch.ofs[0] = floorf(font->pos[0]); + g_batch.ofs[1] = floorf(font->pos[1]); + } + else { + /* Offset is baked in modelview mat. */ + zero_v2(g_batch.ofs); + } + + if (g_batch.active) { + float gpumat[4][4]; + GPU_matrix_model_view_get(gpumat); + + bool mat_changed = (memcmp(gpumat, g_batch.mat, sizeof(g_batch.mat)) != 0); + + if (mat_changed) { + /* Modelviewmat is no longer the same. + * Flush cache but with the previous mat. */ + GPU_matrix_push(); + GPU_matrix_set(g_batch.mat); + } + + /* flush cache if config is not the same. */ + if (mat_changed || font_changed || shader_changed) { + blf_batch_draw(); + g_batch.simple_shader = simple_shader; + g_batch.font = font; + } + else { + /* Nothing changed continue batching. */ + return; + } + + if (mat_changed) { + GPU_matrix_pop(); + /* Save for next memcmp. */ + memcpy(g_batch.mat, gpumat, sizeof(g_batch.mat)); + } + } + else { + /* flush cache */ + blf_batch_draw(); + g_batch.font = font; + g_batch.simple_shader = simple_shader; + } +} + +void blf_batch_draw(void) +{ + if (g_batch.glyph_len == 0) + return; + + GPU_blend(true); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + + /* We need to flush widget base first to ensure correct ordering. */ + UI_widgetbase_draw_cache_flush(); + + GPU_texture_bind(g_batch.tex_bind_state, 0); + GPU_vertbuf_vertex_count_set(g_batch.verts, g_batch.glyph_len); + GPU_vertbuf_use(g_batch.verts); /* send data */ + + GPUBuiltinShader shader = (g_batch.simple_shader) ? GPU_SHADER_TEXT_SIMPLE : GPU_SHADER_TEXT; + GPU_batch_program_set_builtin(g_batch.batch, shader); + GPU_batch_uniform_1i(g_batch.batch, "glyph", 0); + GPU_batch_draw(g_batch.batch); + + GPU_blend(false); + + /* restart to 1st vertex data pointers */ + GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.pos_loc, &g_batch.pos_step); + GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.tex_loc, &g_batch.tex_step); + GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.col_loc, &g_batch.col_step); + g_batch.glyph_len = 0; +} + +static void blf_batch_draw_end(void) +{ + if (!g_batch.active) { + blf_batch_draw(); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ + int blf_font_init(void) { + memset(&g_batch, 0, sizeof(g_batch)); BLI_spin_init(&ft_lib_mutex); return FT_Init_FreeType(&ft_lib); } @@ -81,6 +230,7 @@ void blf_font_exit(void) { FT_Done_FreeType(ft_lib); BLI_spin_end(&ft_lib_mutex); + blf_batch_draw_exit(); } void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) @@ -88,6 +238,14 @@ void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) GlyphCacheBLF *gc; FT_Error err; + gc = blf_glyph_cache_find(font, size, dpi); + if (gc) { + font->glyph_cache = gc; + /* Optimization: do not call FT_Set_Char_Size if size did not change. */ + if (font->size == size && font->dpi == dpi) + return; + } + 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 */ @@ -98,10 +256,7 @@ void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) font->size = size; font->dpi = dpi; - gc = blf_glyph_cache_find(font, size, dpi); - if (gc) - font->glyph_cache = gc; - else { + if (!gc) { gc = blf_glyph_cache_new(font); if (gc) font->glyph_cache = gc; @@ -129,6 +284,20 @@ static void blf_font_ensure_ascii_table(FontBLF *font) } } +static void blf_font_ensure_ascii_kerning(FontBLF *font, const FT_UInt kern_mode) +{ + KerningCacheBLF *kc = font->kerning_cache; + + font->kerning_mode = kern_mode; + + 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); + } + } +} + /* Fast path for runs of ASCII characters. Given that common UTF-8 * input will consist of an overwhelming majority of ASCII * characters. @@ -156,6 +325,26 @@ static void blf_font_ensure_ascii_table(FontBLF *font) (((_font)->flags & BLF_KERNING_DEFAULT) ? \ ft_kerning_default : (FT_UInt)FT_KERNING_UNFITTED) \ +/* Note, + * blf_font_ensure_ascii_kerning(font, 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) \ { \ @@ -176,16 +365,23 @@ static void blf_font_draw_ex( FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info, int pen_y) { - unsigned int c; + unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; - FT_Vector delta; int pen_x = 0; size_t i = 0; GlyphBLF **glyph_ascii_table = font->glyph_cache->glyph_ascii_table; + if (len == 0) { + /* early output, don't do any IMM OpenGL. */ + return; + } + BLF_KERNING_VARS(font, has_kerning, kern_mode); blf_font_ensure_ascii_table(font); + blf_font_ensure_ascii_kerning(font, kern_mode); + + blf_batch_draw_begin(font); while ((i < len) && str[i]) { BLF_UTF8_NEXT_FAST(font, g, str, i, c, glyph_ascii_table); @@ -195,15 +391,18 @@ static void blf_font_draw_ex( 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, kern_mode, g_prev, g, c_prev, c, pen_x); /* do not return this loop if clipped, we want every character tested */ blf_glyph_render(font, g, (float)pen_x, (float)pen_y); pen_x += g->advance_i; g_prev = g; + c_prev = c; } + blf_batch_draw_end(); + if (r_info) { r_info->lines = 1; r_info->width = pen_x; @@ -219,30 +418,35 @@ static void blf_font_draw_ascii_ex( FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info, int pen_y) { - unsigned char c; + unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; - FT_Vector delta; int pen_x = 0; GlyphBLF **glyph_ascii_table = font->glyph_cache->glyph_ascii_table; BLF_KERNING_VARS(font, has_kerning, kern_mode); blf_font_ensure_ascii_table(font); + blf_font_ensure_ascii_kerning(font, 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(font, kern_mode, g_prev, g, delta, pen_x); + BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); /* do not return this loop if clipped, we want every character tested */ blf_glyph_render(font, g, (float)pen_x, (float)pen_y); pen_x += g->advance_i; g_prev = g; + c_prev = c; } + blf_batch_draw_end(); + if (r_info) { r_info->lines = 1; r_info->width = pen_x; @@ -265,6 +469,8 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) blf_font_ensure_ascii_table(font); + blf_batch_draw_begin(font); + while ((i < len) && str[i]) { BLF_UTF8_NEXT_FAST(font, g, str, i, c, glyph_ascii_table); @@ -284,6 +490,8 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) pen_x += cwidth * col; } + blf_batch_draw_end(); + return columns; } @@ -292,9 +500,8 @@ static void blf_font_draw_buffer_ex( FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info, int pen_y) { - unsigned int c; + unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; - FT_Vector delta; int pen_x = (int)font->pos[0]; int pen_y_basis = (int)font->pos[1] + pen_y; size_t i = 0; @@ -310,6 +517,7 @@ static void blf_font_draw_buffer_ex( BLF_KERNING_VARS(font, has_kerning, kern_mode); blf_font_ensure_ascii_table(font); + blf_font_ensure_ascii_kerning(font, kern_mode); /* another buffer specific call for color conversion */ @@ -321,7 +529,7 @@ static void blf_font_draw_buffer_ex( 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, kern_mode, g_prev, g, c_prev, c, pen_x); chx = pen_x + ((int)g->pos_x); chy = pen_y_basis + g->height; @@ -423,6 +631,7 @@ static void blf_font_draw_buffer_ex( pen_x += g->advance_i; g_prev = g; + c_prev = c; } if (r_info) { @@ -444,7 +653,7 @@ size_t blf_font_width_to_strlen(FontBLF *font, const char *str, size_t len, floa int pen_x = 0; size_t i = 0, i_prev; GlyphBLF **glyph_ascii_table = font->glyph_cache->glyph_ascii_table; - const int width_i = (int)width + 1; + const int width_i = (int)width; int width_new; BLF_KERNING_VARS(font, has_kerning, kern_mode); @@ -466,7 +675,7 @@ size_t blf_font_width_to_strlen(FontBLF *font, const char *str, size_t len, floa pen_x += g->advance_i; - if (width_i < pen_x) { + if (width_i <= pen_x) { break; } @@ -570,9 +779,8 @@ static void blf_font_boundbox_ex( FontBLF *font, const char *str, size_t len, rctf *box, struct ResultBLF *r_info, int pen_y) { - unsigned int c; + unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; - FT_Vector delta; int pen_x = 0; size_t i = 0; GlyphBLF **glyph_ascii_table = font->glyph_cache->glyph_ascii_table; @@ -587,6 +795,7 @@ static void blf_font_boundbox_ex( box->ymax = -32000.0f; blf_font_ensure_ascii_table(font); + blf_font_ensure_ascii_kerning(font, kern_mode); while ((i < len) && str[i]) { BLF_UTF8_NEXT_FAST(font, g, str, i, c, glyph_ascii_table); @@ -596,7 +805,7 @@ static void blf_font_boundbox_ex( 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, kern_mode, g_prev, g, c_prev, c, pen_x); gbox.xmin = (float)pen_x; gbox.xmax = (float)pen_x + g->advance; @@ -611,6 +820,7 @@ static void blf_font_boundbox_ex( pen_x += g->advance_i; g_prev = g; + c_prev = c; } if (box->xmin > box->xmax) { @@ -891,6 +1101,8 @@ void blf_font_free(FontBLF *font) blf_glyph_cache_free(gc); } + blf_kerning_cache_clear(font); + FT_Done_Face(font->face); if (font->filename) MEM_freeN(font->filename); @@ -901,8 +1113,6 @@ void blf_font_free(FontBLF *font) static void blf_font_fill(FontBLF *font) { - unsigned int i; - font->aspect[0] = 1.0f; font->aspect[1] = 1.0f; font->aspect[2] = 1.0f; @@ -910,9 +1120,15 @@ static void blf_font_fill(FontBLF *font) font->pos[1] = 0.0f; font->angle = 0.0f; - for (i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) font->m[i] = 0; + /* annoying bright color so we can see where to add BLF_color calls */ + font->color[0] = 255; + font->color[1] = 255; + font->color[2] = 0; + font->color[3] = 255; + font->clip_rec.xmin = 0.0f; font->clip_rec.xmax = 0.0f; font->clip_rec.ymin = 0.0f; @@ -921,8 +1137,12 @@ 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->glyph_cache = NULL; + font->kerning_cache = NULL; +#if BLF_BLUR_ENABLE font->blur = 0; +#endif font->tex_size_max = -1; font->buf_info.fbuf = NULL; |