More text node work

Use the surface as a plain texture. This currently doesn't do
the right thing for masking, and loses text color. Keep a simple
cache of the surfaces.
This commit is contained in:
Matthias Clasen
2017-09-09 11:40:15 -04:00
parent 344bad9788
commit ae574b168a
3 changed files with 165 additions and 7 deletions

View File

@@ -3889,6 +3889,13 @@ render_text (cairo_t *cr,
g_free (cairo_glyphs);
}
/*
* Next steps:
* - figure out A8 textures and masking
* - make the shader use per-glyph rects from a larger texture
* - switch to using a font atlas insead of per-call cached surfaces
*/
static void
gsk_text_node_draw (GskRenderNode *node,
cairo_t *cr)
@@ -3981,6 +3988,116 @@ font_has_color_glyphs (PangoFont *font)
return has_color;
}
typedef struct {
PangoFont *font;
PangoGlyphString *glyphs;
guint hash;
cairo_surface_t *surface;
} CacheItem;
static guint
item_hash (gconstpointer key)
{
const CacheItem *item = key;
PangoFontDescription *desc;
guint32 h = 5381;
char *p, *end;
if (item->hash != 0)
return item->hash;
end = ((char *)&item->glyphs->glyphs[item->glyphs->num_glyphs]);
for (p = (char *)item->glyphs->glyphs; p < end; p++)
h = (h << 5) + h + *p;
desc = pango_font_describe (item->font);
h ^= pango_font_description_hash (desc);
pango_font_description_free (desc);
if (h == 0)
h = 1;
return h;
}
static gboolean
item_equal (gconstpointer v1, gconstpointer v2)
{
const CacheItem *i1 = v1;
const CacheItem *i2 = v2;
int i;
PangoFontDescription *desc1, *desc2;
gboolean ret;
if (i1->glyphs->num_glyphs != i2->glyphs->num_glyphs)
return FALSE;
for (i = 0; i < i1->glyphs->num_glyphs; i++)
{
if (i1->glyphs->glyphs[i].glyph != i2->glyphs->glyphs[i].glyph)
return FALSE;
}
desc1 = pango_font_describe (i1->font);
desc2 = pango_font_describe (i2->font);
ret = pango_font_description_equal (desc1, desc2);
pango_font_description_free (desc1);
pango_font_description_free (desc2);
return ret;
}
static void
item_free (gpointer data)
{
CacheItem *item = data;
g_object_unref (item->font);
pango_glyph_string_free (item->glyphs);
if (item->surface)
cairo_surface_destroy (item->surface);
g_free (item);
}
static GHashTable *cache;
static cairo_surface_t *
find_cached_surface (PangoFont *font, PangoGlyphString *glyphs)
{
CacheItem key;
CacheItem *value;
key.font = font;
key.glyphs = glyphs;
key.hash = 0;
key.surface = NULL;
if (!cache)
cache = g_hash_table_new_full (item_hash, item_equal, NULL, item_free);
value = g_hash_table_lookup (cache, &key);
if (value)
return cairo_surface_reference (value->surface);
return NULL;
}
static void
cache_surface (PangoFont *font, PangoGlyphString *glyphs, cairo_surface_t *surface)
{
CacheItem *item;
item = g_new (CacheItem, 1);
item->font = g_object_ref (font);
item->glyphs = pango_glyph_string_copy (glyphs);
item->hash = 0;
item->surface = cairo_surface_reference (surface);
g_hash_table_insert (cache, item, item);
}
GskRenderNode *
gsk_text_node_new (PangoFont *font,
PangoGlyphString *glyphs,
@@ -4016,16 +4133,30 @@ gsk_text_node_new (PangoFont *font,
ink_rect.x + ink_rect.width,
ink_rect.height);
self->surface = cairo_image_surface_create (self->has_color ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_A8,
ink_rect.x + ink_rect.width,
ink_rect.height);
cr = cairo_create (self->surface);
render_text (cr, glyphs, font, 0, - ink_rect.y);
cairo_destroy (cr);
self->surface = find_cached_surface (font, glyphs);
if (!self->surface)
{
self->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, //self->has_color ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_A8,
ink_rect.x + ink_rect.width,
ink_rect.height);
cr = cairo_create (self->surface);
render_text (cr, glyphs, font, 0, - ink_rect.y);
cairo_destroy (cr);
cache_surface (font, glyphs, self->surface);
}
return &self->render_node;
}
cairo_surface_t *
gsk_text_node_get_surface (GskRenderNode *node)
{
GskTextNode *self = (GskTextNode *) node;
return self->surface;
}
/*** GSK_BLUR_NODE ***/
typedef struct _GskBlurNode GskBlurNode;

View File

@@ -81,6 +81,8 @@ cairo_surface_t *gsk_cairo_node_get_surface (GskRenderNode *node);
GskTexture *gsk_texture_node_get_texture (GskRenderNode *node);
cairo_surface_t *gsk_text_node_get_surface (GskRenderNode *node);
const GdkRGBA *gsk_color_node_peek_color (GskRenderNode *node);
const graphene_rect_t * gsk_clip_node_peek_clip (GskRenderNode *node);

View File

@@ -172,6 +172,22 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
g_array_append_val (self->render_ops, op);
return;
case GSK_TEXT_NODE:
if (gsk_text_node_get_surface (node) == NULL)
return;
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_BLEND;
else if (constants->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_BLEND_CLIP;
else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_BLEND_CLIP_ROUNDED;
else
FALLBACK ("Cairo nodes can't deal with clip type %u\n", constants->clip.type);
op.type = GSK_VULKAN_OP_SURFACE;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return;
case GSK_TEXTURE_NODE:
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_BLEND;
@@ -413,6 +429,10 @@ gsk_vulkan_render_pass_get_node_as_texture (GskVulkanRenderPass *self,
surface = cairo_surface_reference (gsk_cairo_node_get_surface (node));
goto got_surface;
case GSK_TEXT_NODE:
surface = cairo_surface_reference (gsk_text_node_get_surface (node));
goto got_surface;
default:
break;
}
@@ -528,7 +548,12 @@ gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self,
case GSK_VULKAN_OP_SURFACE:
{
cairo_surface_t *surface = gsk_cairo_node_get_surface (op->render.node);
cairo_surface_t *surface;
if (gsk_render_node_get_node_type (op->render.node) == GSK_CAIRO_NODE)
surface = gsk_cairo_node_get_surface (op->render.node);
else
surface = gsk_text_node_get_surface (op->render.node);
op->render.source = gsk_vulkan_image_new_from_data (uploader,
cairo_image_surface_get_data (surface),
cairo_image_surface_get_width (surface),