diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 086a5134c..10cb2172c 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -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 }; @@ -601,6 +612,35 @@ clutter_texture_paint (ClutterActor *self) 0, 0, t_w, t_h); } +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) { @@ -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, diff --git a/clutter/clutter-texture.h b/clutter/clutter-texture.h index e15f798b1..3f20413e8 100644 --- a/clutter/clutter-texture.h +++ b/clutter/clutter-texture.h @@ -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); }; /** diff --git a/clutter/cogl/cogl-texture.h b/clutter/cogl/cogl-texture.h index 07cec1ce1..7c1dd9c9f 100644 --- a/clutter/cogl/cogl-texture.h +++ b/clutter/cogl/cogl-texture.h @@ -134,6 +134,26 @@ CoglHandle cogl_texture_new_from_foreign (GLuint gl_handle, GLuint y_pot_waste, 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__ */ diff --git a/clutter/cogl/cogl-types.h b/clutter/cogl/cogl-types.h index 4fb69953a..6818d1393 100644 --- a/clutter/cogl/cogl-types.h +++ b/clutter/cogl/cogl-types.h @@ -28,6 +28,13 @@ G_BEGIN_DECLS +/** + * CoglBitmap: + * + * Type used for storing image data. + */ +typedef struct _CoglBitmap CoglBitmap; + /** * CoglHandle: * diff --git a/clutter/cogl/common/cogl-bitmap.c b/clutter/cogl/common/cogl-bitmap.c index 39341e0b0..6f31ebb5c 100644 --- a/clutter/cogl/common/cogl-bitmap.c +++ b/clutter/cogl/common/cogl-bitmap.c @@ -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); +} diff --git a/clutter/cogl/common/cogl-bitmap.h b/clutter/cogl/common/cogl-bitmap.h index ca9f8a9c3..9baec8170 100644 --- a/clutter/cogl/common/cogl-bitmap.h +++ b/clutter/cogl/common/cogl-bitmap.h @@ -28,8 +28,6 @@ #include -typedef struct _CoglBitmap CoglBitmap; - struct _CoglBitmap { guchar *data; diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index 12caf7c58..ec4f17d32 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -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; @@ -1398,6 +1382,30 @@ cogl_texture_new_from_file (const gchar *filename, return _cogl_texture_handle_new (tex); } +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,