mirror of
https://codeberg.org/dnkl/yambar.git
synced 2025-04-20 11:35:42 +02:00
particle/string: use fcft_text_run_rasterize() when available
This enables support for text shaping, and is required to render
e.g. 👩👩👧👧 correctly.
Since text-shaping is a fairly expensive operation, and since many
times the text is unchanged, we cache the last *rendered* string.
That is, we hash the instantiated string, and cache it along with the
text-run from fcft in the *particle* object (i.e. not the exposable).
This means two things:
* we only need to call fcft_text_run_rasterize() once per string
* if the string is the same as last time, we don’t have to call it at
all.
This commit is contained in:
parent
18a0920ed9
commit
a5bbf0b769
2 changed files with 75 additions and 14 deletions
|
@ -65,7 +65,7 @@ backend_wayland = wayland_client.found() and wayland_cursor.found()
|
||||||
|
|
||||||
# "My" dependencies, fallback to subproject
|
# "My" dependencies, fallback to subproject
|
||||||
tllist = dependency('tllist', version: '>=1.0.1', fallback: 'tllist')
|
tllist = dependency('tllist', version: '>=1.0.1', fallback: 'tllist')
|
||||||
fcft = dependency('fcft', version: ['>=2.3.90', '<3.0.0'], fallback: 'fcft')
|
fcft = dependency('fcft', version: ['>=2.4.0', '<3.0.0'], fallback: 'fcft')
|
||||||
|
|
||||||
add_project_arguments(
|
add_project_arguments(
|
||||||
['-D_GNU_SOURCE'] +
|
['-D_GNU_SOURCE'] +
|
||||||
|
|
|
@ -13,6 +13,12 @@
|
||||||
struct private {
|
struct private {
|
||||||
char *text;
|
char *text;
|
||||||
size_t max_len;
|
size_t max_len;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint64_t hash;
|
||||||
|
struct fcft_text_run *run;
|
||||||
|
int width;
|
||||||
|
} cached;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct eprivate {
|
struct eprivate {
|
||||||
|
@ -20,17 +26,31 @@ struct eprivate {
|
||||||
char *text;
|
char *text;
|
||||||
|
|
||||||
const struct fcft_glyph **glyphs;
|
const struct fcft_glyph **glyphs;
|
||||||
|
const struct fcft_glyph **allocated_glyphs;
|
||||||
long *kern_x;
|
long *kern_x;
|
||||||
int num_glyphs;
|
int num_glyphs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static uint64_t
|
||||||
|
sdbm_hash(const char *s)
|
||||||
|
{
|
||||||
|
uint64_t hash = 0;
|
||||||
|
|
||||||
|
for (; *s != '\0'; s++) {
|
||||||
|
int c = *s;
|
||||||
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
exposable_destroy(struct exposable *exposable)
|
exposable_destroy(struct exposable *exposable)
|
||||||
{
|
{
|
||||||
struct eprivate *e = exposable->private;
|
struct eprivate *e = exposable->private;
|
||||||
|
|
||||||
free(e->text);
|
free(e->text);
|
||||||
free(e->glyphs);
|
free(e->allocated_glyphs);
|
||||||
free(e->kern_x);
|
free(e->kern_x);
|
||||||
free(e);
|
free(e);
|
||||||
exposable_default_destroy(exposable);
|
exposable_default_destroy(exposable);
|
||||||
|
@ -40,19 +60,55 @@ static int
|
||||||
begin_expose(struct exposable *exposable)
|
begin_expose(struct exposable *exposable)
|
||||||
{
|
{
|
||||||
struct eprivate *e = exposable->private;
|
struct eprivate *e = exposable->private;
|
||||||
|
struct private *p = exposable->particle->private;
|
||||||
struct fcft_font *font = exposable->particle->font;
|
struct fcft_font *font = exposable->particle->font;
|
||||||
|
|
||||||
e->glyphs = NULL;
|
e->glyphs = e->allocated_glyphs = NULL;
|
||||||
e->num_glyphs = 0;
|
e->num_glyphs = 0;
|
||||||
|
e->kern_x = NULL;
|
||||||
|
|
||||||
|
uint64_t hash = sdbm_hash(e->text);
|
||||||
|
|
||||||
|
if (p->cached.hash == hash) {
|
||||||
|
e->glyphs = p->cached.run->glyphs;
|
||||||
|
e->num_glyphs = p->cached.run->count;
|
||||||
|
e->kern_x = calloc(p->cached.run->count, sizeof(e->kern_x[0]));
|
||||||
|
|
||||||
|
exposable->width =
|
||||||
|
exposable->particle->left_margin +
|
||||||
|
p->cached.width +
|
||||||
|
exposable->particle->right_margin;
|
||||||
|
|
||||||
|
return exposable->width;
|
||||||
|
}
|
||||||
|
|
||||||
size_t chars = mbstowcs(NULL, e->text, 0);
|
size_t chars = mbstowcs(NULL, e->text, 0);
|
||||||
if (chars != (size_t)-1) {
|
if (chars != (size_t)-1) {
|
||||||
wchar_t wtext[chars + 1];
|
wchar_t wtext[chars + 1];
|
||||||
mbstowcs(wtext, e->text, chars + 1);
|
mbstowcs(wtext, e->text, chars + 1);
|
||||||
|
|
||||||
e->glyphs = malloc(chars * sizeof(e->glyphs[0]));
|
|
||||||
e->kern_x = calloc(chars, sizeof(e->kern_x[0]));
|
e->kern_x = calloc(chars, sizeof(e->kern_x[0]));
|
||||||
|
|
||||||
|
if (fcft_capabilities() & FCFT_CAPABILITY_TEXT_RUN_SHAPING) {
|
||||||
|
struct fcft_text_run *run = fcft_text_run_rasterize(font, chars, wtext, FCFT_SUBPIXEL_NONE);
|
||||||
|
if (run != NULL) {
|
||||||
|
int w = 0;
|
||||||
|
for (size_t i = 0; i < run->count; i++)
|
||||||
|
w += run->glyphs[i]->advance.x;
|
||||||
|
|
||||||
|
fcft_text_run_destroy(p->cached.run);
|
||||||
|
p->cached.hash = hash;
|
||||||
|
p->cached.run = run;
|
||||||
|
p->cached.width = w;
|
||||||
|
|
||||||
|
e->num_glyphs = run->count;
|
||||||
|
e->glyphs = run->glyphs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e->glyphs == NULL) {
|
||||||
|
e->allocated_glyphs = malloc(chars * sizeof(e->glyphs[0]));
|
||||||
|
|
||||||
/* Convert text to glyph masks/images. */
|
/* Convert text to glyph masks/images. */
|
||||||
for (size_t i = 0; i < chars; i++) {
|
for (size_t i = 0; i < chars; i++) {
|
||||||
const struct fcft_glyph *glyph = fcft_glyph_rasterize(
|
const struct fcft_glyph *glyph = fcft_glyph_rasterize(
|
||||||
|
@ -61,13 +117,16 @@ begin_expose(struct exposable *exposable)
|
||||||
if (glyph == NULL)
|
if (glyph == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
e->glyphs[e->num_glyphs++] = glyph;
|
e->allocated_glyphs[e->num_glyphs++] = glyph;
|
||||||
|
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
fcft_kerning(font, wtext[i - 1], wtext[i], &e->kern_x[i], NULL);
|
fcft_kerning(font, wtext[i - 1], wtext[i], &e->kern_x[i], NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e->glyphs = e->allocated_glyphs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exposable->width = exposable->particle->left_margin +
|
exposable->width = exposable->particle->left_margin +
|
||||||
|
@ -188,6 +247,7 @@ static void
|
||||||
particle_destroy(struct particle *particle)
|
particle_destroy(struct particle *particle)
|
||||||
{
|
{
|
||||||
struct private *p = particle->private;
|
struct private *p = particle->private;
|
||||||
|
fcft_text_run_destroy(p->cached.run);
|
||||||
free(p->text);
|
free(p->text);
|
||||||
free(p);
|
free(p);
|
||||||
particle_default_destroy(particle);
|
particle_default_destroy(particle);
|
||||||
|
@ -199,6 +259,7 @@ string_new(struct particle *common, const char *text, size_t max_len)
|
||||||
struct private *p = calloc(1, sizeof(*p));
|
struct private *p = calloc(1, sizeof(*p));
|
||||||
p->text = strdup(text);
|
p->text = strdup(text);
|
||||||
p->max_len = max_len;
|
p->max_len = max_len;
|
||||||
|
p->cached.hash = -1;
|
||||||
|
|
||||||
common->private = p;
|
common->private = p;
|
||||||
common->destroy = &particle_destroy;
|
common->destroy = &particle_destroy;
|
||||||
|
|
Loading…
Add table
Reference in a new issue