[gtk/wip/chergert/glproto: 283/493] add nine slicing helpers




commit e58d285095d05338758d5e24528fcf66887b15d2
Author: Christian Hergert <chergert redhat com>
Date:   Thu Jan 28 15:51:19 2021 -0800

    add nine slicing helpers
    
     - break it out into a single header with inlines
     - interlace texture rect and translated areas (0..1)
     - store nine-slice info with the texture itself

 gsk/next/gskgltexturepool.c        |  22 +++
 gsk/next/gskgltexturepoolprivate.h |  66 +++++----
 gsk/next/gskgltypesprivate.h       |   1 +
 gsk/next/ninesliceprivate.h        | 284 +++++++++++++++++++++++++++++++++++++
 4 files changed, 346 insertions(+), 27 deletions(-)
---
diff --git a/gsk/next/gskgltexturepool.c b/gsk/next/gskgltexturepool.c
index 18654a7a00..bc1ff47b0c 100644
--- a/gsk/next/gskgltexturepool.c
+++ b/gsk/next/gskgltexturepool.c
@@ -25,6 +25,7 @@
 #include <gdk/gdktextureprivate.h>
 
 #include "gskgltexturepoolprivate.h"
+#include "ninesliceprivate.h"
 
 void
 gsk_gl_texture_free (GskGLTexture *texture)
@@ -52,6 +53,7 @@ gsk_gl_texture_free (GskGLTexture *texture)
         }
 
       g_clear_pointer (&texture->slices, g_free);
+      g_clear_pointer (&texture->nine_slice, g_free);
 
       g_slice_free (GskGLTexture, texture);
     }
@@ -242,3 +244,23 @@ gsk_gl_texture_new (guint  texture_id,
 
   return texture;
 }
+
+const GskGLTextureNineSlice *
+gsk_gl_texture_get_nine_slice (GskGLTexture         *texture,
+                               const GskRoundedRect *outline,
+                               float                 extra_pixels)
+{
+  g_return_val_if_fail (texture != NULL, NULL);
+  g_return_val_if_fail (outline != NULL, NULL);
+
+  if G_UNLIKELY (texture->nine_slice == NULL)
+    {
+      texture->nine_slice = g_new0 (GskGLTextureNineSlice, 9);
+
+      nine_slice_rounded_rect (texture->nine_slice, outline);
+      nine_slice_grow (texture->nine_slice, extra_pixels);
+      nine_slice_to_texture_coords (texture->nine_slice, texture->width, texture->height);
+    }
+
+  return texture->nine_slice;
+}
diff --git a/gsk/next/gskgltexturepoolprivate.h b/gsk/next/gskgltexturepoolprivate.h
index 8c000b5ee8..68af88f44c 100644
--- a/gsk/next/gskgltexturepoolprivate.h
+++ b/gsk/next/gskgltexturepoolprivate.h
@@ -37,51 +37,63 @@ struct _GskGLTextureSlice
   guint texture_id;
 };
 
+struct _GskGLTextureNineSlice
+{
+  cairo_rectangle_int_t rect;
+  graphene_rect_t area;
+};
+
 struct _GskGLTexture
 {
   /* Used to sort by width/height in pool */
-  GList              width_link;
-  GList              height_link;
+  GList width_link;
+  GList height_link;
 
   /* Identifier of the frame that created it */
-  gint64             last_used_in_frame;
+  gint64 last_used_in_frame;
 
   /* Backpointer to texture (can be cleared asynchronously) */
-  GdkTexture        *user;
+  GdkTexture *user;
 
   /* Only used by sliced textures */
   GskGLTextureSlice *slices;
-  guint              n_slices;
+  guint n_slices;
+
+  /* Only used by nine-slice textures */
+  GskGLTextureNineSlice *nine_slice;
 
   /* The actual GL texture identifier in some shared context */
-  guint              texture_id;
+  guint texture_id;
 
-  float              width;
-  float              height;
-  int                min_filter;
-  int                mag_filter;
+  float width;
+  float height;
+  int min_filter;
+  int mag_filter;
 
   /* Set when used by an atlas so we don't drop the texture */
   guint              permanent : 1;
 };
 
