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:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user