gsk: Use harfbuzz for color fonts

harfbuzz has all the information we need, so we
can avoid poking directly at freetype apis. Also
drop the caching of color glyph information until
it turns out to be a problem.
This commit is contained in:
Matthias Clasen 2021-07-31 15:22:14 -04:00
parent 0bf46b4845
commit 915c229396

View File

@ -30,7 +30,7 @@
#include "gdk/gdktextureprivate.h" #include "gdk/gdktextureprivate.h"
#include "gdk/gdk-private.h" #include "gdk/gdk-private.h"
#include <cairo-ft.h> #include <hb-ot.h>
static inline void static inline void
gsk_cairo_rectangle (cairo_t *cr, gsk_cairo_rectangle (cairo_t *cr,
@ -4425,119 +4425,45 @@ gsk_text_node_diff (GskRenderNode *node1,
} }
static gboolean static gboolean
font_has_color_glyphs (const PangoFont *font) font_has_color_glyphs (PangoFont *font)
{ {
cairo_scaled_font_t *scaled_font; hb_face_t *face = hb_font_get_face (pango_font_get_hb_font (font));
gboolean has_color = FALSE;
scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font); return hb_ot_color_has_layers (face) ||
if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_FT) hb_ot_color_has_png (face) ||
{ hb_ot_color_has_svg (face);
FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
has_color = (FT_HAS_COLOR (ft_face) != 0);
cairo_ft_scaled_font_unlock_face (scaled_font);
}
return has_color;
} }
static gboolean static gboolean
glyph_has_color (FT_Face face, glyph_has_color (PangoFont *font,
guint glyph) guint glyph)
{ {
FT_Error error; hb_font_t *hb_font = pango_font_get_hb_font (font);
hb_face_t *face = hb_font_get_face (hb_font);
hb_blob_t *blob;
error = FT_Load_Glyph (face, glyph, FT_LOAD_COLOR); if (hb_ot_color_glyph_get_layers (face, glyph, 0, NULL, NULL) > 0)
if (error != 0)
return FALSE;
error = FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL);
if (error != 0)
return FALSE;
if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
return TRUE; return TRUE;
return FALSE; blob = hb_ot_color_glyph_reference_png (hb_font, glyph);
} if (blob)
static GHashTable *
ensure_color_glyph_cache (const PangoFont *font)
{
GHashTable *cache;
cache = (GHashTable *) g_object_get_data (G_OBJECT (font), "gsk-color-glyph-cache");
if (!cache)
{ {
cache = g_hash_table_new (NULL, NULL); guint length = hb_blob_get_length (blob);
g_object_set_data_full (G_OBJECT (font), "gsk-color-glyph-cache", hb_blob_destroy (blob);
cache, (GDestroyNotify) g_hash_table_unref); return length > 0;
} }
return cache; blob = hb_ot_color_glyph_reference_svg (face, glyph);
} if (blob)
static inline gboolean
lookup_color_glyph_cache (GHashTable *cache,
PangoGlyph glyph,
gboolean *has_color)
{
gpointer value;
if (g_hash_table_lookup_extended (cache, GUINT_TO_POINTER (glyph), NULL, &value))
{ {
*has_color = GPOINTER_TO_UINT (value); guint length = hb_blob_get_length (blob);
return TRUE; hb_blob_destroy (blob);
return length > 0;
} }
return FALSE; return FALSE;
} }
static inline void
insert_color_glyph_cache (GHashTable *cache,
PangoGlyph glyph,
gboolean has_color)
{
g_hash_table_insert (cache, GUINT_TO_POINTER (glyph), GUINT_TO_POINTER (has_color));
}
static void
mark_color_glyphs (const PangoFont *font,
PangoGlyphInfo *glyphs,
int num_glyphs)
{
cairo_scaled_font_t *scaled_font;
FT_Face ft_face;
GHashTable *cache;
scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_FT)
return;
ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
if (!FT_HAS_COLOR (ft_face))
goto out;
cache = ensure_color_glyph_cache (font);
for (int i = 0; i < num_glyphs; i++)
{
gboolean has_color;
if (!lookup_color_glyph_cache (cache, glyphs[i].glyph, &has_color))
{
has_color = glyph_has_color (ft_face, glyphs[i].glyph);
insert_color_glyph_cache (cache, glyphs[i].glyph, has_color);
}
if (has_color)
GLYPH_SET_COLOR (&glyphs[i]);
}
out:
cairo_ft_scaled_font_unlock_face (scaled_font);
}
/** /**
* gsk_text_node_new: * gsk_text_node_new:
* @font: the `PangoFont` containing the glyphs * @font: the `PangoFont` containing the glyphs
@ -4561,6 +4487,9 @@ gsk_text_node_new (PangoFont *font,
GskTextNode *self; GskTextNode *self;
GskRenderNode *node; GskRenderNode *node;
PangoRectangle ink_rect; PangoRectangle ink_rect;
PangoGlyphInfo *glyph_infos;
gboolean has_color_glyphs;
int n;
pango_glyph_string_extents (glyphs, font, &ink_rect, NULL); pango_glyph_string_extents (glyphs, font, &ink_rect, NULL);
pango_extents_to_pixels (&ink_rect, NULL); pango_extents_to_pixels (&ink_rect, NULL);
@ -4573,24 +4502,35 @@ gsk_text_node_new (PangoFont *font,
node = (GskRenderNode *) self; node = (GskRenderNode *) self;
self->font = g_object_ref (font); self->font = g_object_ref (font);
self->has_color_glyphs = font_has_color_glyphs (font);
self->color = *color; self->color = *color;
self->offset = *offset; self->offset = *offset;
self->glyphs = g_malloc_n (glyphs->num_glyphs, sizeof (PangoGlyphInfo)); self->has_color_glyphs = FALSE;
/* skip empty glyphs */ glyph_infos = g_malloc_n (glyphs->num_glyphs, sizeof (PangoGlyphInfo));
self->num_glyphs = 0; has_color_glyphs = font_has_color_glyphs (font);
n = 0;
for (int i = 0; i < glyphs->num_glyphs; i++) for (int i = 0; i < glyphs->num_glyphs; i++)
{ {
if (glyphs->glyphs[i].glyph != PANGO_GLYPH_EMPTY) /* skip empty glyphs */
if (glyphs->glyphs[i].glyph == PANGO_GLYPH_EMPTY)
continue;
glyph_infos[n] = glyphs->glyphs[i];
GLYPH_CLEAR_COLOR (&glyph_infos[n]);
if (has_color_glyphs &&
glyph_has_color (font, glyph_infos[n].glyph))
{ {
self->glyphs[self->num_glyphs] = glyphs->glyphs[i]; self->has_color_glyphs = TRUE;
GLYPH_CLEAR_COLOR (&self->glyphs[self->num_glyphs]); GLYPH_SET_COLOR (&glyph_infos[n]);
self->num_glyphs++;
} }
n++;
} }
mark_color_glyphs (font, self->glyphs, self->num_glyphs); self->glyphs = glyph_infos;
self->num_glyphs = n;
graphene_rect_init (&node->bounds, graphene_rect_init (&node->bounds,
offset->x + ink_rect.x - 1, offset->x + ink_rect.x - 1,