-void          gsk_gl_texture_pool_init  (GskGLTexturePool *self);
-void          gsk_gl_texture_pool_clear (GskGLTexturePool *self);
-GskGLTexture *gsk_gl_texture_pool_get   (GskGLTexturePool *self,
-                                         float             width,
-                                         float             height,
-                                         int               min_filter,
-                                         int               mag_filter,
-                                         gboolean          always_create);
-void          gsk_gl_texture_pool_put   (GskGLTexturePool *self,
-                                         GskGLTexture     *texture);
-GskGLTexture *gsk_gl_texture_new        (guint             texture_id,
-                                         int               width,
-                                         int               height,
-                                         int               min_filter,
-                                         int               mag_filter,
-                                         gint64            frame_id);
-void          gsk_gl_texture_free       (GskGLTexture     *texture);
+void                         gsk_gl_texture_pool_init      (GskGLTexturePool *self);
+void                         gsk_gl_texture_pool_clear     (GskGLTexturePool *self);
+GskGLTexture                *gsk_gl_texture_pool_get       (GskGLTexturePool *self,
+                                                            float             width,
+                                                            float             height,
+                                                            int               min_filter,
+                                                            int               mag_filter,
+                                                            gboolean          always_create);
+void                         gsk_gl_texture_pool_put       (GskGLTexturePool *self,
+                                                            GskGLTexture     *texture);
+GskGLTexture                *gsk_gl_texture_new            (guint             texture_id,
+                                                            int               width,
+                                                            int               height,
+                                                            int               min_filter,
+                                                            int               mag_filter,
+                                                            gint64            frame_id);
+const GskGLTextureNineSlice *gsk_gl_texture_get_nine_slice (GskGLTexture         *texture,
+                                                            const GskRoundedRect *outline,
+                                                            float                 extra_pixels);
+void                         gsk_gl_texture_free           (GskGLTexture     *texture);
 
 G_END_DECLS
 
diff --git a/gsk/next/gskgltypesprivate.h b/gsk/next/gskgltypesprivate.h
index 5927d780fc..d8b22f7019 100644
--- a/gsk/next/gskgltypesprivate.h
+++ b/gsk/next/gskgltypesprivate.h
@@ -45,6 +45,7 @@ typedef struct _GskGLTexture GskGLTexture;
 typedef struct _GskGLTextureSlice GskGLTextureSlice;
 typedef struct _GskGLTextureAtlas GskGLTextureAtlas;
 typedef struct _GskGLTextureLibrary GskGLTextureLibrary;
+typedef struct _GskGLTextureNineSlice GskGLTextureNineSlice;
 typedef struct _GskGLUniformState GskGLUniformState;
 typedef struct _GskNextDriver GskNextDriver;
 
