[gtk/wip/otte/paintable: 54/62] image: Implement support for paintables



commit 9a17383ba30b04f818bb3e9739025a10d215e766
Author: Benjamin Otte <otte redhat com>
Date:   Fri Feb 16 10:50:49 2018 +0100

    image: Implement support for paintables
    
    This includes adding support to GtkImageDefintion and GtkIconHelper.
    
    Only GtkImage handles support for signals from the paintable.

 gtk/gtkcellrendererpixbuf.c     |   3 +
 gtk/gtkentry.c                  |   1 +
 gtk/gtkiconhelper.c             |  85 +++++++++++++++++++++----
 gtk/gtkiconhelperprivate.h      |   3 +
 gtk/gtkimage.c                  | 137 ++++++++++++++++++++++++++++++++++++++++
 gtk/gtkimage.h                  |  12 +++-
 gtk/gtkimagedefinition.c        |  40 +++++++++++-
 gtk/gtkimagedefinitionprivate.h |   2 +
 8 files changed, 270 insertions(+), 13 deletions(-)
---
diff --git a/gtk/gtkcellrendererpixbuf.c b/gtk/gtkcellrendererpixbuf.c
index 3d0efa9054..cd042c9a70 100644
--- a/gtk/gtkcellrendererpixbuf.c
+++ b/gtk/gtkcellrendererpixbuf.c
@@ -297,6 +297,9 @@ notify_storage_type (GtkCellRendererPixbuf *cellpixbuf,
     case GTK_IMAGE_TEXTURE:
       g_object_notify (G_OBJECT (cellpixbuf), "texture");
       break;
+    case GTK_IMAGE_PAINTABLE:
+      g_object_notify (G_OBJECT (cellpixbuf), "paintable");
+      break;
     case GTK_IMAGE_ICON_NAME:
       g_object_notify (G_OBJECT (cellpixbuf), "icon-name");
       break;
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index f943193761..6170ba35e2 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -6636,6 +6636,7 @@ gtk_entry_clear_icon (GtkEntry             *entry,
       break;
 
     case GTK_IMAGE_SURFACE:
+    case GTK_IMAGE_PAINTABLE:
     case GTK_IMAGE_EMPTY:
     default:
       g_assert_not_reached ();
diff --git a/gtk/gtkiconhelper.c b/gtk/gtkiconhelper.c
index ddc58e803b..3f9eee4454 100644
--- a/gtk/gtkiconhelper.c
+++ b/gtk/gtkiconhelper.c
@@ -268,6 +268,16 @@ ensure_paintable_from_texture (GtkIconHelper *self,
   return g_object_ref (GDK_PAINTABLE (texture));
 }
 
+static GdkPaintable *
+ensure_paintable_from_paintable (GtkIconHelper *self,
+                                 GdkPaintable  *paintable,
+                                 int           *scale)
+{
+  *scale = 1;
+
+  return g_object_ref (paintable);
+}
+
 static GdkPaintable *
 ensure_paintable_for_gicon (GtkIconHelper    *self,
                             GtkCssStyle      *style,
@@ -326,6 +336,11 @@ gtk_icon_helper_load_paintable (GtkIconHelper   *self,
       symbolic = FALSE;
       break;
 
+    case GTK_IMAGE_PAINTABLE:
+      paintable = ensure_paintable_from_paintable (self, gtk_image_definition_get_paintable (self->def), 
&scale);
+      symbolic = FALSE;
+      break;
+
     case GTK_IMAGE_ICON_NAME:
       scale = gtk_widget_get_scale_factor (self->owner);
       if (self->use_fallback)
@@ -379,6 +394,43 @@ gtk_icon_helper_ensure_paintable (GtkIconHelper *self)
   self->texture_is_symbolic = symbolic;
 }
 
+static void
+get_size_for_paintable (GtkIconHelper *self,
+                        GdkPaintable  *paintable,
+                        int           *width_out,
+                        int           *height_out)
+{
+  int width = gdk_paintable_get_intrinsic_width (paintable);
+  int height = gdk_paintable_get_intrinsic_height (paintable);
+
+  if (width == 0)
+    {
+      if (height != 0)
+        {
+          double ar = gdk_paintable_get_intrinsic_aspect_ratio (paintable);
+
+          if (ar > 0)
+            width = ceil (height * ar);
+        }
+    }
+  else
+    {
+      if (height == 0)
+        {
+          double ar = gdk_paintable_get_intrinsic_aspect_ratio (paintable);
+
+          if (ar > 0)
+            height = ceil (width / ar);
+        }
+    }
+  
+  if (width == 0 || height == 0)
+    ensure_icon_size (self, &width, &height);
+
+  *width_out = width;
+  *height_out = height;
+}
+
 void
 _gtk_icon_helper_get_size (GtkIconHelper *self,
                            gint *width_out,
@@ -413,6 +465,14 @@ _gtk_icon_helper_get_size (GtkIconHelper *self,
       }
       break;
 
+    case GTK_IMAGE_PAINTABLE:
+      {
+        GdkPaintable *paintable = gtk_image_definition_get_paintable (self->def);
+
+        get_size_for_paintable (self, paintable, &width, &height);
+      }
+      break;
+
     case GTK_IMAGE_EMPTY:
     default:
       break;
@@ -425,17 +485,7 @@ _gtk_icon_helper_get_size (GtkIconHelper *self,
 
       if (self->paintable != NULL)
         {
-          width = gdk_paintable_get_intrinsic_width (self->paintable);
-          height = gdk_paintable_get_intrinsic_height (self->paintable);
-          if (width == 0 || height == 0)
-            {
-              ensure_icon_size (self, &width, &height);
-            }
-          else
-            {
-              width = (width + self->texture_scale - 1) / self->texture_scale;
-              height = (height + self->texture_scale - 1) / self->texture_scale;
-            }
+          get_size_for_paintable (self, self->paintable, &width, &height);
         }
       else
         {
@@ -487,6 +537,13 @@ _gtk_icon_helper_set_texture (GtkIconHelper *self,
   gtk_icon_helper_take_definition (self, gtk_image_definition_new_texture (texture));
 }
 
+void
+_gtk_icon_helper_set_paintable (GtkIconHelper *self,
+                               GdkPaintable  *paintable)
+{
+  gtk_icon_helper_take_definition (self, gtk_image_definition_new_paintable (paintable));
+}
+
 gboolean
 _gtk_icon_helper_set_pixel_size (GtkIconHelper *self,
                                  gint           pixel_size)
@@ -555,6 +612,12 @@ _gtk_icon_helper_peek_texture (GtkIconHelper *self)
   return gtk_image_definition_get_texture (self->def);
 }
 
+GdkPaintable *
+_gtk_icon_helper_peek_paintable (GtkIconHelper *self)
+{
+  return gtk_image_definition_get_paintable (self->def);
+}
+
 const gchar *
 _gtk_icon_helper_get_icon_name (GtkIconHelper *self)
 {
diff --git a/gtk/gtkiconhelperprivate.h b/gtk/gtkiconhelperprivate.h
index 6c39294cae..6fd794b70b 100644
--- a/gtk/gtkiconhelperprivate.h
+++ b/gtk/gtkiconhelperprivate.h
@@ -69,6 +69,8 @@ void _gtk_icon_helper_set_surface (GtkIconHelper *self,
                                   cairo_surface_t *surface);
 void _gtk_icon_helper_set_texture (GtkIconHelper *self,
                                   GdkTexture *texture);
+void _gtk_icon_helper_set_paintable (GtkIconHelper *self,
+                                    GdkPaintable  *paintable);
 
 gboolean _gtk_icon_helper_set_pixel_size   (GtkIconHelper *self,
                                             gint           pixel_size);
@@ -82,6 +84,7 @@ gboolean _gtk_icon_helper_get_use_fallback (GtkIconHelper *self);
 GIcon *_gtk_icon_helper_peek_gicon (GtkIconHelper *self);
 cairo_surface_t *_gtk_icon_helper_peek_surface (GtkIconHelper *self);
 GdkTexture *_gtk_icon_helper_peek_texture (GtkIconHelper *self);
+GdkPaintable *_gtk_icon_helper_peek_paintable (GtkIconHelper *self);
 
 GtkImageDefinition *gtk_icon_helper_get_definition (GtkIconHelper *self);
 const gchar *_gtk_icon_helper_get_icon_name (GtkIconHelper *self);
diff --git a/gtk/gtkimage.c b/gtk/gtkimage.c
index 31ae0a4d38..7deb8b70d3 100644
--- a/gtk/gtkimage.c
+++ b/gtk/gtkimage.c
@@ -119,6 +119,7 @@ enum
 {
   PROP_0,
   PROP_SURFACE,
+  PROP_PAINTABLE,
   PROP_TEXTURE,
   PROP_FILE,
   PROP_ICON_SIZE,
@@ -161,6 +162,13 @@ gtk_image_class_init (GtkImageClass *class)
                           CAIRO_GOBJECT_TYPE_SURFACE,
                           GTK_PARAM_READWRITE);
 
+  image_props[PROP_PAINTABLE] =
+      g_param_spec_object ("paintable",
+                           P_("Paintable"),
+                           P_("A GdkPaintable to display"),
+                           GDK_TYPE_PAINTABLE,
+                           GTK_PARAM_READWRITE);
+
   image_props[PROP_TEXTURE] =
       g_param_spec_object ("texture",
                            P_("Texture"),
@@ -284,6 +292,8 @@ gtk_image_finalize (GObject *object)
   GtkImage *image = GTK_IMAGE (object);
   GtkImagePrivate *priv = gtk_image_get_instance_private (image);
 
+  gtk_image_clear (image);
+
   gtk_icon_helper_destroy (&priv->icon_helper);
 
   g_free (priv->filename);
@@ -306,6 +316,9 @@ gtk_image_set_property (GObject      *object,
     case PROP_SURFACE:
       gtk_image_set_from_surface (image, g_value_get_boxed (value));
       break;
+    case PROP_PAINTABLE:
+      gtk_image_set_from_paintable (image, g_value_get_object (value));
+      break;
     case PROP_TEXTURE:
       gtk_image_set_from_texture (image, g_value_get_object (value));
       break;
@@ -353,6 +366,9 @@ gtk_image_get_property (GObject     *object,
     case PROP_SURFACE:
       g_value_set_boxed (value, _gtk_icon_helper_peek_surface (&priv->icon_helper));
       break;
+    case PROP_PAINTABLE:
+      g_value_set_object (value, _gtk_icon_helper_peek_paintable (&priv->icon_helper));
+      break;
     case PROP_TEXTURE:
       g_value_set_object (value, _gtk_icon_helper_peek_texture (&priv->icon_helper));
       break;
@@ -479,6 +495,32 @@ gtk_image_new_from_pixbuf (GdkPixbuf *pixbuf)
   return GTK_WIDGET (image);  
 }
 
+/**
+ * gtk_image_new_from_paintable:
+ * @paintable: (allow-none): a #GdkPaintable, or %NULL
+ *
+ * Creates a new #GtkImage displaying @paintable.
+ * The #GtkImage does not assume a reference to the
+ * paintable; you still need to unref it if you own references.
+ * #GtkImage will add its own reference rather than adopting yours.
+ *
+ * The #GtkImage will track changes to the @paintable and update
+ * its size and contents in response to it.
+ *
+ * Returns: a new #GtkImage
+ **/
+GtkWidget*
+gtk_image_new_from_paintable (GdkPaintable *paintable)
+{
+  GtkImage *image;
+
+  image = g_object_new (GTK_TYPE_IMAGE, NULL);
+
+  gtk_image_set_from_paintable (image, paintable);
+
+  return GTK_WIDGET (image);  
+}
+
 /**
  * gtk_image_new_from_texture:
  * @texture: (allow-none): a #GdkTexture, or %NULL
@@ -945,6 +987,64 @@ gtk_image_set_from_surface (GtkImage       *image,
   g_object_thaw_notify (G_OBJECT (image));
 }
 
+static void
+gtk_image_paintable_invalidate_contents (GdkPaintable *paintable,
+                                         GtkImage     *image)
+{
+  gtk_widget_queue_draw (GTK_WIDGET (image));
+}
+
+static void
+gtk_image_paintable_invalidate_size (GdkPaintable *paintable,
+                                     GtkImage     *image)
+{
+  GtkImagePrivate *priv = gtk_image_get_instance_private (image);
+
+  gtk_icon_helper_invalidate (&priv->icon_helper);
+}
+
+/**
+ * gtk_image_set_from_paintable:
+ * @image: a #GtkImage
+ * @paintable: (nullable): a #GdkPaintable or %NULL
+ *
+ * See gtk_image_new_from_paintable() for details.
+ **/
+void
+gtk_image_set_from_paintable (GtkImage     *image,
+                             GdkPaintable *paintable)
+{
+  GtkImagePrivate *priv = gtk_image_get_instance_private (image);
+
+  g_return_if_fail (GTK_IS_IMAGE (image));
+  g_return_if_fail (paintable == NULL || GDK_IS_PAINTABLE (paintable));
+
+  g_object_freeze_notify (G_OBJECT (image));
+
+  if (paintable)
+    g_object_ref (paintable);
+
+  gtk_image_clear (image);
+
+  if (paintable)
+    {
+      _gtk_icon_helper_set_paintable (&priv->icon_helper, paintable);
+      g_signal_connect (paintable,
+                        "invalidate-contents",
+                        G_CALLBACK (gtk_image_paintable_invalidate_contents),
+                        image);
+      g_signal_connect (paintable,
+                        "invalidate-size",
+                        G_CALLBACK (gtk_image_paintable_invalidate_size),
+                        image);
+      g_object_unref (paintable);
+    }
+
+  g_object_notify_by_pspec (G_OBJECT (image), image_props[PROP_PAINTABLE]);
+  
+  g_object_thaw_notify (G_OBJECT (image));
+}
+
 /**
  * gtk_image_set_from_texture:
  * @image: a #GtkImage
@@ -1022,6 +1122,29 @@ gtk_image_get_surface (GtkImage *image)
   return _gtk_icon_helper_peek_surface (&priv->icon_helper);
 }
 
+/**
+ * gtk_image_get_paintable:
+ * @image: a #GtkImage
+ *
+ * Gets the image #GdkPaintable being displayed by the #GtkImage.
+ * The storage type of the image must be %GTK_IMAGE_EMPTY or
+ * %GTK_IMAGE_PAINTABLE (see gtk_image_get_storage_type()).
+ * The caller of this function does not own a reference to the
+ * returned paintable.
+ * 
+ * Returns: (nullable) (transfer none): the displayed paintable, or %NULL if
+ *   the image is empty
+ **/
+GdkPaintable *
+gtk_image_get_paintable (GtkImage *image)
+{
+  GtkImagePrivate *priv = gtk_image_get_instance_private (image);
+
+  g_return_val_if_fail (GTK_IS_IMAGE (image), NULL);
+
+  return _gtk_icon_helper_peek_paintable (&priv->icon_helper);
+}
+
 /**
  * gtk_image_get_texture:
  * @image: a #GtkImage
@@ -1205,6 +1328,9 @@ gtk_image_notify_for_storage_type (GtkImage     *image,
     case GTK_IMAGE_TEXTURE:
       g_object_notify_by_pspec (G_OBJECT (image), image_props[PROP_TEXTURE]);
       break;
+    case GTK_IMAGE_PAINTABLE:
+      g_object_notify_by_pspec (G_OBJECT (image), image_props[PROP_PAINTABLE]);
+      break;
     case GTK_IMAGE_EMPTY:
     default:
       break;
@@ -1277,6 +1403,17 @@ gtk_image_clear (GtkImage *image)
       g_object_notify_by_pspec (G_OBJECT (image), image_props[PROP_RESOURCE]);
     }
 
+  if (storage_type == GTK_IMAGE_PAINTABLE)
+    {
+      GdkPaintable *paintable = _gtk_icon_helper_peek_paintable (&priv->icon_helper);
+      g_signal_handlers_disconnect_by_func (paintable,
+                                            gtk_image_paintable_invalidate_contents,
+                                            image);
+      g_signal_handlers_disconnect_by_func (paintable,
+                                            gtk_image_paintable_invalidate_size,
+                                            image);
+    }
+
   _gtk_icon_helper_clear (&priv->icon_helper);
 
   g_object_thaw_notify (G_OBJECT (image));
diff --git a/gtk/gtkimage.h b/gtk/gtkimage.h
index 45ac3f8fd2..10ce7da730 100644
--- a/gtk/gtkimage.h
+++ b/gtk/gtkimage.h
@@ -59,6 +59,8 @@ typedef struct _GtkImageClass         GtkImageClass;
  *  This image type was added in GTK+ 3.10
  * @GTK_IMAGE_TEXTURE: the widget contains a #GdkTexture.
  *  This image type was added in GTK+ 3.94
+ * @GTK_IMAGE_PAINTABLE: the widget contains a #GdkPaintable.
+ *  This image type was added in GTK+ 3.96
  *
  * Describes the image data representation used by a #GtkImage. If you
  * want to get the image from the widget, you can only get the
@@ -74,7 +76,8 @@ typedef enum
   GTK_IMAGE_ICON_NAME,
   GTK_IMAGE_GICON,
   GTK_IMAGE_SURFACE,
-  GTK_IMAGE_TEXTURE
+  GTK_IMAGE_TEXTURE,
+  GTK_IMAGE_PAINTABLE
 } GtkImageType;
 
 /**
@@ -113,6 +116,8 @@ GtkWidget* gtk_image_new_from_pixbuf    (GdkPixbuf       *pixbuf);
 GDK_AVAILABLE_IN_ALL
 GtkWidget* gtk_image_new_from_texture   (GdkTexture      *texture);
 GDK_AVAILABLE_IN_ALL
+GtkWidget* gtk_image_new_from_paintable (GdkPaintable    *paintable);
+GDK_AVAILABLE_IN_ALL
 GtkWidget* gtk_image_new_from_icon_name (const gchar     *icon_name);
 GDK_AVAILABLE_IN_ALL
 GtkWidget* gtk_image_new_from_gicon     (GIcon           *icon);
@@ -134,6 +139,9 @@ GDK_AVAILABLE_IN_ALL
 void gtk_image_set_from_texture   (GtkImage        *image,
                                    GdkTexture      *texture);
 GDK_AVAILABLE_IN_ALL
+void gtk_image_set_from_paintable (GtkImage        *image,
+                                   GdkPaintable    *paintable);
+GDK_AVAILABLE_IN_ALL
 void gtk_image_set_from_icon_name (GtkImage        *image,
                                   const gchar     *icon_name);
 GDK_AVAILABLE_IN_ALL
@@ -156,6 +164,8 @@ GDK_AVAILABLE_IN_ALL
 cairo_surface_t *gtk_image_get_surface (GtkImage *image);
 GDK_AVAILABLE_IN_ALL
 GdkTexture *gtk_image_get_texture  (GtkImage       *image);
+GDK_AVAILABLE_IN_ALL
+GdkPaintable *gtk_image_get_paintable (GtkImage       *image);
 
 GDK_AVAILABLE_IN_ALL
 const char *gtk_image_get_icon_name (GtkImage     *image);
diff --git a/gtk/gtkimagedefinition.c b/gtk/gtkimagedefinition.c
index 6c45ca4305..561b2b296e 100644
--- a/gtk/gtkimagedefinition.c
+++ b/gtk/gtkimagedefinition.c
@@ -24,6 +24,7 @@ typedef struct _GtkImageDefinitionIconName GtkImageDefinitionIconName;
 typedef struct _GtkImageDefinitionGIcon GtkImageDefinitionGIcon;
 typedef struct _GtkImageDefinitionSurface GtkImageDefinitionSurface;
 typedef struct _GtkImageDefinitionTexture GtkImageDefinitionTexture;
+typedef struct _GtkImageDefinitionPaintable GtkImageDefinitionPaintable;
 
 struct _GtkImageDefinitionEmpty {
   GtkImageType type;
@@ -58,6 +59,13 @@ struct _GtkImageDefinitionTexture {
   GdkTexture *texture;
 };
 
+struct _GtkImageDefinitionPaintable {
+  GtkImageType type;
+  gint ref_count;
+
+  GdkPaintable *paintable;
+};
+
 union _GtkImageDefinition
 {
   GtkImageType type;
@@ -66,6 +74,7 @@ union _GtkImageDefinition
   GtkImageDefinitionGIcon gicon;
   GtkImageDefinitionSurface surface;
   GtkImageDefinitionTexture texture;
+  GtkImageDefinitionPaintable paintable;
 };
 
 GtkImageDefinition *
@@ -84,7 +93,8 @@ gtk_image_definition_alloc (GtkImageType type)
     sizeof (GtkImageDefinitionIconName),
     sizeof (GtkImageDefinitionGIcon),
     sizeof (GtkImageDefinitionSurface),
-    sizeof (GtkImageDefinitionTexture)
+    sizeof (GtkImageDefinitionTexture),
+    sizeof (GtkImageDefinitionPaintable)
   };
   GtkImageDefinition *def;
 
@@ -153,6 +163,20 @@ gtk_image_definition_new_texture (GdkTexture *texture)
   return def;
 }
 
+GtkImageDefinition *
+gtk_image_definition_new_paintable (GdkPaintable *paintable)
+{
+  GtkImageDefinition *def;
+
+  if (paintable == NULL)
+    return NULL;
+
+  def = gtk_image_definition_alloc (GTK_IMAGE_PAINTABLE);
+  def->paintable.paintable = g_object_ref (paintable);
+
+  return def;
+}
+
 GtkImageDefinition *
 gtk_image_definition_ref (GtkImageDefinition *def)
 {
@@ -181,6 +205,9 @@ gtk_image_definition_unref (GtkImageDefinition *def)
     case GTK_IMAGE_TEXTURE:
       g_object_unref (def->texture.texture);
       break;
+    case GTK_IMAGE_PAINTABLE:
+      g_object_unref (def->paintable.paintable);
+      break;
     case GTK_IMAGE_ICON_NAME:
       g_free (def->icon_name.icon_name);
       break;
@@ -208,6 +235,7 @@ gtk_image_definition_get_scale (const GtkImageDefinition *def)
     case GTK_IMAGE_EMPTY:
     case GTK_IMAGE_SURFACE:
     case GTK_IMAGE_TEXTURE:
+    case GTK_IMAGE_PAINTABLE:
     case GTK_IMAGE_ICON_NAME:
     case GTK_IMAGE_GICON:
       return 1;
@@ -249,3 +277,13 @@ gtk_image_definition_get_texture (const GtkImageDefinition *def)
 
   return def->texture.texture;
 }
+
+GdkPaintable *
+gtk_image_definition_get_paintable (const GtkImageDefinition *def)
+{
+  if (def->type != GTK_IMAGE_PAINTABLE)
+    return NULL;
+
+  return def->paintable.paintable;
+}
+
diff --git a/gtk/gtkimagedefinitionprivate.h b/gtk/gtkimagedefinitionprivate.h
index 2dac974ee8..8cdb26c9e6 100644
--- a/gtk/gtkimagedefinitionprivate.h
+++ b/gtk/gtkimagedefinitionprivate.h
@@ -30,6 +30,7 @@ GtkImageDefinition *    gtk_image_definition_new_icon_name      (const char
 GtkImageDefinition *    gtk_image_definition_new_gicon          (GIcon                          *gicon);
 GtkImageDefinition *    gtk_image_definition_new_surface        (cairo_surface_t                *surface);
 GtkImageDefinition *    gtk_image_definition_new_texture        (GdkTexture                     *texture);
+GtkImageDefinition *    gtk_image_definition_new_paintable      (GdkPaintable                   *paintable);
 
 GtkImageDefinition *    gtk_image_definition_ref                (GtkImageDefinition             *def);
 void                    gtk_image_definition_unref              (GtkImageDefinition             *def);
@@ -40,6 +41,7 @@ const gchar *           gtk_image_definition_get_icon_name      (const GtkImageD
 GIcon *                 gtk_image_definition_get_gicon          (const GtkImageDefinition       *def);
 cairo_surface_t *       gtk_image_definition_get_surface        (const GtkImageDefinition       *def);
 GdkTexture *            gtk_image_definition_get_texture        (const GtkImageDefinition       *def);
+GdkPaintable *          gtk_image_definition_get_paintable      (const GtkImageDefinition       *def);
 
 
 G_END_DECLS


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