[gtk+] Add a render node for text



commit 4975aa7920b0857a54114556c9830f7d9b69abfc
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Aug 29 17:28:10 2017 -0400

    Add a render node for text
    
    This is just a wrapper around a PangoGlyphString + PangoFont.
    Basically, the arguments that are passed to pango_renderer_draw_glyphs.

 gsk/gskenums.h                         |    4 +-
 gsk/gskrendernode.h                    |    9 ++
 gsk/gskrendernodeimpl.c                |  245 +++++++++++++++++++++++++++++++-
 gtk/inspector/gtktreemodelrendernode.c |    1 +
 gtk/inspector/recorder.c               |    2 +
 5 files changed, 259 insertions(+), 2 deletions(-)
---
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index b2869e8..1141485 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -45,6 +45,7 @@
  * @GSK_SHADOW_NODE: A node that draws a shadow below its child
  * @GSK_BLEND_NODE: A node that blends two children together
  * @GSK_CROSS_FADE_NODE: A node that cross-fades between two children
+ * @GSK_TEXT_NODE: A node containing a glyph string
  *
  * The type of a node determines what the node is rendering.
  *
@@ -69,7 +70,8 @@ typedef enum {
   GSK_ROUNDED_CLIP_NODE,
   GSK_SHADOW_NODE,
   GSK_BLEND_NODE,
-  GSK_CROSS_FADE_NODE
+  GSK_CROSS_FADE_NODE,
+  GSK_TEXT_NODE
 } GskRenderNodeType;
 
 /**
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 0bbeed6..4c94f8d 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -174,6 +174,15 @@ GskRenderNode *         gsk_cross_fade_node_new                 (GskRenderNode
                                                                  GskRenderNode            *end,
                                                                  double                    progress);
 
+GDK_AVAILABLE_IN_3_92
+GskRenderNode *         gsk_text_node_new                       (PangoFont        *font,
+                                                                 PangoGlyphString *glyphs,
+                                                                 const GdkRGBA    *color,
+                                                                 int               x_offset,
+                                                                 int               y_offset,
+                                                                 double            base_x,
+                                                                 double            base_y);
+
 GDK_AVAILABLE_IN_3_90
 void                    gsk_render_node_set_scaling_filters     (GskRenderNode *node,
                                                                  GskScalingFilter min_filter,
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index 214025b..2d04bb3 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -3801,6 +3801,248 @@ gsk_cross_fade_node_get_progress (GskRenderNode *node)
   return self->progress;
 }
 
+/*** GSK_TEXT_NODE ***/
+
+typedef struct _GskTextNode GskTextNode;
+
+struct _GskTextNode
+{
+  GskRenderNode render_node;
+
+  PangoFont *font;
+  PangoGlyphString *glyphs;
+  GdkRGBA color;
+  int x_offset;
+  int y_offset;
+  double base_x;
+  double base_y;
+};
+
+static void
+gsk_text_node_finalize (GskRenderNode *node)
+{
+  GskTextNode *self = (GskTextNode *) node;
+
+  g_object_unref (self->font);
+  pango_glyph_string_free (self->glyphs);
+}
+
+static gboolean
+_pango_cairo_font_install (PangoFont *font,
+                           cairo_t   *cr)
+{
+  cairo_scaled_font_t *scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
+
+  if (G_UNLIKELY (scaled_font == NULL || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
+    return FALSE;
+
+  cairo_set_scaled_font (cr, scaled_font);
+
+  return TRUE;
+}
+
+#ifndef STACK_BUFFER_SIZE
+#define STACK_BUFFER_SIZE (512 * sizeof (int))
+#endif
+
+#define STACK_ARRAY_LENGTH(T) (STACK_BUFFER_SIZE / sizeof(T))
+
+static void
+gsk_text_node_draw (GskRenderNode *node,
+                    cairo_t       *cr)
+{
+  GskTextNode *self = (GskTextNode *) node;
+  int i, count;
+  int x_position = 0;
+  cairo_glyph_t *cairo_glyphs;
+  cairo_glyph_t stack_glyphs[STACK_ARRAY_LENGTH (cairo_glyph_t)];
+
+  cairo_save (cr);
+
+  cairo_translate (cr, self->x_offset, self->y_offset);
+
+  gdk_cairo_set_source_rgba (cr, &self->color);
+  if (!_pango_cairo_font_install (self->font, cr))
+    goto done;
+
+  if (self->glyphs->num_glyphs > (int) G_N_ELEMENTS (stack_glyphs))
+    cairo_glyphs = g_new (cairo_glyph_t, self->glyphs->num_glyphs);
+  else
+    cairo_glyphs = stack_glyphs;
+
+  count = 0;
+  for (i = 0; i < self->glyphs->num_glyphs; i++)
+    {
+      PangoGlyphInfo *gi = &self->glyphs->glyphs[i];
+
+      if (gi->glyph != PANGO_GLYPH_EMPTY)
+        {
+          double cx = self->base_x + (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
+          double cy = gi->geometry.y_offset == 0 ? self->base_y : self->base_y + 
(double)(gi->geometry.y_offset) / PANGO_SCALE;
+
+          if (!(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
+            {
+              cairo_glyphs[count].index = gi->glyph;
+              cairo_glyphs[count].x = cx;
+              cairo_glyphs[count].y = cy;
+              count++;
+            }
+        }
+      x_position += gi->geometry.width;
+    }
+
+  cairo_show_glyphs (cr, cairo_glyphs, count);
+
+  if (cairo_glyphs != stack_glyphs)
+    g_free (cairo_glyphs);
+
+done:
+  cairo_restore (cr);
+}
+
+#define GSK_TEXT_NODE_VARIANT_TYPE "(sddddiidda(uiiii))"
+
+static GVariant *
+gsk_text_node_serialize (GskRenderNode *node)
+{
+  GskTextNode *self = (GskTextNode *) node;
+  GVariant *v;
+  GVariantBuilder builder;
+  int i;
+  PangoFontDescription *desc;
+  char *s;
+
+  desc = pango_font_describe (self->font);
+  s = pango_font_description_to_string (desc);
+  for (i = 0; i < self->glyphs->num_glyphs; i++)
+    {
+      PangoGlyphInfo *glyph = &self->glyphs->glyphs[i];
+      g_variant_builder_add (&builder, "(uiiii)",
+                             glyph->glyph,
+                             glyph->geometry.width,
+                             glyph->geometry.x_offset,
+                             glyph->geometry.y_offset,
+                             glyph->attr.is_cluster_start);
+    }
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uiiii)"));
+  v = g_variant_new (GSK_TEXT_NODE_VARIANT_TYPE,
+                     s,
+                     self->color.red,
+                     self->color.green,
+                     self->color.blue,
+                     self->color.alpha,
+                     self->x_offset,
+                     self->y_offset,
+                     self->base_x,
+                     self->base_y,
+                     &builder);
+
+  g_free (s);
+  pango_font_description_free (desc);
+
+  return v;
+}
+
+static GskRenderNode *
+gsk_text_node_deserialize (GVariant  *variant,
+                           GError   **error)
+{
+  PangoFont *font;
+  PangoGlyphString *glyphs;
+  GVariantIter iter;
+  GskRenderNode *result;
+  PangoGlyphInfo glyph;
+  PangoFontDescription *desc;
+  PangoFontMap *fontmap;
+  PangoContext *context;
+  int cluster_start;
+  char *s;
+  GdkRGBA color;
+  int x_offset, y_offset;
+  double base_x, base_y;
+  int i;
+
+  if (!check_variant_type (variant, GSK_TEXT_NODE_VARIANT_TYPE, error))
+    return NULL;
+
+  g_variant_get (variant, "(&sddddiidda(uiiii))",
+                 &color.red, &color.green, &color.blue, &color.alpha,
+                 &x_offset, &y_offset,
+                 &base_x, &base_y,
+                 &s, &iter);
+
+  desc = pango_font_description_from_string (s);
+  fontmap = pango_cairo_font_map_get_default ();
+  context = pango_font_map_create_context (fontmap);
+  font = pango_font_map_load_font (fontmap, context, desc);
+
+  glyphs = pango_glyph_string_new ();
+  pango_glyph_string_set_size (glyphs, g_variant_iter_n_children (&iter));
+  i = 0;
+  while (g_variant_iter_next (&iter, "(uiiii)", &glyph.glyph, &glyph.geometry.width, 
&glyph.geometry.x_offset, &glyph.geometry.y_offset, &cluster_start))
+    {
+      glyph.attr.is_cluster_start = cluster_start;
+      glyphs->glyphs[i] = glyph;
+      i++;
+    }
+
+  result = gsk_text_node_new (font, glyphs, &color, /* FIXME: Avoid copying glyphs */
+                              x_offset, y_offset,
+                              base_x, base_y);
+
+  pango_glyph_string_free (glyphs);
+  pango_font_description_free (desc);
+  g_object_unref (context);
+  g_object_unref (font);
+
+  return result;
+}
+
+static const GskRenderNodeClass GSK_TEXT_NODE_CLASS = {
+  GSK_TEXT_NODE,
+  sizeof (GskTextNode),
+  "GskTextNode",
+  gsk_text_node_finalize,
+  gsk_text_node_draw,
+  gsk_text_node_serialize,
+  gsk_text_node_deserialize
+};
+
+GskRenderNode *
+gsk_text_node_new (PangoFont        *font,
+                   PangoGlyphString *glyphs,
+                   const GdkRGBA    *color,
+                   int               x_offset,
+                   int               y_offset,
+                   double            base_x,
+                   double            base_y)
+{
+  GskTextNode *self;
+  PangoRectangle ink_rect;
+
+  self = (GskTextNode *) gsk_render_node_new (&GSK_TEXT_NODE_CLASS, 0);
+
+  self->font = g_object_ref (font);
+  self->glyphs = pango_glyph_string_copy (glyphs);
+  self->color = *color;
+  self->x_offset = x_offset;
+  self->y_offset = y_offset;
+  self->base_x = base_x;
+  self->base_y = base_y;
+
+  pango_glyph_string_extents (glyphs, font, &ink_rect, NULL);
+  pango_extents_to_pixels (&ink_rect, NULL);
+
+  graphene_rect_init (&self->render_node.bounds,
+                      x_offset + base_x + ink_rect.x,
+                      y_offset + base_y + ink_rect.y,
+                      ink_rect.width,
+                      ink_rect.height);
+
+  return &self->render_node;
+}
+
 static const GskRenderNodeClass *klasses[] = {
   [GSK_CONTAINER_NODE] = &GSK_CONTAINER_NODE_CLASS,
   [GSK_CAIRO_NODE] = &GSK_CAIRO_NODE_CLASS,
@@ -3818,7 +4060,8 @@ static const GskRenderNodeClass *klasses[] = {
   [GSK_ROUNDED_CLIP_NODE] = &GSK_ROUNDED_CLIP_NODE_CLASS,
   [GSK_SHADOW_NODE] = &GSK_SHADOW_NODE_CLASS,
   [GSK_BLEND_NODE] = &GSK_BLEND_NODE_CLASS,
-  [GSK_CROSS_FADE_NODE] = &GSK_CROSS_FADE_NODE_CLASS
+  [GSK_CROSS_FADE_NODE] = &GSK_CROSS_FADE_NODE_CLASS,
+  [GSK_TEXT_NODE] = &GSK_TEXT_NODE_CLASS
 };
 
 GskRenderNode *
diff --git a/gtk/inspector/gtktreemodelrendernode.c b/gtk/inspector/gtktreemodelrendernode.c
index 63e3001..1697aaf 100644
--- a/gtk/inspector/gtktreemodelrendernode.c
+++ b/gtk/inspector/gtktreemodelrendernode.c
@@ -523,6 +523,7 @@ append_node (GtkTreeModelRenderNode *nodemodel,
       break;
 
     case GSK_CAIRO_NODE:
+    case GSK_TEXT_NODE:
     case GSK_TEXTURE_NODE:
     case GSK_COLOR_NODE:
     case GSK_LINEAR_GRADIENT_NODE:
diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c
index 587c72e..e422b9e 100644
--- a/gtk/inspector/recorder.c
+++ b/gtk/inspector/recorder.c
@@ -181,6 +181,8 @@ node_type_name (GskRenderNodeType type)
       return "Blend";
     case GSK_CROSS_FADE_NODE:
       return "CrossFade";
+    case GSK_TEXT_NODE:
+      return "Text";
     }
 }
 


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