Update/clean and apply the async-texture patch from bug #1144
authorChris Lord <chris@linux.intel.com>
Wed, 7 Jan 2009 17:02:43 +0000 (17:02 +0000)
committerChris Lord <chris@linux.intel.com>
Wed, 7 Jan 2009 17:02:43 +0000 (17:02 +0000)
clutter/clutter-texture.c
clutter/clutter-texture.h
clutter/cogl/cogl-texture.h
clutter/cogl/cogl-types.h
clutter/cogl/common/cogl-bitmap.c
clutter/cogl/common/cogl-bitmap.h
clutter/cogl/gl/cogl-texture.c

index 086a513..10cb217 100644 (file)
@@ -96,6 +96,13 @@ struct _ClutterTexturePrivate
   guint                        repeat_y : 1;
   guint                        in_dispose : 1;
   guint                        keep_aspect_ratio : 1;
+  guint                        load_async : 1;
+  
+  GThread                     *load_thread;
+  gchar                       *load_filename;
+  CoglBitmap                  *load_bitmap;
+  GError                      *load_error;
+  guint                        load_source;
 };
 
 enum
@@ -110,13 +117,17 @@ enum
   PROP_FILTER_QUALITY,
   PROP_COGL_TEXTURE,
   PROP_FILENAME,
-  PROP_KEEP_ASPECT_RATIO
+  PROP_KEEP_ASPECT_RATIO,
+  PROP_LOAD_ASYNC
 };
 
 enum
 {
   SIZE_CHANGE,
   PIXBUF_CHANGE,
+  LOAD_SUCCESS,
+  LOAD_FINISHED,
+  
   LAST_SIGNAL
 };
 
@@ -602,6 +613,35 @@ clutter_texture_paint (ClutterActor *self)
 }
 
 static void
+clutter_texture_thread_cancel (ClutterTexture *texture)
+{
+  ClutterTexturePrivate *priv = texture->priv;
+
+  if (priv->load_thread)
+    {
+      g_thread_join (priv->load_thread);
+      priv->load_thread = NULL;
+    }
+  
+  if (priv->load_source)
+    {
+      g_source_remove (priv->load_source);
+      priv->load_source = 0;
+      
+      if (priv->load_error)
+        {
+          g_error_free (priv->load_error);
+          priv->load_error = NULL;
+        }
+      else
+        {
+          cogl_bitmap_free (priv->load_bitmap);
+          priv->load_bitmap = NULL;
+        }
+    }
+}
+
+static void
 clutter_texture_dispose (GObject *object)
 {
   ClutterTexture *texture = CLUTTER_TEXTURE (object);
@@ -625,7 +665,9 @@ clutter_texture_dispose (GObject *object)
       g_free (priv->local_data);
       priv->local_data = NULL;
     }
-
+  
+  clutter_texture_thread_cancel (texture);
+  
   G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object);
 }
 
@@ -686,6 +728,11 @@ clutter_texture_set_property (GObject      *object,
     case PROP_KEEP_ASPECT_RATIO:
       priv->keep_aspect_ratio = g_value_get_boolean (value);
       break;
+    case PROP_LOAD_ASYNC:
+      priv->load_async = g_value_get_boolean (value);
+      if (priv->load_async && !g_thread_supported())
+        priv->load_async = FALSE;
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -736,6 +783,9 @@ clutter_texture_get_property (GObject    *object,
     case PROP_KEEP_ASPECT_RATIO:
       g_value_set_boolean (value, priv->keep_aspect_ratio);
       break;
+    case PROP_LOAD_ASYNC:
+      g_value_set_boolean (value, priv->load_async);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -861,6 +911,15 @@ clutter_texture_class_init (ClutterTextureClass *klass)
                           FALSE,
                           CLUTTER_PARAM_READWRITE));
 
+  g_object_class_install_property
+    (gobject_class, PROP_LOAD_ASYNC,
+     g_param_spec_boolean ("load-async",
+                          "Load asynchronously",
+                          "Load files inside a thread to avoid blocking when "
+                           "loading images.",
+                          FALSE,
+                          CLUTTER_PARAM_READWRITE));
+
   /**
    * ClutterTexture::size-change:
    * @texture: the texture which received the signal
@@ -896,6 +955,25 @@ clutter_texture_class_init (ClutterTextureClass *klass)
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE,
                  0);
+  /**
+   * ClutterTexture::load-finished:
+   * @texture: the texture which received the signal
+   * @error: A set error, or %NULL
+   *
+   * The ::load-finished signal is emitted when asynchronous texture 
+   * load has completed. If there was an error during loading, @error will 
+   * be set.
+   */
+  texture_signals[LOAD_FINISHED] =
+    g_signal_new ("load-finished",
+                 G_TYPE_FROM_CLASS (gobject_class),
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (ClutterTextureClass, load_finished),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__POINTER,
+                 G_TYPE_NONE,
+                 1,
+                  G_TYPE_POINTER);
 }
 
 static ClutterScriptableIface *parent_scriptable_iface = NULL;
