diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index 4b91076e5b..247446881f 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -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; diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h index be29f8725c..a0b0429dcc 100644 --- a/gsk/gskrendernodeprivate.h +++ b/gsk/gskrendernodeprivate.h @@ -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); diff --git a/gsk/gskvulkanrenderpass.c b/gsk/gskvulkanrenderpass.c index f3b2b0cb92..1308c3a44a 100644 --- a/gsk/gskvulkanrenderpass.c +++ b/gsk/gskvulkanrenderpass.c @@ -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),