From 13f2ea664a9c5e3fdf318ef75914e2e876b5aaef Mon Sep 17 00:00:00 2001 From: ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> Date: Wed, 4 Jan 2023 17:23:13 +0100 Subject: Cairo/Pango: compute character widths fast and string widths accurately This commit has Fl_Cairo_Graphics_Driver compute string widths in 2 ways: 1) when the string contains several unicode characters, the width of the whole string is computed, accounting for kerning when it occurs; 2) when the string contains a single unicode character, its width is computed, memorised, and re-used next time it's necessary. The effect of this approach is - Fl_Text_Display is fast because it uses memorised single character widths repeatedly - Fl_Input is drawn accurately because the cursor position is determined by string widths, not by sums of character widths. --- src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H | 2 +- src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx | 60 +++++++++++++------------- 2 files changed, 32 insertions(+), 30 deletions(-) (limited to 'src/drivers/Cairo') diff --git a/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H b/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H index 0b5eee198..c6fa15403 100644 --- a/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H +++ b/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.H @@ -50,7 +50,7 @@ private: bool *needs_commit_tag_; // NULL or points to whether cairo surface was drawn to cairo_t *dummy_cairo_; // used to measure text width before showing a window int linestyle_; - int width_unscaled_(unsigned int c); + int do_width_unscaled_(const char* str, int n); protected: cairo_t *cairo_; PangoContext *pango_context_; diff --git a/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx b/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx index 50a4dbae7..c7e2c2ae3 100644 --- a/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx +++ b/src/drivers/Cairo/Fl_Cairo_Graphics_Driver.cxx @@ -1236,31 +1236,13 @@ void Fl_Cairo_Graphics_Driver::rtl_draw(const char* str, int n, int x, int y) { } -double Fl_Cairo_Graphics_Driver::width(const char* c, int n) { - if (!font_descriptor()) return -1.0; - int i = 0, w = 0, l; - const char *end = c + n; - unsigned int ucs; - while (i < n) { - ucs = fl_utf8decode(c + i, end, &l); - i += l; - w += width_unscaled_(ucs); - } - return w / double(PANGO_SCALE); -} - - -double Fl_Cairo_Graphics_Driver::width(unsigned int c) { - return width_unscaled_(c)/ double(PANGO_SCALE); -} - - -int Fl_Cairo_Graphics_Driver::width_unscaled_(unsigned int c) { - unsigned int r = 0; +// cache the widths of single Unicode characters +double Fl_Cairo_Graphics_Driver::width(unsigned int utf32) { + unsigned r=0; Fl_Cairo_Font_Descriptor *desc = NULL; - if (c <= 0xFFFF) { // when inside basic multilingual plane + if (utf32 <= 0xFFFF) { desc = (Fl_Cairo_Font_Descriptor*)font_descriptor(); - r = (c & 0xFC00) >> 10; + r = (utf32 & 0xFC00) >> 10; if (!desc->width) { desc->width = (int**)new int*[64]; memset(desc->width, 0, 64*sizeof(int*)); @@ -1269,17 +1251,37 @@ int Fl_Cairo_Graphics_Driver::width_unscaled_(unsigned int c) { desc->width[r] = (int*)new int[0x0400]; for (int i = 0; i < 0x0400; i++) desc->width[r][i] = -1; } else { - if ( desc->width[r][c & 0x03FF] >= 0 ) { // already cached - return desc->width[r][c & 0x03FF]; + if ( desc->width[r][utf32&0x03FF] >= 0 ) { // already cached + return desc->width[r][utf32 & 0x03FF] / double(PANGO_SCALE); } } } - char buf[4]; - int n = fl_utf8encode(c, buf); - pango_layout_set_text(pango_layout_, buf, n); + char buf4[4]; + int n = fl_utf8encode(utf32, buf4); + int width = do_width_unscaled_(buf4, n); + if (utf32 <= 0xFFFF) { + desc->width[r][utf32 & 0x03FF] = width; + } + return width / double(PANGO_SCALE); +} + + +double Fl_Cairo_Graphics_Driver::width(const char* str, int n) { + if (!font_descriptor()) return -1; + if (n == fl_utf8len(*str)) { // str contains a single unicode character + int l; + unsigned c = fl_utf8decode(str, str+n, &l); + return width(c); // that character's width may have been cached + } + return do_width_unscaled_(str, n) / double(PANGO_SCALE); // full width computation for multi-char strings +} + + +int Fl_Cairo_Graphics_Driver::do_width_unscaled_(const char* str, int n) { + if (!n) return 0; + pango_layout_set_text(pango_layout_, str, n); PangoRectangle p_rect; pango_layout_get_extents(pango_layout_, NULL, &p_rect); - if (c <= 0xFFFF) desc->width[r][c & 0x03FF] = p_rect.width; return p_rect.width; } -- cgit v1.2.3