@@ -1301,6 +1379,65 @@ clutter_texture_set_from_yuv_data   (ClutterTexture     *texture,
                                        error);
 }
 
+static gboolean
+clutter_texture_thread_cb (ClutterTexture *self)
+{
+  ClutterTexturePrivate *priv = self->priv;
+  
+  priv->load_source = 0;
+  
+  if (priv->load_thread)
+    {
+      g_thread_join (priv->load_thread);
+      priv->load_thread = NULL;
+    }
+  else
+    return FALSE;
+  
+  if (!priv->load_error)
+    {
+      CoglHandle handle;
+      
+      handle = cogl_texture_new_from_bitmap (priv->load_bitmap,
+                                             priv->no_slice ?
+                                               -1 : priv->max_tile_waste,
+                                             priv->filter_quality ==
+                                             CLUTTER_TEXTURE_QUALITY_HIGH,
+                                             COGL_PIXEL_FORMAT_ANY);
+      clutter_texture_set_cogl_texture (self, handle);
+      cogl_texture_unref (handle);
+      
+      cogl_bitmap_free (priv->load_bitmap);
+      priv->load_bitmap = NULL;
+    }
+  
+  g_signal_emit (self, texture_signals[LOAD_FINISHED], 0, priv->load_error);
+  
+  if (priv->load_error)
+    {
+      g_error_free (priv->load_error);
+      priv->load_error = NULL;
+    }
+  
+  return FALSE;
+}
+
+static gpointer
+clutter_texture_thread_func (ClutterTexture *self)
+{
+  ClutterTexturePrivate *priv = self->priv;
+  
+  /* Try loading with imaging backend */
+  priv->load_bitmap = cogl_bitmap_new_from_file (priv->load_filename,
+                                                 &priv->load_error);
+  g_free (priv->load_filename);
+  priv->load_filename = NULL;
+  
+  clutter_threads_add_idle ((GSourceFunc)clutter_texture_thread_cb, self);
+  
+  return NULL;
+}
+
 /**
  * clutter_texture_set_from_file:
  * @texture: A #ClutterTexture
@@ -1326,6 +1463,17 @@ clutter_texture_set_from_file (ClutterTexture *texture,
 
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
+  if (priv->load_async)
+    {
+      clutter_texture_thread_cancel (texture);
+      
+      priv->load_filename = g_strdup (filename);
+      priv->load_thread = g_thread_create ((GThreadFunc)
+                                           clutter_texture_thread_func,
+                                           texture, TRUE, error);
+      return priv->load_thread ? TRUE : FALSE;
+    }
+
   if ((new_texture = cogl_texture_new_from_file
                           (filename,
                            priv->no_slice ? -1 : priv->max_tile_waste,
index e15f798..3f20413 100644 (file)
@@ -84,6 +84,8 @@ struct _ClutterTextureClass
                         gint            width,
                         gint            height);
   void (*pixbuf_change) (ClutterTexture *texture);
+  void (*load_finished) (ClutterTexture *texture,
+                         GError         *error);
 
   /*< private >*/
   /* padding, for future expansion */
@@ -92,7 +94,6 @@ struct _ClutterTextureClass
   void (*_clutter_texture3) (void);
   void (*_clutter_texture4) (void);
   void (*_clutter_texture5) (void);
-  void (*_clutter_texture6) (void);
 };
 
 /**
index 07cec1c..7c1dd9c 100644 (file)
@@ -135,6 +135,26 @@ CoglHandle      cogl_texture_new_from_foreign (GLuint              gl_handle,
                                                CoglPixelFormat     format);
 
 /**
+ * cogl_texture_new_from_bitmap:
+ * @handle: handle of the preloaded texture.
+ * @max_waste: maximum extra horizontal and|or vertical margin pixels to make
+ * texture fit GPU limitations.
+ * @auto_mipmap: enable or disable automatic generation of mipmap pyramid
+ * from the base level image whenever it is updated.
+ * @internal_format: the #CoglPixelFormat to use for the GPU storage of the
+ * texture.
+ *
+ * Create a cogl texture from a #CoglBitmap.
+ *
+ * Returns: a #CoglHandle to the newly created texture or COGL_INVALID_HANDLE
+ * if creating the texture failed.
+ */
+CoglHandle      cogl_texture_new_from_bitmap (CoglBitmap     *bitmap,
+                                              gint            max_waste,
+                                              gboolean        auto_mipmap,
+                                              CoglPixelFormat internal_format);
+
+/**
  * cogl_is_texture:
  * @handle: A CoglHandle
  *
@@ -385,6 +405,28 @@ void            cogl_texture_polygon          (CoglHandle          handle,
                                                CoglTextureVertex  *vertices,
                                                gboolean            use_color);
 
+/**
+ * cogl_bitmap_new_from_file:
+ * @filename: the file to load.
+ * @error: a #GError or %NULL.
+ *
+ * Load an image file from disk. This function can be safely called from 
+ * within a thread.
+ *
+ * Returns: A #CoglBitmap to the new loaded image data, or %NULL if loading 
+ * the image failed.
+ */
+CoglBitmap *    cogl_bitmap_new_from_file     (const gchar    *filename,
+                                               GError        **error);
+
+/**
+ * cogl_bitmap_free:
+ * @bmp: a #CoglBitmap.
+ *
+ * Frees a #CoglBitmap.
+ */
+void            cogl_bitmap_free              (CoglBitmap     *bmp);
+
 G_END_DECLS
 
 #endif /* __COGL_TEXTURE_H__ */
