[pango/pango2: 162/168] Bring back shape attributes




commit 4d24c714cee854e8add40f0b9f3f00e74ec0396d
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Jun 20 06:29:30 2022 -0700

    Bring back shape attributes
    
    It turns out that using user fonts for
    embedding is a bit too involved, so lets
    keep the shape attribute machinery around.

 pango/pango-attr-list.c          |   1 +
 pango/pango-attr.c               |  93 ++++++++++++++++++++--------------
 pango/pango-attributes-private.h |   9 ++++
 pango/pango-attributes.c         |  44 ++++++++++++++++
 pango/pango-attributes.h         |   8 ++-
 pango/pango-item-private.h       |   1 +
 pango/pango-item.c               |   5 ++
 pango/pango-line-breaker.c       |  37 ++++++++++++--
 pango/pango-renderer.c           | 105 +++++++++++++++++++++++++++++++++++----
 pango/pango-renderer.h           |  12 +++--
 pango/pango-run.c                |  57 ++++++++++++++++++++-
 pango/serializer.c               |   1 +
 12 files changed, 315 insertions(+), 58 deletions(-)
---
diff --git a/pango/pango-attr-list.c b/pango/pango-attr-list.c
index 0cf381b1..3f5c00f8 100644
--- a/pango/pango-attr-list.c
+++ b/pango/pango-attr-list.c
@@ -1253,6 +1253,7 @@ pango_attr_list_from_string (const char *text)
           INT_ATTR(line_spacing, int);
           break;
 
+        case PANGO_ATTR_SHAPE:
         default:
           g_assert_not_reached ();
         }
diff --git a/pango/pango-attr.c b/pango/pango-attr.c
index 9640e009..211d8232 100644
--- a/pango/pango-attr.c
+++ b/pango/pango-attr.c
@@ -23,6 +23,7 @@
 #include <string.h>
 
 #include "pango-attr-private.h"
+#include "pango-attributes-private.h"
 #include "pango-impl-utils.h"
 
 
@@ -98,6 +99,7 @@ is_valid_attr_type (guint type)
     case PANGO_ATTR_SENTENCE:
     case PANGO_ATTR_BASELINE_SHIFT:
     case PANGO_ATTR_FONT_SCALE:
+    case PANGO_ATTR_SHAPE:
       return TRUE;
     default:
       if (!attr_type)
@@ -265,28 +267,39 @@ pango_attribute_copy (const PangoAttribute *attr)
       break;
 
     case PANGO_ATTR_VALUE_POINTER:
-      {
-        PangoAttrDataCopyFunc copy = NULL;
+      if (attr->type == PANGO_ATTR_SHAPE)
+        {
+          ShapeData *shape_data;
 
-        G_LOCK (attr_type);
+          shape_data = g_memdup2 (attr->pointer_value, sizeof (ShapeData));
+          if (shape_data->copy)
+            shape_data->data = shape_data->copy (shape_data->data);
 
-        g_assert (attr_type != NULL);
+          result->pointer_value = shape_data;
+        }
+      else
+        {
+          PangoAttrDataCopyFunc copy = NULL;
 
-        for (int i = 0; i < attr_type->len; i++)
-          {
-            PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i);
-            if (class->type == attr->type)
-              {
-                copy = class->copy;
-                break;
-              }
-          }
+          G_LOCK (attr_type);
 
-        G_UNLOCK (attr_type);
+          g_assert (attr_type != NULL);
 
-        if (copy)
-          result->pointer_value = copy (attr->pointer_value);
-      }
+          for (int i = 0; i < attr_type->len; i++)
+            {
+              PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i);
+              if (class->type == attr->type)
+                {
+                  copy = class->copy;
+                  break;
+                }
+            }
+
+          G_UNLOCK (attr_type);
+
+          if (copy)
+            result->pointer_value = copy (attr->pointer_value);
+        }
       break;
 
     case PANGO_ATTR_VALUE_INT:
@@ -322,28 +335,38 @@ pango_attribute_destroy (PangoAttribute *attr)
       break;
 
     case PANGO_ATTR_VALUE_POINTER:
