mirror of
https://github.com/fltk/fltk.git
synced 2026-06-04 15:22:40 +08:00
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.
This commit is contained in:
@@ -50,7 +50,7 @@ private:
|
|||||||
bool *needs_commit_tag_; // NULL or points to whether cairo surface was drawn to
|
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
|
cairo_t *dummy_cairo_; // used to measure text width before showing a window
|
||||||
int linestyle_;
|
int linestyle_;
|
||||||
int width_unscaled_(unsigned int c);
|
int do_width_unscaled_(const char* str, int n);
|
||||||
protected:
|
protected:
|
||||||
cairo_t *cairo_;
|
cairo_t *cairo_;
|
||||||
PangoContext *pango_context_;
|
PangoContext *pango_context_;
|
||||||
|
|||||||
@@ -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) {
|
// cache the widths of single Unicode characters
|
||||||
if (!font_descriptor()) return -1.0;
|
double Fl_Cairo_Graphics_Driver::width(unsigned int utf32) {
|
||||||
int i = 0, w = 0, l;
|
unsigned r=0;
|
||||||
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;
|
|
||||||
Fl_Cairo_Font_Descriptor *desc = NULL;
|
Fl_Cairo_Font_Descriptor *desc = NULL;
|
||||||
if (c <= 0xFFFF) { // when inside basic multilingual plane
|
if (utf32 <= 0xFFFF) {
|
||||||
desc = (Fl_Cairo_Font_Descriptor*)font_descriptor();
|
desc = (Fl_Cairo_Font_Descriptor*)font_descriptor();
|
||||||
r = (c & 0xFC00) >> 10;
|
r = (utf32 & 0xFC00) >> 10;
|
||||||
if (!desc->width) {
|
if (!desc->width) {
|
||||||
desc->width = (int**)new int*[64];
|
desc->width = (int**)new int*[64];
|
||||||
memset(desc->width, 0, 64*sizeof(int*));
|
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];
|
desc->width[r] = (int*)new int[0x0400];
|
||||||
for (int i = 0; i < 0x0400; i++) desc->width[r][i] = -1;
|
for (int i = 0; i < 0x0400; i++) desc->width[r][i] = -1;
|
||||||
} else {
|
} else {
|
||||||
if ( desc->width[r][c & 0x03FF] >= 0 ) { // already cached
|
if ( desc->width[r][utf32&0x03FF] >= 0 ) { // already cached
|
||||||
return desc->width[r][c & 0x03FF];
|
return desc->width[r][utf32 & 0x03FF] / double(PANGO_SCALE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
char buf[4];
|
char buf4[4];
|
||||||
int n = fl_utf8encode(c, buf);
|
int n = fl_utf8encode(utf32, buf4);
|
||||||
pango_layout_set_text(pango_layout_, buf, n);
|
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;
|
PangoRectangle p_rect;
|
||||||
pango_layout_get_extents(pango_layout_, NULL, &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;
|
return p_rect.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user