[ui] Add a shaped text cache system
Shaped text is cached using all the shaping parameters as well as the text itself as a key. This makes text shaping a non-issue for imui when the text is stable, taking my simple test from 120fps to 1000fps (optimized build).
This commit is contained in:
parent
0360e33a00
commit
79ab2f7ba7
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
shaper.h
|
||||||
|
|
||||||
|
Text shaping system
|
||||||
|
|
||||||
|
Copyright (C) 2023 Bill Currie <bill@taniwha.org>
|
||||||
|
|
||||||
|
Author: Bill Currie <bill@taniwha.org>
|
||||||
|
Date: 2023/07/07
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to:
|
||||||
|
|
||||||
|
Free Software Foundation, Inc.
|
||||||
|
59 Temple Place - Suite 330
|
||||||
|
Boston, MA 02111-1307, USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __QF_ui_shaper_h
|
||||||
|
#define __QF_ui_shaper_h
|
||||||
|
|
||||||
|
#include "QF/ui/text.h"
|
||||||
|
|
||||||
|
typedef struct text_shaper_s text_shaper_t;
|
||||||
|
|
||||||
|
typedef struct shaped_glyphs_s {
|
||||||
|
const hb_glyph_info_t *glyphInfo;
|
||||||
|
const hb_glyph_position_t *glyphPos;
|
||||||
|
unsigned count;
|
||||||
|
} shaped_glyphs_t;
|
||||||
|
|
||||||
|
typedef struct shaping_s {
|
||||||
|
const script_component_t *script;
|
||||||
|
const featureset_t *features;
|
||||||
|
const struct font_s *font;
|
||||||
|
} shaping_t;
|
||||||
|
|
||||||
|
text_shaper_t *Shaper_New (void);
|
||||||
|
void Shaper_Delete (text_shaper_t *shaper);
|
||||||
|
void Shaper_FlushUnused (text_shaper_t *shaper);
|
||||||
|
|
||||||
|
shaped_glyphs_t Shaper_ShapeText (text_shaper_t *shaper,
|
||||||
|
const shaping_t *control,
|
||||||
|
const char *text, size_t text_len);
|
||||||
|
|
||||||
|
#endif//__QF_ui_shaper_h
|
|
@ -106,6 +106,7 @@ extern hb_feature_t CligOn;
|
||||||
|
|
||||||
struct font_s;
|
struct font_s;
|
||||||
struct passage_s;
|
struct passage_s;
|
||||||
|
struct text_shaper_s;
|
||||||
|
|
||||||
typedef struct text_system_s {
|
typedef struct text_system_s {
|
||||||
ecs_registry_t *reg;
|
ecs_registry_t *reg;
|
||||||
|
@ -118,7 +119,8 @@ struct view_s Text_PassageView (text_system_t textsys,
|
||||||
struct view_s Text_StringView (text_system_t textsys, struct view_s parent,
|
struct view_s Text_StringView (text_system_t textsys, struct view_s parent,
|
||||||
struct font_s *font,
|
struct font_s *font,
|
||||||
const char *str, uint32_t len,
|
const char *str, uint32_t len,
|
||||||
script_component_t *sc, featureset_t *fs);
|
script_component_t *sc, featureset_t *fs,
|
||||||
|
struct text_shaper_s *shaper);
|
||||||
void Text_SetScript (text_system_t textsys, uint32_t textid,
|
void Text_SetScript (text_system_t textsys, uint32_t textid,
|
||||||
const char *lang, hb_script_t script, text_dir_e dir);
|
const char *lang, hb_script_t script, text_dir_e dir);
|
||||||
void Text_SetFont (text_system_t textsys, uint32_t textid,
|
void Text_SetFont (text_system_t textsys, uint32_t textid,
|
||||||
|
|
|
@ -23,6 +23,7 @@ libs_ui_libQFgui_la_SOURCES= \
|
||||||
libs/ui/canvas.c \
|
libs/ui/canvas.c \
|
||||||
libs/ui/font.c \
|
libs/ui/font.c \
|
||||||
libs/ui/imui.c \
|
libs/ui/imui.c \
|
||||||
|
libs/ui/shaper.c \
|
||||||
libs/ui/text.c
|
libs/ui/text.c
|
||||||
|
|
||||||
libs_ui_libQFui_la_LDFLAGS= $(lib_ldflags)
|
libs_ui_libQFui_la_LDFLAGS= $(lib_ldflags)
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include "QF/ui/canvas.h"
|
#include "QF/ui/canvas.h"
|
||||||
#include "QF/ui/font.h"
|
#include "QF/ui/font.h"
|
||||||
#include "QF/ui/imui.h"
|
#include "QF/ui/imui.h"
|
||||||
|
#include "QF/ui/shaper.h"
|
||||||
#include "QF/ui/text.h"
|
#include "QF/ui/text.h"
|
||||||
|
|
||||||
#define c_percent_x (ctx->csys.imui_base + imui_percent_x)
|
#define c_percent_x (ctx->csys.imui_base + imui_percent_x)
|
||||||
|
@ -81,6 +82,9 @@ struct imui_ctx_s {
|
||||||
uint32_t canvas;
|
uint32_t canvas;
|
||||||
ecs_system_t vsys;
|
ecs_system_t vsys;
|
||||||
text_system_t tsys;
|
text_system_t tsys;
|
||||||
|
|
||||||
|
text_shaper_t *shaper;
|
||||||
|
|
||||||
hashctx_t *hashctx;
|
hashctx_t *hashctx;
|
||||||
hashtab_t *tab;
|
hashtab_t *tab;
|
||||||
PR_RESMAP (imui_state_t) state_map;
|
PR_RESMAP (imui_state_t) state_map;
|
||||||
|
@ -181,6 +185,7 @@ IMUI_NewContext (canvas_system_t canvas_sys, const char *font, float fontsize)
|
||||||
.canvas = canvas = Canvas_New (canvas_sys),
|
.canvas = canvas = Canvas_New (canvas_sys),
|
||||||
.vsys = { canvas_sys.reg, canvas_sys.view_base },
|
.vsys = { canvas_sys.reg, canvas_sys.view_base },
|
||||||
.tsys = { canvas_sys.reg, canvas_sys.view_base, canvas_sys.text_base },
|
.tsys = { canvas_sys.reg, canvas_sys.view_base, canvas_sys.text_base },
|
||||||
|
.shaper = Shaper_New (),
|
||||||
.root_view = Canvas_GetRootView (canvas_sys, canvas),
|
.root_view = Canvas_GetRootView (canvas_sys, canvas),
|
||||||
.parent_stack = DARRAY_STATIC_INIT (8),
|
.parent_stack = DARRAY_STATIC_INIT (8),
|
||||||
.hot = nullent,
|
.hot = nullent,
|
||||||
|
@ -234,6 +239,7 @@ IMUI_DestroyContext (imui_ctx_t *ctx)
|
||||||
|
|
||||||
Hash_DelTable (ctx->tab);
|
Hash_DelTable (ctx->tab);
|
||||||
Hash_DelContext (ctx->hashctx);
|
Hash_DelContext (ctx->hashctx);
|
||||||
|
Shaper_Delete (ctx->shaper);
|
||||||
free (ctx);
|
free (ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,6 +280,7 @@ IMUI_ProcessEvent (imui_ctx_t *ctx, const IE_event_t *ie_event)
|
||||||
void
|
void
|
||||||
IMUI_BeginFrame (imui_ctx_t *ctx)
|
IMUI_BeginFrame (imui_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
|
Shaper_FlushUnused (ctx->shaper);
|
||||||
uint32_t root_ent = ctx->root_view.id;
|
uint32_t root_ent = ctx->root_view.id;
|
||||||
auto root_size = View_GetLen (ctx->root_view);
|
auto root_size = View_GetLen (ctx->root_view);
|
||||||
Ent_RemoveComponent (root_ent, ctx->root_view.comp, ctx->root_view.reg);
|
Ent_RemoveComponent (root_ent, ctx->root_view.comp, ctx->root_view.reg);
|
||||||
|
@ -717,7 +724,8 @@ add_text (imui_ctx_t *ctx, view_t view, imui_state_t *state, int mode)
|
||||||
auto reg = ctx->csys.reg;
|
auto reg = ctx->csys.reg;
|
||||||
|
|
||||||
auto text = Text_StringView (ctx->tsys, view, ctx->font,
|
auto text = Text_StringView (ctx->tsys, view, ctx->font,
|
||||||
state->label, state->label_len, 0, 0);
|
state->label, state->label_len, 0, 0,
|
||||||
|
ctx->shaper);
|
||||||
|
|
||||||
int ascender = ctx->font->face->size->metrics.ascender / 64;
|
int ascender = ctx->font->face->size->metrics.ascender / 64;
|
||||||
int descender = ctx->font->face->size->metrics.descender / 64;
|
int descender = ctx->font->face->size->metrics.descender / 64;
|
||||||
|
|
|
@ -0,0 +1,295 @@
|
||||||
|
/*
|
||||||
|
shaper.c
|
||||||
|
|
||||||
|
Immediate mode user inferface
|
||||||
|
|
||||||
|
Copyright (C) 2023 Bill Currie <bill@taniwha.org>
|
||||||
|
|
||||||
|
Author: Bill Currie <bill@taniwha.org>
|
||||||
|
Date: 2023/07/01
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to:
|
||||||
|
|
||||||
|
Free Software Foundation, Inc.
|
||||||
|
59 Temple Place - Suite 330
|
||||||
|
Boston, MA 02111-1307, USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "QF/darray.h"
|
||||||
|
#include "QF/ecs.h"
|
||||||
|
#include "QF/fbsearch.h"
|
||||||
|
#include "QF/hash.h"
|
||||||
|
#include "QF/mathlib.h"
|
||||||
|
#include "QF/progs.h"
|
||||||
|
#include "QF/quakeio.h"
|
||||||
|
#include "QF/va.h"
|
||||||
|
|
||||||
|
#include "QF/input/event.h"
|
||||||
|
|
||||||
|
#include "QF/ui/font.h"
|
||||||
|
#include "QF/ui/shaper.h"
|
||||||
|
|
||||||
|
typedef struct shaper_cache_s {
|
||||||
|
struct shaper_cache_s *next;
|
||||||
|
struct shaper_cache_s **prev;
|
||||||
|
script_component_t script;
|
||||||
|
featureset_t features;
|
||||||
|
const font_t *font;
|
||||||
|
char *text;
|
||||||
|
size_t text_len;
|
||||||
|
hb_buffer_t *buffer;
|
||||||
|
} shaper_cache_t;
|
||||||
|
|
||||||
|
typedef struct shaper_font_s {
|
||||||
|
hb_font_t *hb_font;
|
||||||
|
uint32_t fontid;
|
||||||
|
} shaper_font_t;
|
||||||
|
|
||||||
|
struct text_shaper_s {
|
||||||
|
hashtab_t *tab;
|
||||||
|
hashctx_t *hashctx;
|
||||||
|
PR_RESMAP (shaper_cache_t) cache_map;
|
||||||
|
shaper_cache_t *buffers;
|
||||||
|
shaper_cache_t *unused_buffers; // buffers not used since last flush
|
||||||
|
struct DARRAY_TYPE (shaper_font_t) fonts;
|
||||||
|
};
|
||||||
|
|
||||||
|
static uintptr_t
|
||||||
|
shaper_get_hash (const void *obj, void *data)
|
||||||
|
{
|
||||||
|
const shaper_cache_t *cache = obj;
|
||||||
|
uintptr_t script = Hash_Buffer (&cache->script, sizeof (cache->script));
|
||||||
|
auto f = &cache->features;
|
||||||
|
uintptr_t features = 0;
|
||||||
|
if (f->a && f->size) {
|
||||||
|
features = Hash_Buffer (f->a, sizeof (hb_feature_t[f->size]));
|
||||||
|
}
|
||||||
|
uintptr_t font = cache->font->fontid;
|
||||||
|
uintptr_t text = Hash_nString (cache->text, cache->text_len);
|
||||||
|
return script + features + font + text;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
shaper_compare (const void *a, const void *b, void *data)
|
||||||
|
{
|
||||||
|
const shaper_cache_t *cachea = a;
|
||||||
|
const shaper_cache_t *cacheb = b;
|
||||||
|
if (cachea->font->fontid != cacheb->font->fontid) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto af = &cachea->features;
|
||||||
|
auto bf = &cacheb->features;
|
||||||
|
if (af->size != bf->size || (!af->a != !bf->a)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (af->size && memcmp (af->a, &bf->a, sizeof (hb_feature_t[af->size]))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (memcmp (&cachea->script, &cacheb->script, sizeof (cachea->script))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (cachea->text_len == cacheb->text_len
|
||||||
|
&& !strncmp (cachea->text, cacheb->text, cachea->text_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shaper_free_cache_data (shaper_cache_t *cache)
|
||||||
|
{
|
||||||
|
free (cache->text);
|
||||||
|
free (cache->features.a);
|
||||||
|
if (cache->buffer) {
|
||||||
|
hb_buffer_destroy (cache->buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text_shaper_t *
|
||||||
|
Shaper_New (void)
|
||||||
|
{
|
||||||
|
text_shaper_t *shaper = malloc (sizeof (text_shaper_t));
|
||||||
|
*shaper = (text_shaper_t) {
|
||||||
|
.fonts = DARRAY_STATIC_INIT (8),
|
||||||
|
};
|
||||||
|
shaper->tab = Hash_NewTable (511, 0, 0, shaper, &shaper->hashctx);
|
||||||
|
Hash_SetHashCompare (shaper->tab, shaper_get_hash, shaper_compare);
|
||||||
|
return shaper;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Shaper_Delete (text_shaper_t *shaper)
|
||||||
|
{
|
||||||
|
for (auto c = shaper->buffers; c; c = c->next) {
|
||||||
|
shaper_free_cache_data (c);
|
||||||
|
}
|
||||||
|
for (auto c = shaper->unused_buffers; c; c = c->next) {
|
||||||
|
shaper_free_cache_data (c);
|
||||||
|
}
|
||||||
|
PR_RESDELMAP (shaper->cache_map);
|
||||||
|
Hash_DelTable (shaper->tab);
|
||||||
|
Hash_DelContext (shaper->hashctx);
|
||||||
|
for (size_t i = 0; i < shaper->fonts.size; i++) {
|
||||||
|
hb_font_destroy (shaper->fonts.a[i].hb_font);
|
||||||
|
}
|
||||||
|
DARRAY_CLEAR (&shaper->fonts);
|
||||||
|
free (shaper);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shaper_link_cache (text_shaper_t *shaper, shaper_cache_t *cache)
|
||||||
|
{
|
||||||
|
cache->next = shaper->buffers;
|
||||||
|
cache->prev = &shaper->buffers;
|
||||||
|
if (shaper->buffers) {
|
||||||
|
shaper->buffers->prev = &cache->next;
|
||||||
|
}
|
||||||
|
shaper->buffers = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shaper_unlink_cache (shaper_cache_t *cache)
|
||||||
|
{
|
||||||
|
if (cache->next) {
|
||||||
|
cache->next->prev = cache->prev;
|
||||||
|
}
|
||||||
|
*cache->prev = cache->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static shaper_cache_t *
|
||||||
|
shaper_cache_new (text_shaper_t *shaper)
|
||||||
|
{
|
||||||
|
shaper_cache_t *cache = PR_RESNEW (shaper->cache_map);
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shaper_cache_free (text_shaper_t *shaper, shaper_cache_t *cache)
|
||||||
|
{
|
||||||
|
Hash_DelElement (shaper->tab, cache);
|
||||||
|
shaper_unlink_cache (cache);
|
||||||
|
shaper_free_cache_data (cache);
|
||||||
|
PR_RESFREE (shaper->cache_map, cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Shaper_FlushUnused (text_shaper_t *shaper)
|
||||||
|
{
|
||||||
|
shaper_cache_t *c;
|
||||||
|
while ((c = shaper->unused_buffers)) {
|
||||||
|
shaper_cache_free (shaper, c);
|
||||||
|
}
|
||||||
|
shaper->unused_buffers = shaper->buffers;
|
||||||
|
if (shaper->buffers) {
|
||||||
|
shaper->buffers->prev = &shaper->unused_buffers;
|
||||||
|
}
|
||||||
|
shaper->buffers = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
shaper_font_compare (const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const uint32_t *fontid = a;
|
||||||
|
const shaper_font_t *font = b;
|
||||||
|
return *fontid - font->fontid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static hb_font_t *
|
||||||
|
shaper_find_font (text_shaper_t *shaper, const font_t *font)
|
||||||
|
{
|
||||||
|
shaper_font_t *f = fbsearch (&font->fontid, shaper->fonts.a,
|
||||||
|
shaper->fonts.size, sizeof (shaper_font_t),
|
||||||
|
shaper_font_compare);
|
||||||
|
if (f && f->fontid == font->fontid) {
|
||||||
|
return f->hb_font;
|
||||||
|
}
|
||||||
|
auto nf = (shaper_font_t) {
|
||||||
|
.hb_font = hb_ft_font_create (font->face, 0),
|
||||||
|
.fontid = font->fontid,
|
||||||
|
};
|
||||||
|
size_t index = f - shaper->fonts.a;
|
||||||
|
if (!f) {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
DARRAY_INSERT_AT (&shaper->fonts, nf, index);
|
||||||
|
return nf.hb_font;
|
||||||
|
}
|
||||||
|
|
||||||
|
shaped_glyphs_t
|
||||||
|
Shaper_ShapeText (text_shaper_t *shaper, const shaping_t *control,
|
||||||
|
const char *text, size_t text_len)
|
||||||
|
{
|
||||||
|
shaper_cache_t search_cache = {
|
||||||
|
.script = *control->script,
|
||||||
|
.features.size = control->features->size,
|
||||||
|
.features.a = control->features->a,
|
||||||
|
.font = control->font,
|
||||||
|
.text = (char *) text,
|
||||||
|
.text_len = text_len,
|
||||||
|
};
|
||||||
|
if (!control->features->size) {
|
||||||
|
search_cache.features.a = 0;
|
||||||
|
}
|
||||||
|
shaper_cache_t *cache = Hash_FindElement (shaper->tab, &search_cache);
|
||||||
|
if (!cache) {
|
||||||
|
cache = shaper_cache_new (shaper);
|
||||||
|
cache->script = *control->script;
|
||||||
|
cache->features.size = control->features->size;
|
||||||
|
if (control->features->size) {
|
||||||
|
size_t feat_size = sizeof (hb_feature_t[control->features->size]);
|
||||||
|
cache->features.a = malloc (feat_size);
|
||||||
|
memcpy (cache->features.a, control->features->a, feat_size);
|
||||||
|
} else {
|
||||||
|
cache->features.a = 0;
|
||||||
|
}
|
||||||
|
cache->font = control->font;
|
||||||
|
cache->text_len = text_len;
|
||||||
|
cache->text = malloc (cache->text_len + 1);
|
||||||
|
strncpy (cache->text, text, text_len);
|
||||||
|
cache->text[text_len] = 0;
|
||||||
|
cache->buffer = hb_buffer_create ();
|
||||||
|
hb_buffer_allocation_successful (cache->buffer);
|
||||||
|
|
||||||
|
Hash_AddElement (shaper->tab, cache);
|
||||||
|
|
||||||
|
auto buffer = cache->buffer;
|
||||||
|
auto hb_font = shaper_find_font (shaper, control->font);
|
||||||
|
auto direction = cache->script.direction;
|
||||||
|
auto script = cache->script.script;
|
||||||
|
auto language = cache->script.language;
|
||||||
|
auto features = &cache->features;
|
||||||
|
hb_buffer_reset (cache->buffer);
|
||||||
|
hb_buffer_set_direction (buffer, direction | HB_DIRECTION_LTR);
|
||||||
|
hb_buffer_set_script (buffer, script);
|
||||||
|
hb_buffer_set_language (buffer, language);
|
||||||
|
hb_buffer_add_utf8 (buffer, text, cache->text_len, 0, cache->text_len);
|
||||||
|
hb_shape (hb_font, buffer, features->a, features->size);
|
||||||
|
} else {
|
||||||
|
// remove from the unused buffers list if there, else move to head
|
||||||
|
// of used buffers list (via link call below)
|
||||||
|
shaper_unlink_cache (cache);
|
||||||
|
}
|
||||||
|
shaper_link_cache (shaper, cache);
|
||||||
|
unsigned c;
|
||||||
|
shaped_glyphs_t glyphs = {
|
||||||
|
.glyphInfo = hb_buffer_get_glyph_infos (cache->buffer, &c),
|
||||||
|
.glyphPos = hb_buffer_get_glyph_positions (cache->buffer, &c),
|
||||||
|
};
|
||||||
|
glyphs.count = c;
|
||||||
|
return glyphs;
|
||||||
|
}
|
|
@ -38,6 +38,7 @@
|
||||||
|
|
||||||
#include "QF/ui/font.h"
|
#include "QF/ui/font.h"
|
||||||
#include "QF/ui/passage.h"
|
#include "QF/ui/passage.h"
|
||||||
|
#include "QF/ui/shaper.h"
|
||||||
#include "QF/ui/text.h"
|
#include "QF/ui/text.h"
|
||||||
#include "QF/ui/view.h"
|
#include "QF/ui/view.h"
|
||||||
|
|
||||||
|
@ -116,7 +117,8 @@ static view_resize_f text_flow_funcs[] = {
|
||||||
|
|
||||||
static void
|
static void
|
||||||
layout_glyphs (glyphnode_t *node, font_t *font, unsigned glpyhCount,
|
layout_glyphs (glyphnode_t *node, font_t *font, unsigned glpyhCount,
|
||||||
hb_glyph_info_t *glyphInfo, hb_glyph_position_t *glyphPos)
|
const hb_glyph_info_t *glyphInfo,
|
||||||
|
const hb_glyph_position_t *glyphPos)
|
||||||
{
|
{
|
||||||
int x = 0, y = 0;
|
int x = 0, y = 0;
|
||||||
for (unsigned k = 0; k < glpyhCount; k++) {
|
for (unsigned k = 0; k < glpyhCount; k++) {
|
||||||
|
@ -320,7 +322,8 @@ Text_PassageView (text_system_t textsys, font_t *font, passage_t *passage)
|
||||||
view_t
|
view_t
|
||||||
Text_StringView (text_system_t textsys, view_t parent,
|
Text_StringView (text_system_t textsys, view_t parent,
|
||||||
font_t *font, const char *str, uint32_t len,
|
font_t *font, const char *str, uint32_t len,
|
||||||
script_component_t *sc, featureset_t *fs)
|
script_component_t *sc, featureset_t *fs,
|
||||||
|
text_shaper_t *shaper)
|
||||||
{
|
{
|
||||||
auto reg = textsys.reg;
|
auto reg = textsys.reg;
|
||||||
uint32_t c_glyphs = textsys.text_base + text_glyphs;
|
uint32_t c_glyphs = textsys.text_base + text_glyphs;
|
||||||
|
@ -328,37 +331,25 @@ Text_StringView (text_system_t textsys, view_t parent,
|
||||||
glyphnode_t *glyph_nodes = 0;
|
glyphnode_t *glyph_nodes = 0;
|
||||||
glyphnode_t **head = &glyph_nodes;
|
glyphnode_t **head = &glyph_nodes;
|
||||||
|
|
||||||
hb_font_t *fnt = hb_ft_font_create (font->face, 0);
|
|
||||||
hb_buffer_t *buffer = hb_buffer_create ();
|
|
||||||
hb_buffer_allocation_successful (buffer);
|
|
||||||
|
|
||||||
hb_script_t script = HB_SCRIPT_LATIN;
|
script_component_t script = {
|
||||||
text_dir_e direction = text_right_down;
|
.script = sc ? sc->script : HB_SCRIPT_LATIN,
|
||||||
hb_language_t language = hb_language_from_string ("en", 2);
|
.direction = sc ? sc->direction : text_right_down,
|
||||||
|
.language = sc ? sc->language : hb_language_from_string ("en", 2),
|
||||||
|
};
|
||||||
featureset_t text_features = DARRAY_STATIC_INIT (0);
|
featureset_t text_features = DARRAY_STATIC_INIT (0);
|
||||||
featureset_t *features = &text_features;
|
shaping_t shaping = {
|
||||||
|
.script = &script,
|
||||||
if (sc) {
|
.features = fs ? fs : &text_features,
|
||||||
script = sc->script;
|
.font = font,
|
||||||
language = sc->language;
|
};
|
||||||
direction = sc->direction;
|
|
||||||
}
|
|
||||||
if (fs) {
|
|
||||||
features = fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t glyph_count = 0;
|
uint32_t glyph_count = 0;
|
||||||
|
|
||||||
hb_buffer_reset (buffer);
|
auto shaped_glyphs = Shaper_ShapeText (shaper, &shaping, str, len);
|
||||||
hb_buffer_set_direction (buffer, direction | HB_DIRECTION_LTR);
|
unsigned c = shaped_glyphs.count;
|
||||||
hb_buffer_set_script (buffer, script);
|
auto glyphInfo = shaped_glyphs.glyphInfo;
|
||||||
hb_buffer_set_language (buffer, language);
|
auto glyphPos = shaped_glyphs.glyphPos;
|
||||||
hb_buffer_add_utf8 (buffer, str, len, 0, len);
|
|
||||||
hb_shape (fnt, buffer, features->a, features->size);
|
|
||||||
|
|
||||||
unsigned c;
|
|
||||||
auto glyphInfo = hb_buffer_get_glyph_infos (buffer, &c);
|
|
||||||
auto glyphPos = hb_buffer_get_glyph_positions (buffer, &c);
|
|
||||||
|
|
||||||
*head = alloca (sizeof (glyphnode_t));
|
*head = alloca (sizeof (glyphnode_t));
|
||||||
**head = (glyphnode_t) {
|
**head = (glyphnode_t) {
|
||||||
|
@ -387,8 +378,6 @@ Text_StringView (text_system_t textsys, view_t parent,
|
||||||
.count = glyph_count,
|
.count = glyph_count,
|
||||||
};
|
};
|
||||||
Ent_SetComponent (stringview.id, c_passage_glyphs, reg, &glyphset);
|
Ent_SetComponent (stringview.id, c_passage_glyphs, reg, &glyphset);
|
||||||
hb_buffer_destroy (buffer);
|
|
||||||
hb_font_destroy (fnt);
|
|
||||||
return stringview;
|
return stringview;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue