Update/clean and apply the async-texture patch from bug #1144

This commit is contained in:
Chris Lord 2009-01-07 17:02:43 +00:00
parent 0d9c07f824
commit 2cea22e6a0
7 changed files with 262 additions and 27 deletions

View 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
};
@ -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,

View 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);
};
/**

View File

@ -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__ */

View File

@ -28,6 +28,13 @@
G_BEGIN_DECLS
/**
* CoglBitmap:
*
* Type used for storing image data.
*/
typedef struct _CoglBitmap CoglBitmap;
/**
* CoglHandle:
*

View 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);
}

View File

@ -28,8 +28,6 @@
#include <glib.h>
typedef struct _CoglBitmap CoglBitmap;
struct _CoglBitmap
{
guchar *data;

View 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;
@ -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,