diff --git a/gsk/next/ninesliceprivate.h b/gsk/next/ninesliceprivate.h
new file mode 100644
index 0000000000..02d250ec4e
--- /dev/null
+++ b/gsk/next/ninesliceprivate.h
@@ -0,0 +1,284 @@
+/* ninesliceprivate.h
+ *
+ * Copyright 2017 Timm Bäder <mail baedert org>
+ * Copyright 2021 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __NINE_SLICE_PRIVATE_H__
+#define __NINE_SLICE_PRIVATE_H__
+
+#include "gskgltexturepoolprivate.h"
+
+G_BEGIN_DECLS
+
+enum {
+  NINE_SLICE_TOP_LEFT      = 0,
+  NINE_SLICE_TOP_CENTER    = 1,
+  NINE_SLICE_TOP_RIGHT     = 2,
+  NINE_SLICE_LEFT_CENTER   = 3,
+  NINE_SLICE_CENTER        = 4,
+  NINE_SLICE_RIGHT_CENTER  = 5,
+  NINE_SLICE_BOTTOM_LEFT   = 6,
+  NINE_SLICE_BOTTOM_CENTER = 7,
+  NINE_SLICE_BOTTOM_RIGHT  = 8,
+};
+
+static inline bool G_GNUC_PURE
+slice_is_visible (const cairo_rectangle_int_t *r)
+{
+  return (r->width > 0 && r->height > 0);
+}
+
+static inline void
+nine_slice_rounded_rect (GskGLTextureNineSlice *slices,
+                         const GskRoundedRect  *rect)
+{
+  const graphene_point_t *origin = &rect->bounds.origin;
+  const graphene_size_t *size = &rect->bounds.size;
+  int top_height = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].height,
+                               rect->corner[GSK_CORNER_TOP_RIGHT].height));
+  int bottom_height = ceilf (MAX (rect->corner[GSK_CORNER_BOTTOM_LEFT].height,
+                                  rect->corner[GSK_CORNER_BOTTOM_RIGHT].height));
+  int right_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_RIGHT].width,
+                                rect->corner[GSK_CORNER_BOTTOM_RIGHT].width));
+  int left_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].width,
+                               rect->corner[GSK_CORNER_BOTTOM_LEFT].width));
+
+  /* Top left */
+  slices[0].rect.x = origin->x;
+  slices[0].rect.y = origin->y;
+  slices[0].rect.width = left_width;
+  slices[0].rect.height = top_height;
+
+  /* Top center */
+  slices[1].rect.x = origin->x + size->width / 2.0 - 0.5;
+  slices[1].rect.y = origin->y;
+  slices[1].rect.width = 1;
+  slices[1].rect.height = top_height;
+
+  /* Top right */
+  slices[2].rect.x = origin->x + size->width - right_width;
+  slices[2].rect.y = origin->y;
+  slices[2].rect.width = right_width;
+  slices[2].rect.height = top_height;
+
+  /* Left center */
+  slices[3].rect.x = origin->x;
+  slices[3].rect.y = origin->y + size->height / 2.0;
+  slices[3].rect.width = left_width;
+  slices[3].rect.height = 1;
+
+  /* center */
+  slices[4].rect.x = origin->x + size->width / 2.0 - 0.5;
+  slices[4].rect.y = origin->y + size->height / 2.0 - 0.5;
+  slices[4].rect.width = 1;
+  slices[4].rect.height = 1;
+
+  /* Right center */
+  slices[5].rect.x = origin->x + size->width - right_width;
+  slices[5].rect.y = origin->y + (size->height / 2.0) - 0.5;
+  slices[5].rect.width = right_width;
+  slices[5].rect.height = 1;
+
+  /* Bottom Left */
+  slices[6].rect.x = origin->x;
+  slices[6].rect.y = origin->y + size->height - bottom_height;
+  slices[6].rect.width = left_width;
+  slices[6].rect.height = bottom_height;
+
+  /* Bottom center */
+  slices[7].rect.x = origin->x + (size->width / 2.0) - 0.5;
+  slices[7].rect.y = origin->y + size->height - bottom_height;
+  slices[7].rect.width = 1;
+  slices[7].rect.height = bottom_height;
+
+  /* Bottom right */
+  slices[8].rect.x = origin->x + size->width - right_width;
+  slices[8].rect.y = origin->y + size->height - bottom_height;
+  slices[8].rect.width = right_width;
+  slices[8].rect.height = bottom_height;
+
+#ifdef G_ENABLE_DEBUG
+  g_assert_cmpfloat (size->width, >=, left_width + right_width);
+  g_assert_cmpfloat (size->height, >=, top_height + bottom_height);
+#endif
+}
+
+static inline void
+nine_slice_to_texture_coords (GskGLTextureNineSlice *slices,
+                              int                    texture_width,
+                              int                    texture_height)
+{
+  float fw = (float)texture_width;
+  float fh = (float)texture_height;
+
+  for (guint i = 0; i < 9; i++)
+    {
+      GskGLTextureNineSlice *slice = &slices[i];
+
+      slice->area.origin.x = slice->rect.x / fw;
+      slice->area.origin.y = 1.0 - ((slice->rect.y + slice->rect.height) / fh);
+      slice->area.size.width = slice->rect.width / fw;
+      slice->area.size.height = slice->rect.height / fh;
+
+#ifdef G_ENABLE_DEBUG
+      g_assert_cmpfloat (slice->area.origin.x, >=, 0);
+      g_assert_cmpfloat (slice->area.origin.x, <=, 1);
+      g_assert_cmpfloat (slice->area.origin.y, >=, 0);
+      g_assert_cmpfloat (slice->area.origin.y, <=, 1);
+      g_assert_cmpfloat (slice->area.origin.x + slice->area.size.width, <=, 1);
+      g_assert_cmpfloat (slice->area.origin.y + slice->area.size.height, <=, 1);
+#endif
+    }
+}
+
+static inline void
+nine_slice_grow (GskGLTextureNineSlice *slices,
+                 int                    amount)
+{
+  /* top left */
+  slices[0].rect.x -= amount;
+  slices[0].rect.y -= amount;
+  if (amount > slices[0].rect.width)
+    slices[0].rect.width += amount * 2;
+  else
+    slices[0].rect.width += amount;
+
+  if (amount > slices[0].rect.height)
+    slices[0].rect.height += amount * 2;
+  else
+    slices[0].rect.height += amount;
+
+
+  /* Top center */
+  slices[1].rect.y -= amount;
+  if (amount > slices[1].rect.height)
+    slices[1].rect.height += amount * 2;
+  else
+    slices[1].rect.height += amount;
+
+  /* top right */
+  slices[2].rect.y -= amount;
+  if (amount > slices[2].rect.width)
+    {
+      slices[2].rect.x -= amount;
+      slices[2].rect.width += amount * 2;
+    }
+  else
+    {
+     slices[2].rect.width += amount;
+    }
+
+  if (amount > slices[2].rect.height)
+    slices[2].rect.height += amount * 2;
+  else
+    slices[2].rect.height += amount;
+
+
+
+  slices[3].rect.x -= amount;
+  if (amount > slices[3].rect.width)
+    slices[3].rect.width += amount * 2;
+  else
+    slices[3].rect.width += amount;
+
+  /* Leave center alone */
+
+  if (amount > slices[5].rect.width)
+    {
+      slices[5].rect.x -= amount;
+      slices[5].rect.width += amount * 2;
+    }
+  else
+    {
+      slices[5].rect.width += amount;
+    }
+
+
+  /* Bottom left */
+  slices[6].rect.x -= amount;
+  if (amount > slices[6].rect.width)
+    {
+      slices[6].rect.width += amount * 2;
+    }
+  else
+    {
+      slices[6].rect.width += amount;
+    }
+
+  if (amount > slices[6].rect.height)
+    {
+      slices[6].rect.y -= amount;
+      slices[6].rect.height += amount * 2;
+    }
+  else
+    {
+      slices[6].rect.height += amount;
+    }
+
+
+  /* Bottom center */
+  if (amount > slices[7].rect.height)
+    {
+      slices[7].rect.y -= amount;
+      slices[7].rect.height += amount * 2;
+    }
+  else
+    {
+      slices[7].rect.height += amount;
+    }
+
+  if (amount > slices[8].rect.width)
+    {
+      slices[8].rect.x -= amount;
+      slices[8].rect.width += amount * 2;
+    }
+  else
+    {
+      slices[8].rect.width += amount;
+    }
+
+  if (amount > slices[8].rect.height)
+    {
+      slices[8].rect.y -= amount;
+      slices[8].rect.height += amount * 2;
+    }
+  else
+    {
+      slices[8].rect.height += amount;
+    }
+
+#ifdef G_ENABLE_DEBUG
+  for (guint i = 0; i < 9; i ++)
+    {
+      g_assert_cmpint (slices[i].rect.x, >=, 0);
+      g_assert_cmpint (slices[i].rect.y, >=, 0);
+      g_assert_cmpint (slices[i].rect.width, >=, 0);
+      g_assert_cmpint (slices[i].rect.height, >=, 0);
+    }
+
+  /* Rows don't overlap */
+  for (guint i = 0; i < 3; i++)
+    g_assert_cmpint (slices[i * 3 + 0].rect.x + slices[i * 3 + 0].rect.width, <, slices[i * 3 + 1].rect.x);
+#endif
+
+}
+
+G_END_DECLS
+
+#endif /* __NINE_SLICE_PRIVATE_H__ */


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