-      {
-        GDestroyNotify destroy = NULL;
+      if (attr->type == PANGO_ATTR_SHAPE)
+        {
+          ShapeData *shape_data = attr->pointer_value;
 
-        G_LOCK (attr_type);
+          if (shape_data->destroy)
+            shape_data->destroy (shape_data->data);
 
-        g_assert (attr_type != NULL);
+          g_free (shape_data);
+        }
+      else
+        {
+          GDestroyNotify destroy = NULL;
 
-        for (int i = 0; i < attr_type->len; i++)
-          {
-            PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i);
-            if (class->type == attr->type)
-              {
-                destroy = class->destroy;
-                break;
-              }
-          }
+          G_LOCK (attr_type);
 
-        G_UNLOCK (attr_type);
+          g_assert (attr_type != NULL);
 
-        if (destroy)
-          destroy (attr->pointer_value);
-      }
+          for (int i = 0; i < attr_type->len; i++)
+            {
+              PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i);
+              if (class->type == attr->type)
+                {
+                  destroy = class->destroy;
+                  break;
+                }
+            }
+
+          G_UNLOCK (attr_type);
+
+          if (destroy)
+            destroy (attr->pointer_value);
+        }
       break;
 
     case PANGO_ATTR_VALUE_INT:
@@ -427,8 +450,6 @@ pango_attribute_equal (const PangoAttribute *attr1,
 
         G_UNLOCK (attr_type);
 
-        g_assert (equal != NULL);
-
         if (equal)
           return equal (attr1->pointer_value, attr2->pointer_value);
 
diff --git a/pango/pango-attributes-private.h b/pango/pango-attributes-private.h
index 3ff30529..181fe98d 100644
--- a/pango/pango-attributes-private.h
+++ b/pango/pango-attributes-private.h
@@ -25,3 +25,12 @@ gboolean pango_attribute_affects_itemization    (PangoAttribute *attr,
                                                  gpointer        data);
 gboolean pango_attribute_affects_break_or_shape (PangoAttribute *attr,
                                                  gpointer        data);
+
+typedef struct {
+  PangoRectangle ink_rect;
+  PangoRectangle logical_rect;
+  gpointer data;
+  PangoAttrDataCopyFunc copy;
+  GDestroyNotify destroy;
+} ShapeData;
+
diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c
index 253feca9..c1dc26d2 100644
--- a/pango/pango-attributes.c
+++ b/pango/pango-attributes.c
@@ -799,6 +799,50 @@ pango_attr_text_transform_new (PangoTextTransform transform)
   return pango_attr_int_new (PANGO_ATTR_TEXT_TRANSFORM, transform);
 }
 
+/**
+ * pango_attr_shape_new:
+ * @ink_rect: ink rectangle to use for each character
+ * @logical_rect: logical rectangle to use for each character
+ * @data: user data
+ * @copy: (nullable): function to copy @data when the attribute
+ *   is copied
+ * @destroy: (nullable): function to free @data when the attribute
+ *   is freed
+ *
+ * Creates a new shape attribute.
+ *
+ * Shape attributes override the extents for a glyph and can
+ * trigger custom rendering in a `PangoRenderer`. This might
+ * be used, for instance, for embedding a picture or a widget
+ * inside a `PangoLayout`.
+ *
+ * Return value: (transfer full): the newly allocated
+ *   `PangoAttribute`, which should be freed with
+ *   [method@Pango.Attribute.destroy]
+ */
+PangoAttribute *
+pango_attr_shape_new (PangoRectangle        *ink_rect,
+                      PangoRectangle        *logical_rect,
+                      gpointer               data,
+                      PangoAttrDataCopyFunc  copy,
+                      GDestroyNotify         destroy)
+{
+  PangoAttribute *attr;
+  ShapeData *shape_data;
+
+  shape_data = g_new0 (ShapeData, 1);
+  shape_data->ink_rect = *ink_rect;
+  shape_data->logical_rect = *logical_rect;
+  shape_data->data = data;
+  shape_data->copy = copy;
+  shape_data->destroy = destroy;
+
+  attr = pango_attr_init (PANGO_ATTR_SHAPE);
+  attr->pointer_value = shape_data;
+
+  return attr;
+}
+
 /* }}} */
 /* {{{ Private API */
 
diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h
index a92350a8..ffb3d07f 100644
--- a/pango/pango-attributes.h
+++ b/pango/pango-attributes.h
@@ -118,11 +118,11 @@ typedef enum
   PANGO_ATTR_BASELINE_SHIFT       = PANGO_ATTR_TYPE (INT, ITEMIZATION, ACCUMULATES),
   PANGO_ATTR_FONT_SCALE           = PANGO_ATTR_TYPE (INT, ITEMIZATION, ACCUMULATES),
   PANGO_ATTR_LINE_SPACING         = PANGO_ATTR_TYPE (INT, ITEMIZATION, OVERRIDES),
+  PANGO_ATTR_SHAPE                = PANGO_ATTR_TYPE (POINTER, ITEMIZATION, OVERRIDES),
 } PangoAttrType;
 
 #undef PANGO_ATTR_TYPE
 
-
 PANGO_AVAILABLE_IN_ALL
 PangoAttribute *        pango_attr_language_new                 (PangoLanguage              *language);
 PANGO_AVAILABLE_IN_ALL
@@ -301,5 +301,11 @@ typedef enum {
 PANGO_AVAILABLE_IN_ALL
 PangoAttribute *        pango_attr_text_transform_new           (PangoTextTransform transform);
 
+PANGO_AVAILABLE_IN_ALL
+PangoAttribute *        pango_attr_shape_new                    (PangoRectangle        *ink_rect,
+                                                                 PangoRectangle        *logical_rect,
+                                                                 gpointer               data,
+                                                                 PangoAttrDataCopyFunc  copy,
+                                                                 GDestroyNotify         destroy);
 
 G_END_DECLS
diff --git a/pango/pango-item-private.h b/pango/pango-item-private.h
index 541eb017..3c8b18d7 100644
--- a/pango/pango-item-private.h
+++ b/pango/pango-item-private.h
@@ -116,6 +116,7 @@ struct _ItemProperties
   int line_spacing;
   int absolute_line_height;
   double line_height;
+  PangoAttribute *shape;
 };
 
 void               pango_item_get_properties          (PangoItem        *item,
diff --git a/pango/pango-item.c b/pango/pango-item.c
index 079f5796..952cab05 100644
--- a/pango/pango-item.c
+++ b/pango/pango-item.c
@@ -401,6 +401,7 @@ pango_item_get_properties (PangoItem      *item,
   properties->line_height = 0.0;
   properties->absolute_line_height = 0;
   properties->line_spacing = 0;
+  properties->shape = NULL;
 
   while (tmp_list)
     {
@@ -448,6 +449,10 @@ pango_item_get_properties (PangoItem      *item,
           properties->no_paragraph_break = TRUE;
           break;
 
+        case PANGO_ATTR_SHAPE:
+          properties->shape = attr;
+          break;
+
         default:
           break;
         }
diff --git a/pango/pango-line-breaker.c b/pango/pango-line-breaker.c
index 55eb9f4f..6fe52dc3 100644
--- a/pango/pango-line-breaker.c
+++ b/pango/pango-line-breaker.c
@@ -849,6 +849,28 @@ distribute_letter_spacing (int  letter_spacing,
   *space_right = letter_spacing - *space_left;
 }
 
+static void
+pango_shape_shape (const char       *text,
+                   unsigned int      n_chars,
+                   ShapeData        *shape,
+                   PangoGlyphString *glyphs)
+{
+  unsigned int i;
+  const char *p;
+
+  pango_glyph_string_set_size (glyphs, n_chars);
+
+  for (i = 0, p = text; i < n_chars; i++, p = g_utf8_next_char (p))
+    {
+      glyphs->glyphs[i].glyph = PANGO_GLYPH_EMPTY;
+      glyphs->glyphs[i].geometry.x_offset = 0;
+      glyphs->glyphs[i].geometry.y_offset = 0;
+      glyphs->glyphs[i].geometry.width = shape->logical_rect.width;
+      glyphs->glyphs[i].attr.is_cluster_start = 1;
+
+      glyphs->log_clusters[i] = p - text;
+    }
+}
 
 static PangoGlyphString *
 shape_run (PangoLineBreaker *self,
@@ -866,11 +888,16 @@ shape_run (PangoLineBreaker *self,
       if (pango_context_get_round_glyph_positions (self->context))
         shape_flags |= PANGO_SHAPE_ROUND_POSITIONS;
 
-      pango_shape_item (item,
-                        self->data->text, self->data->length,
-                        self->data->log_attrs + self->start_offset,
-                        glyphs,
-                        shape_flags);
+      if (self->properties.shape)
+        pango_shape_shape (self->data->text + item->offset, item->num_chars,
+                           (ShapeData *)self->properties.shape->pointer_value,
+                           glyphs);
+      else
+        pango_shape_item (item,
+                          self->data->text, self->data->length,
+                          self->data->log_attrs + self->start_offset,
+                          glyphs,
+                          shape_flags);
 
       if (self->properties.letter_spacing)
         {
diff --git a/pango/pango-renderer.c b/pango/pango-renderer.c
index 5c94be74..adaeb70f 100644
--- a/pango/pango-renderer.c
+++ b/pango/pango-renderer.c
@@ -27,6 +27,7 @@
 #include "pango-layout.h"
 #include "pango-run-private.h"
 #include "pango-line-private.h"
+#include "pango-attributes-private.h"
 
 #define N_RENDER_PARTS 5
 
@@ -505,6 +506,34 @@ static void pango_renderer_draw_runs (PangoRenderer *renderer,
                                       int            x,
                                       int            y);
 
+static void
+draw_shaped_glyphs (PangoRenderer    *renderer,
+                    PangoGlyphString *glyphs,
+                    PangoAttribute   *attr,
+                    int               x,
+                    int               y)
+{
+  PangoRendererClass *class = PANGO_RENDERER_GET_CLASS (renderer);
+  int i;
+
+  if (!class->draw_shape)
+    return;
+
+  for (i = 0; i < glyphs->num_glyphs; i++)
+    {
+      PangoGlyphInfo *gi = &glyphs->glyphs[i];
+      ShapeData *data = (ShapeData *)attr->pointer_value;
+
+      class->draw_shape (renderer,
+                         &data->ink_rect,
+                         &data->logical_rect,
+                         data->data,
+                         x, y);
+
+      x += gi->geometry.width;
+    }
+}
+
 /**
  * pango_renderer_draw_line:
  * @renderer: a `PangoRenderer`
@@ -598,6 +627,41 @@ pango_renderer_draw_lines (PangoRenderer *renderer,
   renderer->priv->lines = NULL;
 }
 
+static void
+pango_shape_get_extents (int               n_chars,
+                         PangoRectangle   *shape_ink,
+                         PangoRectangle   *shape_logical,
+                         PangoRectangle   *ink_rect,
+                         PangoRectangle   *logical_rect)
+{
+  if (n_chars > 0)
+    {
+      ink_rect->x = MIN (shape_ink->x, shape_ink->x + shape_logical->width * (n_chars - 1));
+      ink_rect->width = MAX (shape_ink->width, shape_ink->width + shape_logical->width * (n_chars - 1));
+      ink_rect->y = shape_ink->y;
+      ink_rect->height = shape_ink->height;
+
+      logical_rect->x = MIN (shape_logical->x, shape_logical->x + shape_logical->width * (n_chars - 1));
+      logical_rect->width = MAX (shape_logical->width, shape_logical->width + shape_logical->width * 
(n_chars - 1));
+      logical_rect->y = shape_logical->y;
+      logical_rect->height = shape_logical->height;
+    }
+  else
+    {
+      ink_rect->x = 0;
+      ink_rect->y = 0;
+      ink_rect->width = 0;
+      ink_rect->height = 0;
+
+      logical_rect->x = 0;
+      logical_rect->y = 0;
+      logical_rect->width = 0;
+      logical_rect->height = 0;
+    }
+}
+
+
+
 static void
 pango_renderer_draw_runs (PangoRenderer *renderer,
                           PangoLine     *line,
@@ -621,6 +685,7 @@ pango_renderer_draw_runs (PangoRenderer *renderer,
       PangoGlyphString *glyphs = glyph_item->glyphs;
       PangoRectangle ink_rect, *ink = NULL;
       PangoRectangle logical_rect, *logical = NULL;
+      ItemProperties properties;
       int y_off;
 
       if (glyph_item->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE)
@@ -628,19 +693,38 @@ pango_renderer_draw_runs (PangoRenderer *renderer,
 
       pango_renderer_prepare_run (renderer, run);
 
-      if (renderer->underline != PANGO_LINE_STYLE_NONE ||
-          renderer->overline != PANGO_LINE_STYLE_NONE ||
-          renderer->strikethrough != PANGO_LINE_STYLE_NONE)
+      pango_item_get_properties (item, &properties);
+
+      if (properties.shape)
         {
+          ShapeData *data = (ShapeData *)properties.shape->pointer_value;
+
           ink = &ink_rect;
           logical = &logical_rect;
+          pango_shape_get_extents (glyphs->num_glyphs,
+                                   &data->ink_rect,
+                                   &data->logical_rect,
+                                   ink,
+                                   logical);
+
+          glyph_string_width = logical->width;
         }
-      if (G_UNLIKELY (ink || logical))
-        pango_glyph_string_extents (glyphs, item->analysis.font, ink, logical);
-      if (logical)
-        glyph_string_width = logical_rect.width;
       else
-        glyph_string_width = pango_glyph_string_get_width (glyphs);
+        {
+          if (renderer->underline != PANGO_LINE_STYLE_NONE ||
+              renderer->overline != PANGO_LINE_STYLE_NONE ||
+              renderer->strikethrough != PANGO_LINE_STYLE_NONE)
+            {
+              ink = &ink_rect;
+              logical = &logical_rect;
+            }
+          if (G_UNLIKELY (ink || logical))
+            pango_glyph_string_extents (glyphs, item->analysis.font, ink, logical);
+          if (logical)
+            glyph_string_width = logical_rect.width;
+          else
+            glyph_string_width = pango_glyph_string_get_width (glyphs);
+        }
 
       renderer->priv->line_state->logical_rect_end = x + x_off + glyph_string_width;
 
@@ -674,7 +758,10 @@ pango_renderer_draw_runs (PangoRenderer *renderer,
                                          overall_rect.height);
         }
 
-      pango_renderer_draw_glyph_item (renderer, text, glyph_item, x + x_off, y - y_off);
+      if (properties.shape)
+        draw_shaped_glyphs (renderer, glyphs, properties.shape, x + x_off, y - y_off);
+      else
+        pango_renderer_draw_glyph_item (renderer, text, glyph_item, x + x_off, y - y_off);
 
       if (renderer->underline != PANGO_LINE_STYLE_NONE ||
           renderer->overline != PANGO_LINE_STYLE_NONE ||
diff --git a/pango/pango-renderer.h b/pango/pango-renderer.h
index 954ffea8..ab70f183 100644
--- a/pango/pango-renderer.h
+++ b/pango/pango-renderer.h
@@ -127,7 +127,6 @@ struct _PangoRendererClass
   /*< private >*/
   GObjectClass parent_class;
 
-  /* vtable - not signals */
   /*< public >*/
 
   void (*draw_glyphs)          (PangoRenderer    *renderer,
@@ -146,7 +145,12 @@ struct _PangoRendererClass
                                 int               y,
                                 int               width,
                                 int               height);
-
+  void (*draw_shape)           (PangoRenderer    *renderer,
+                                PangoRectangle   *ink_rect,
+                                PangoRectangle   *logical_rect,
+                                gpointer          data,
+                                int               x,
+                                int               y);
   void (*draw_trapezoid)       (PangoRenderer    *renderer,
                                 PangoRenderPart   part,
                                 double            y1_,
@@ -179,9 +183,7 @@ struct _PangoRendererClass
   /*< private >*/
 
   /* Padding for future expansion */
-  void (*_pango_reserved2) (void);
-  void (*_pango_reserved3) (void);
-  void (*_pango_reserved4) (void);
+  gpointer _pango_reserved[8];
 };
 
 PANGO_AVAILABLE_IN_ALL
diff --git a/pango/pango-run.c b/pango/pango-run.c
index 23f495aa..d3138cb6 100644
--- a/pango/pango-run.c
+++ b/pango/pango-run.c
@@ -4,6 +4,7 @@
 #include "pango-item-private.h"
 #include "pango-impl-utils.h"
 #include "pango-font-metrics-private.h"
+#include "pango-attributes-private.h"
 
 #include <math.h>
 
@@ -48,6 +49,54 @@ pango_run_get_glyphs (PangoRun *run)
   return run->glyph_item.glyphs;
 }
 
+static void
+pango_shape_get_extents (int             n_chars,
+                         PangoAttribute *attr,
+                         PangoRectangle *ink_rect,
+                         PangoRectangle *logical_rect)
+{
+  if (n_chars > 0)
+    {
+      ShapeData *data = (ShapeData *)attr->pointer_value;
+      PangoRectangle *shape_ink = &data->ink_rect;
+      PangoRectangle *shape_logical = &data->logical_rect;
+
+      if (ink_rect)
+        {
+          ink_rect->x = MIN (shape_ink->x, shape_ink->x + shape_logical->width * (n_chars - 1));
+          ink_rect->width = MAX (shape_ink->width, shape_ink->width + shape_logical->width * (n_chars - 1));
+          ink_rect->y = shape_ink->y;
+          ink_rect->height = shape_ink->height;
+        }
+
+      if (logical_rect)
+        {
+          logical_rect->x = MIN (shape_logical->x, shape_logical->x + shape_logical->width * (n_chars - 1));
+          logical_rect->width = MAX (shape_logical->width, shape_logical->width + shape_logical->width * 
(n_chars - 1));
+          logical_rect->y = shape_logical->y;
+          logical_rect->height = shape_logical->height;
+        }
+    }
+  else
+    {
+      if (ink_rect)
+        {
+          ink_rect->x = 0;
+          ink_rect->y = 0;
+          ink_rect->width = 0;
+          ink_rect->height = 0;
+        }
+
+      if (logical_rect)
+        {
+          logical_rect->x = 0;
+          logical_rect->y = 0;
+          logical_rect->width = 0;
+          logical_rect->height = 0;
+        }
+    }
+}
+
 /**
  * pango_run_get_extents:
  * @run: a `PangoRun`
@@ -91,8 +140,12 @@ pango_run_get_extents (PangoRun         *run,
   if (!logical_rect && (has_underline || has_overline || has_strikethrough))
     logical_rect = &logical;
 
-  pango_glyph_string_extents (glyph_item->glyphs, glyph_item->item->analysis.font,
-                              ink_rect, logical_rect);
+  if (properties.shape)
+    pango_shape_get_extents (glyph_item->item->num_chars, properties.shape,
+                             ink_rect, logical_rect);
+  else
+    pango_glyph_string_extents (glyph_item->glyphs, glyph_item->item->analysis.font,
+                                ink_rect, logical_rect);
 
   if (ink_rect && (has_underline || has_overline || has_strikethrough))
     {
diff --git a/pango/serializer.c b/pango/serializer.c
index 6298f48c..f9941faa 100644
--- a/pango/serializer.c
+++ b/pango/serializer.c
@@ -936,6 +936,7 @@ attr_for_type (GtkJsonParser *parser,
 
   switch (type)
     {
+    case PANGO_ATTR_SHAPE:
     default:
       g_assert_not_reached ();
 


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