index 4fb6995..6818d13 100644 (file)
 G_BEGIN_DECLS
 
 /**
+ * CoglBitmap:
+ *
+ * Type used for storing image data.
+ */
+typedef struct _CoglBitmap CoglBitmap;
+
+/**
  * CoglHandle:
  *
  * Type used for storing references to cogl objects, the CoglHandle is
index 39341e0..6f31ebb 100644 (file)
@@ -148,3 +148,34 @@ _cogl_bitmap_copy_subregion (CoglBitmap *src,
       dstdata += dst->rowstride;
     }
 }
+
+CoglBitmap *
+cogl_bitmap_new_from_file (const gchar    *filename,
+                           GError        **error)
+{
+  CoglBitmap   bmp;
+  
+  g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE);
+
+  /* Try loading with imaging backend */
+  if (!_cogl_bitmap_from_file (&bmp, filename, error))
+    {
+      /* Try fallback */
+      if (!_cogl_bitmap_fallback_from_file (&bmp, filename))
+       return NULL;
+      else if (error && *error)
+       {
+         g_error_free (*error);
+         *error = NULL;
+       }
+    }
+  
+  return (CoglBitmap *) g_memdup (&bmp, sizeof (CoglBitmap));
+}
+
+void
+cogl_bitmap_free (CoglBitmap *bmp)
+{
+  g_free (bmp->data);
+  g_free (bmp);
+}
index ca9f8a9..9baec81 100644 (file)
@@ -28,8 +28,6 @@
 
 #include <glib.h>
 
-typedef struct _CoglBitmap CoglBitmap;
-
 struct _CoglBitmap
 {
   guchar          *data;
index 12caf7c..ec4f17d 100644 (file)
@@ -1323,30 +1323,13 @@ cogl_texture_new_from_data (guint              width,
 }
 
 CoglHandle
-cogl_texture_new_from_file (const gchar     *filename,
-                           gint             max_waste,
-                            gboolean         auto_mipmap,
-                           CoglPixelFormat  internal_format,
-                           GError         **error)
+cogl_texture_new_from_bitmap (CoglBitmap      *bmp,
+                              gint             max_waste,
+                              gboolean         auto_mipmap,
+                              CoglPixelFormat  internal_format)
 {
-  CoglBitmap   bmp;
   CoglTexture *tex;
 
-  g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE);
-
-  /* Try loading with imaging backend */
-  if (!_cogl_bitmap_from_file (&bmp, filename, error))
-    {
-      /* Try fallback */
-      if (!_cogl_bitmap_fallback_from_file (&bmp, filename))
-       return COGL_INVALID_HANDLE;
-      else if (error && *error)
-       {
-         g_error_free (*error);
-         *error = NULL;
-       }
-    }
-
   /* Create new texture and fill with loaded data */
   tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture));
 
@@ -1356,8 +1339,9 @@ cogl_texture_new_from_file (const gchar     *filename,
   tex->is_foreign = FALSE;
   tex->auto_mipmap = auto_mipmap;
 
-  tex->bitmap = bmp;
+  tex->bitmap = *bmp;
   tex->bitmap_owner = TRUE;
+  bmp->data = NULL;
 
   tex->slice_x_spans = NULL;
   tex->slice_y_spans = NULL;
@@ -1399,6 +1383,30 @@ cogl_texture_new_from_file (const gchar     *filename,
 }
 
 CoglHandle
+cogl_texture_new_from_file (const gchar     *filename,
+                            gint             max_waste,
+                            gboolean         auto_mipmap,
+                            CoglPixelFormat  internal_format,
+                            GError         **error)
+{
+  CoglBitmap  *bmp;
+  CoglHandle   handle;
+  
+  g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE);
+
+  if (!(bmp = cogl_bitmap_new_from_file (filename, error)))
+    return COGL_INVALID_HANDLE;
+    
+  handle = cogl_texture_new_from_bitmap (bmp,
+                                         max_waste,
+                                         auto_mipmap,
+                                         internal_format);
+  cogl_bitmap_free (bmp);
+
+  return handle;
+}
+
+CoglHandle
 cogl_texture_new_from_foreign (GLuint           gl_handle,
                               GLenum           gl_target,
                               GLuint           width,