[gtk+/wip/matthiasc/text-node] More text node work



commit ae574b168acf36b5a3cd3a1ccd75fb196608c0dc
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Sep 9 11:40:15 2017 -0400

    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.

 gsk/gskrendernodeimpl.c    |  143 ++++++++++++++++++++++++++++++++++++++++++--
 gsk/gskrendernodeprivate.h |    2 +
 gsk/gskvulkanrenderpass.c  |   27 ++++++++-
 3 files changed, 165 insertions(+), 7 deletions(-)
---
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index 4b91076..2474468 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 be29f87..a0b0429 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 f3b2b0c..1308c3a 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),


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]