2009-05-13 14:06:24 -04:00
|
|
|
#include "shell-texture-cache.h"
|
|
|
|
#include "shell-global.h"
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
GIcon *icon;
|
|
|
|
guint size;
|
|
|
|
} CacheKey;
|
|
|
|
|
|
|
|
struct _ShellTextureCachePrivate
|
|
|
|
{
|
|
|
|
GHashTable *gicon_cache; /* CacheKey -> CoglTexture* */
|
|
|
|
};
|
|
|
|
|
|
|
|
static void shell_texture_cache_dispose (GObject *object);
|
|
|
|
static void shell_texture_cache_finalize (GObject *object);
|
|
|
|
|
|
|
|
G_DEFINE_TYPE(ShellTextureCache, shell_texture_cache, G_TYPE_OBJECT);
|
|
|
|
|
|
|
|
static guint
|
|
|
|
cache_key_hash (gconstpointer a)
|
|
|
|
{
|
|
|
|
CacheKey *akey = (CacheKey *)a;
|
|
|
|
|
|
|
|
if (akey->icon)
|
|
|
|
return g_icon_hash (akey->icon) + 31*akey->size;
|
|
|
|
g_assert_not_reached ();
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
cache_key_equal (gconstpointer a,
|
|
|
|
gconstpointer b)
|
|
|
|
{
|
|
|
|
CacheKey *akey = (CacheKey*)a;
|
|
|
|
CacheKey *bkey = (CacheKey*)b;
|
|
|
|
|
|
|
|
if (akey->size != bkey->size)
|
|
|
|
return FALSE;
|
|
|
|
if (akey->icon && bkey->icon)
|
|
|
|
return g_icon_equal (akey->icon, bkey->icon);
|
|
|
|
g_assert_not_reached ();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cache_key_destroy (gpointer a)
|
|
|
|
{
|
|
|
|
CacheKey *akey = (CacheKey*)a;
|
|
|
|
if (akey->icon)
|
|
|
|
g_object_unref (akey->icon);
|
|
|
|
g_free (akey);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_texture_cache_class_init (ShellTextureCacheClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *gobject_class = (GObjectClass *)klass;
|
|
|
|
|
|
|
|
gobject_class->dispose = shell_texture_cache_dispose;
|
|
|
|
gobject_class->finalize = shell_texture_cache_finalize;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_texture_cache_init (ShellTextureCache *self)
|
|
|
|
{
|
|
|
|
self->priv = g_new0 (ShellTextureCachePrivate, 1);
|
|
|
|
self->priv->gicon_cache = g_hash_table_new_full (cache_key_hash, cache_key_equal,
|
|
|
|
cache_key_destroy, cogl_handle_unref);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_texture_cache_dispose (GObject *object)
|
|
|
|
{
|
|
|
|
ShellTextureCache *self = (ShellTextureCache*)object;
|
|
|
|
|
|
|
|
if (self->priv->gicon_cache)
|
|
|
|
g_hash_table_destroy (self->priv->gicon_cache);
|
|
|
|
self->priv->gicon_cache = NULL;
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (shell_texture_cache_parent_class)->dispose (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_texture_cache_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
G_OBJECT_CLASS (shell_texture_cache_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
ShellTextureCache*
|
|
|
|
shell_texture_cache_new ()
|
|
|
|
{
|
|
|
|
return SHELL_TEXTURE_CACHE (g_object_new (SHELL_TYPE_TEXTURE_CACHE,
|
|
|
|
NULL));
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char *uri;
|
|
|
|
GIcon *icon;
|
|
|
|
GtkIconInfo *icon_info;
|
|
|
|
gint width;
|
|
|
|
gint height;
|
|
|
|
gpointer user_data;
|
|
|
|
} AsyncIconLookupData;
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
compute_pixbuf_scale (gint width,
|
|
|
|
gint height,
|
|
|
|
gint available_width,
|
|
|
|
gint available_height,
|
|
|
|
gint *new_width,
|
|
|
|
gint *new_height)
|
|
|
|
{
|
|
|
|
int scaled_width, scaled_height;
|
|
|
|
|
|
|
|
if (width == 0 || height == 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (available_width >= 0 && available_height >= 0)
|
|
|
|
{
|
|
|
|
// This should keep the aspect ratio of the image intact, because if
|
|
|
|
// available_width < (available_height * width) / height
|
|
|
|
// than
|
|
|
|
// (available_width * height) / width < available_height
|
|
|
|
// So we are guaranteed to either scale the image to have an available_width
|
|
|
|
// for width and height scaled accordingly OR have the available_height
|
|
|
|
// for height and width scaled accordingly, whichever scaling results
|
|
|
|
// in the image that can fit both available dimensions.
|
|
|
|
scaled_width = MIN (available_width, (available_height * width) / height);
|
|
|
|
scaled_height = MIN (available_height, (available_width * height) / width);
|
|
|
|
}
|
|
|
|
else if (available_width >= 0)
|
|
|
|
{
|
|
|
|
scaled_width = available_width;
|
|
|
|
scaled_height = (available_width * height) / width;
|
|
|
|
}
|
|
|
|
else if (available_height >= 0)
|
|
|
|
{
|
|
|
|
scaled_width = (available_height * width) / height;
|
|
|
|
scaled_height = available_height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
scaled_width = scaled_height = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scale the image only if that will not increase its original dimensions.
|
|
|
|
if (scaled_width >= 0 && scaled_height >= 0 && scaled_width < width && scaled_height < height)
|
|
|
|
{
|
|
|
|
*new_width = scaled_width;
|
|
|
|
*new_height = scaled_height;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GdkPixbuf *
|
|
|
|
impl_load_pixbuf_gicon (GIcon *icon,
|
|
|
|
GtkIconInfo *info,
|
|
|
|
int size,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
int scaled_width, scaled_height;
|
|
|
|
GdkPixbuf *pixbuf = gtk_icon_info_load_icon (info, error);
|
|
|
|
int width, height;
|
|
|
|
|
|
|
|
if (!pixbuf)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
width = gdk_pixbuf_get_width (pixbuf);
|
|
|
|
height = gdk_pixbuf_get_height (pixbuf);
|
|
|
|
|
|
|
|
if (compute_pixbuf_scale (width,
|
|
|
|
height,
|
|
|
|
size, size,
|
|
|
|
&scaled_width, &scaled_height))
|
|
|
|
{
|
|
|
|
GdkPixbuf *scaled = gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR);
|
|
|
|
g_object_unref (pixbuf);
|
|
|
|
pixbuf = scaled;
|
|
|
|
}
|
|
|
|
return pixbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A private structure for keeping width and height.
|
|
|
|
typedef struct {
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
} Dimensions;
|
|
|
|
|
2009-06-16 11:55:14 -04:00
|
|
|
static void
|
|
|
|
icon_lookup_data_destroy (gpointer p)
|
|
|
|
{
|
|
|
|
AsyncIconLookupData *data = p;
|
|
|
|
|
|
|
|
if (data->icon)
|
|
|
|
{
|
|
|
|
g_object_unref (data->icon);
|
|
|
|
gtk_icon_info_free (data->icon_info);
|
|
|
|
}
|
|
|
|
else if (data->uri)
|
|
|
|
g_free (data->uri);
|
|
|
|
|
|
|
|
g_free (data);
|
|
|
|
}
|
|
|
|
|
2009-05-13 14:06:24 -04:00
|
|
|
/**
|
|
|
|
* on_image_size_prepared:
|
|
|
|
*
|
|
|
|
* @pixbuf_loader: #GdkPixbufLoader loading the image
|
|
|
|
* @width: the original width of the image
|
|
|
|
* @height: the original height of the image
|
|
|
|
* @data: pointer to the #Dimensions sructure containing available width and height for the image,
|
|
|
|
* available width or height can be -1 if the dimension is not limited
|
|
|
|
*
|
|
|
|
* Private function.
|
|
|
|
*
|
|
|
|
* Sets the size of the image being loaded to fit the available width and height dimensions,
|
|
|
|
* but never scales up the image beyond its actual size.
|
|
|
|
* Intended to be used as a callback for #GdkPixbufLoader "size-prepared" signal.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
on_image_size_prepared (GdkPixbufLoader *pixbuf_loader,
|
|
|
|
gint width,
|
|
|
|
gint height,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
Dimensions *available_dimensions = data;
|
|
|
|
int available_width = available_dimensions->width;
|
|
|
|
int available_height = available_dimensions->height;
|
|
|
|
int scaled_width;
|
|
|
|
int scaled_height;
|
|
|
|
|
|
|
|
if (compute_pixbuf_scale (width, height, available_width, available_height,
|
|
|
|
&scaled_width, &scaled_height))
|
|
|
|
gdk_pixbuf_loader_set_size (pixbuf_loader, scaled_width, scaled_height);
|
|
|
|
}
|
|
|
|
|
|
|
|
static GdkPixbuf *
|
|
|
|
impl_load_pixbuf_file (const char *uri,
|
|
|
|
int available_width,
|
|
|
|
int available_height,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
GdkPixbufLoader *pixbuf_loader = NULL;
|
|
|
|
GdkPixbuf *rotated_pixbuf = NULL;
|
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
GFile *file = NULL;
|
|
|
|
char *contents = NULL;
|
|
|
|
gsize size;
|
|
|
|
gboolean success;
|
|
|
|
Dimensions available_dimensions;
|
|
|
|
int width_before_rotation, width_after_rotation;
|
|
|
|
|
|
|
|
file = g_file_new_for_uri (uri);
|
|
|
|
|
|
|
|
success = g_file_load_contents (file, NULL, &contents, &size, NULL, error);
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
{
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
pixbuf_loader = gdk_pixbuf_loader_new ();
|
|
|
|
|
|
|
|
available_dimensions.width = available_width;
|
|
|
|
available_dimensions.height = available_height;
|
|
|
|
g_signal_connect (pixbuf_loader, "size-prepared",
|
|
|
|
G_CALLBACK (on_image_size_prepared), &available_dimensions);
|
|
|
|
|
|
|
|
success = gdk_pixbuf_loader_write (pixbuf_loader,
|
|
|
|
(const guchar *) contents,
|
|
|
|
size,
|
|
|
|
error);
|
|
|
|
if (!success)
|
|
|
|
goto out;
|
|
|
|
success = gdk_pixbuf_loader_close (pixbuf_loader, error);
|
|
|
|
if (!success)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
|
|
|
|
|
|
|
|
width_before_rotation = gdk_pixbuf_get_width (pixbuf);
|
|
|
|
|
|
|
|
rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
|
|
|
|
width_after_rotation = gdk_pixbuf_get_width (rotated_pixbuf);
|
|
|
|
|
|
|
|
// There is currently no way to tell if the pixbuf will need to be rotated before it is loaded,
|
|
|
|
// so we only check that once it is loaded, and reload it again if it needs to be rotated in order
|
|
|
|
// to use the available width and height correctly.
|
|
|
|
// http://bugzilla.gnome.org/show_bug.cgi?id=579003
|
|
|
|
if (width_before_rotation != width_after_rotation)
|
|
|
|
{
|
|
|
|
g_object_unref (pixbuf_loader);
|
|
|
|
g_object_unref (rotated_pixbuf);
|
|
|
|
rotated_pixbuf = NULL;
|
|
|
|
|
|
|
|
pixbuf_loader = gdk_pixbuf_loader_new ();
|
|
|
|
|
|
|
|
// We know that the image will later be rotated, so we reverse the available dimensions.
|
|
|
|
available_dimensions.width = available_height;
|
|
|
|
available_dimensions.height = available_width;
|
|
|
|
g_signal_connect (pixbuf_loader, "size-prepared",
|
|
|
|
G_CALLBACK (on_image_size_prepared), &available_dimensions);
|
|
|
|
|
|
|
|
success = gdk_pixbuf_loader_write (pixbuf_loader,
|
|
|
|
(const guchar *) contents,
|
|
|
|
size,
|
|
|
|
error);
|
|
|
|
if (!success)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
success = gdk_pixbuf_loader_close (pixbuf_loader, error);
|
|
|
|
if (!success)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
|
|
|
|
|
|
|
|
rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
g_free (contents);
|
|
|
|
if (file)
|
|
|
|
g_object_unref (file);
|
|
|
|
if (pixbuf_loader)
|
|
|
|
g_object_unref (pixbuf_loader);
|
|
|
|
return rotated_pixbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
load_pixbuf_thread (GSimpleAsyncResult *result,
|
|
|
|
GObject *object,
|
|
|
|
GCancellable *cancellable)
|
|
|
|
{
|
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
AsyncIconLookupData *data;
|
|
|
|
GError *error = NULL;
|
|
|
|
|
2009-06-18 16:44:14 -04:00
|
|
|
data = g_object_get_data (G_OBJECT (result), "load_icon_pixbuf_async");
|
2009-05-13 14:06:24 -04:00
|
|
|
|
|
|
|
if (data->uri)
|
|
|
|
pixbuf = impl_load_pixbuf_file (data->uri, data->width, data->height, &error);
|
|
|
|
else if (data->icon)
|
|
|
|
pixbuf = impl_load_pixbuf_gicon (data->icon, data->icon_info, data->width, &error);
|
|
|
|
else
|
|
|
|
g_assert_not_reached ();
|
|
|
|
|
|
|
|
if (error != NULL)
|
|
|
|
{
|
|
|
|
g_simple_async_result_set_from_error (result, error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_simple_async_result_set_op_res_gpointer (result, g_object_ref (pixbuf),
|
|
|
|
g_object_unref);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* load_icon_pixbuf_async:
|
|
|
|
*
|
|
|
|
* Asynchronously load the #GdkPixbuf associated with a #GIcon. Currently
|
|
|
|
* the #GtkIconInfo must have already been provided.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
load_icon_pixbuf_async (ShellTextureCache *cache,
|
|
|
|
GIcon *icon,
|
|
|
|
GtkIconInfo *icon_info,
|
|
|
|
gint size,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GAsyncReadyCallback callback,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
GSimpleAsyncResult *result;
|
|
|
|
AsyncIconLookupData *data;
|
|
|
|
|
|
|
|
data = g_new0 (AsyncIconLookupData, 1);
|
|
|
|
data->icon = g_object_ref (icon);
|
|
|
|
data->icon_info = gtk_icon_info_copy (icon_info);
|
|
|
|
data->width = data->height = size;
|
|
|
|
data->user_data = user_data;
|
|
|
|
|
|
|
|
result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_icon_pixbuf_async);
|
|
|
|
|
2009-06-18 16:44:14 -04:00
|
|
|
g_object_set_data_full (G_OBJECT (result), "load_icon_pixbuf_async", data, icon_lookup_data_destroy);
|
2009-05-13 14:06:24 -04:00
|
|
|
g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
|
2009-06-16 11:55:14 -04:00
|
|
|
|
|
|
|
g_object_unref (result);
|
2009-05-13 14:06:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
load_uri_pixbuf_async (ShellTextureCache *cache,
|
|
|
|
const char *uri,
|
|
|
|
guint width,
|
|
|
|
guint height,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GAsyncReadyCallback callback,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
GSimpleAsyncResult *result;
|
|
|
|
AsyncIconLookupData *data;
|
|
|
|
|
|
|
|
data = g_new0 (AsyncIconLookupData, 1);
|
|
|
|
data->uri = g_strdup (uri);
|
|
|
|
data->width = width;
|
|
|
|
data->height = height;
|
|
|
|
data->user_data = user_data;
|
|
|
|
|
|
|
|
result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_uri_pixbuf_async);
|
|
|
|
|
2009-06-18 16:44:14 -04:00
|
|
|
g_object_set_data_full (G_OBJECT (result), "load_uri_pixbuf_async", data, icon_lookup_data_destroy);
|
2009-05-13 14:06:24 -04:00
|
|
|
g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
|
2009-06-16 11:55:14 -04:00
|
|
|
|
|
|
|
g_object_unref (result);
|
2009-05-13 14:06:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static GdkPixbuf *
|
|
|
|
load_pixbuf_async_finish (ShellTextureCache *cache, GAsyncResult *result, GError **error)
|
|
|
|
{
|
|
|
|
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
|
|
|
|
if (g_simple_async_result_propagate_error (simple, error))
|
|
|
|
return NULL;
|
|
|
|
return g_simple_async_result_get_op_res_gpointer (simple);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char *uri;
|
|
|
|
GIcon *icon;
|
|
|
|
GtkIconInfo *icon_info;
|
|
|
|
guint width;
|
|
|
|
guint height;
|
|
|
|
ClutterTexture *texture;
|
|
|
|
} AsyncTextureLoadData;
|
|
|
|
|
|
|
|
static CoglHandle
|
|
|
|
pixbuf_to_cogl_handle (GdkPixbuf *pixbuf)
|
|
|
|
{
|
|
|
|
return cogl_texture_new_from_data (gdk_pixbuf_get_width (pixbuf),
|
|
|
|
gdk_pixbuf_get_height (pixbuf),
|
2009-06-06 13:07:41 -04:00
|
|
|
COGL_TEXTURE_NONE,
|
2009-05-13 14:06:24 -04:00
|
|
|
gdk_pixbuf_get_has_alpha (pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
|
|
|
|
COGL_PIXEL_FORMAT_ANY,
|
|
|
|
gdk_pixbuf_get_rowstride (pixbuf),
|
|
|
|
gdk_pixbuf_get_pixels (pixbuf));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_pixbuf_loaded (GObject *source,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
ShellTextureCache *cache;
|
|
|
|
AsyncTextureLoadData *data;
|
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
GError *error = NULL;
|
|
|
|
CoglHandle texdata;
|
|
|
|
CacheKey *key;
|
|
|
|
|
|
|
|
data = user_data;
|
|
|
|
cache = SHELL_TEXTURE_CACHE (source);
|
|
|
|
pixbuf = load_pixbuf_async_finish (cache, result, &error);
|
|
|
|
if (pixbuf == NULL)
|
|
|
|
{
|
|
|
|
/* TODO - we need a "broken image" display of some sort */
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
texdata = pixbuf_to_cogl_handle (pixbuf);
|
|
|
|
|
2009-06-16 11:55:14 -04:00
|
|
|
g_object_unref (pixbuf);
|
|
|
|
|
2009-05-13 14:06:24 -04:00
|
|
|
if (data->icon)
|
|
|
|
{
|
|
|
|
gpointer orig_key, value;
|
|
|
|
|
|
|
|
key = g_new0 (CacheKey, 1);
|
|
|
|
key->icon = g_object_ref (data->icon);
|
|
|
|
key->size = data->width;
|
|
|
|
|
|
|
|
if (!g_hash_table_lookup_extended (cache->priv->gicon_cache, key,
|
|
|
|
&orig_key, &value))
|
|
|
|
g_hash_table_insert (cache->priv->gicon_cache, key,
|
|
|
|
texdata);
|
|
|
|
else
|
|
|
|
cache_key_destroy (key);
|
|
|
|
}
|
|
|
|
|
|
|
|
clutter_texture_set_cogl_texture (data->texture, texdata);
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (data->icon)
|
|
|
|
{
|
|
|
|
gtk_icon_info_free (data->icon_info);
|
|
|
|
g_object_unref (data->icon);
|
|
|
|
}
|
|
|
|
else if (data->uri)
|
|
|
|
g_free (data->uri);
|
|
|
|
/* Alternatively we could weakref and just do nothing if the texture
|
|
|
|
is destroyed */
|
|
|
|
g_object_unref (data->texture);
|
|
|
|
|
|
|
|
g_free (data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_texture_cache_load_gicon:
|
|
|
|
*
|
|
|
|
* This method returns a new #ClutterClone for a given #GIcon. If the
|
|
|
|
* icon isn't loaded already, the texture will be filled asynchronously.
|
|
|
|
*
|
|
|
|
* Return Value: (transfer none): A new #ClutterActor for the icon
|
|
|
|
*/
|
|
|
|
ClutterActor *
|
|
|
|
shell_texture_cache_load_gicon (ShellTextureCache *cache,
|
|
|
|
GIcon *icon,
|
|
|
|
gint size)
|
|
|
|
{
|
|
|
|
ClutterTexture *texture;
|
|
|
|
CoglHandle texdata;
|
|
|
|
CacheKey key;
|
|
|
|
|
|
|
|
texture = CLUTTER_TEXTURE (clutter_texture_new ());
|
|
|
|
clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
|
|
|
|
|
|
|
|
key.icon = icon;
|
|
|
|
key.size = size;
|
|
|
|
texdata = g_hash_table_lookup (cache->priv->gicon_cache, &key);
|
|
|
|
|
|
|
|
if (texdata == NULL)
|
|
|
|
{
|
|
|
|
GtkIconTheme *theme;
|
|
|
|
GtkIconInfo *info;
|
|
|
|
|
|
|
|
/* Do theme lookups in the main thread to avoid thread-unsafety */
|
|
|
|
theme = gtk_icon_theme_get_default ();
|
|
|
|
|
|
|
|
info = gtk_icon_theme_lookup_by_gicon (theme, icon, size, GTK_ICON_LOOKUP_USE_BUILTIN);
|
|
|
|
if (info != NULL)
|
|
|
|
{
|
|
|
|
AsyncTextureLoadData *data;
|
|
|
|
data = g_new0 (AsyncTextureLoadData, 1);
|
|
|
|
|
2009-06-18 16:44:14 -04:00
|
|
|
data->icon = g_object_ref (icon);
|
2009-05-13 14:06:24 -04:00
|
|
|
data->icon_info = info;
|
|
|
|
data->texture = g_object_ref (texture);
|
2009-05-21 18:49:00 -04:00
|
|
|
data->width = data->height = size;
|
2009-05-13 14:06:24 -04:00
|
|
|
load_icon_pixbuf_async (cache, icon, info, size, NULL, on_pixbuf_loaded, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
clutter_texture_set_cogl_texture (texture, texdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
return CLUTTER_ACTOR (texture);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_texture_cache_load_uri:
|
|
|
|
*
|
|
|
|
* @cache: The texture cache instance
|
|
|
|
* @uri: uri of the image file from which to create a pixbuf
|
|
|
|
* @available_width: available width for the image, can be -1 if not limited
|
|
|
|
* @available_height: available height for the image, can be -1 if not limited
|
|
|
|
*
|
|
|
|
* Asynchronously load an image. Initially, the returned texture will have a natural
|
|
|
|
* size of zero. At some later point, either the image will be loaded successfully
|
|
|
|
* and at that point size will be negotiated, or upon an error, no image will be set.
|
|
|
|
*
|
|
|
|
* Return value: (transfer none): A new #ClutterActor with no image loaded initially.
|
|
|
|
*/
|
|
|
|
ClutterActor *
|
|
|
|
shell_texture_cache_load_uri_async (ShellTextureCache *cache,
|
|
|
|
const gchar *uri,
|
|
|
|
int available_width,
|
|
|
|
int available_height)
|
|
|
|
{
|
|
|
|
ClutterTexture *texture;
|
|
|
|
AsyncTextureLoadData *data;
|
|
|
|
|
|
|
|
texture = CLUTTER_TEXTURE (clutter_texture_new ());
|
|
|
|
|
|
|
|
data = g_new0 (AsyncTextureLoadData, 1);
|
|
|
|
data->uri = g_strdup (uri);
|
|
|
|
data->width = available_width;
|
|
|
|
data->height = available_height;
|
|
|
|
data->texture = g_object_ref (texture);
|
|
|
|
load_uri_pixbuf_async (cache, uri, available_width, available_height, NULL, on_pixbuf_loaded, data);
|
|
|
|
|
|
|
|
return CLUTTER_ACTOR (texture);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_texture_cache_load_uri_sync:
|
|
|
|
*
|
|
|
|
* @cache: The texture cache instance
|
|
|
|
* @uri: uri of the image file from which to create a pixbuf
|
|
|
|
* @available_width: available width for the image, can be -1 if not limited
|
|
|
|
* @available_height: available height for the image, can be -1 if not limited
|
|
|
|
* @error: Return location for error
|
|
|
|
*
|
|
|
|
* Synchronously load an image from a uri. The image is scaled down to fit the
|
|
|
|
* available width and height imensions, but the image is never scaled up beyond
|
|
|
|
* its actual size. The pixbuf is rotated according to the associated orientation
|
|
|
|
* setting.
|
|
|
|
*
|
|
|
|
* Return value: (transfer none): A new #ClutterActor with the image file loaded if it was
|
|
|
|
* generated succesfully, %NULL otherwise
|
|
|
|
*/
|
|
|
|
ClutterActor *
|
|
|
|
shell_texture_cache_load_uri_sync (ShellTextureCache *cache,
|
|
|
|
const gchar *uri,
|
|
|
|
int available_width,
|
|
|
|
int available_height,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
ClutterTexture *texture;
|
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
CoglHandle texdata;
|
|
|
|
|
|
|
|
pixbuf = impl_load_pixbuf_file (uri, available_width, available_height, error);
|
|
|
|
if (!pixbuf)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
texture = CLUTTER_TEXTURE (clutter_texture_new ());
|
|
|
|
texdata = pixbuf_to_cogl_handle (pixbuf);
|
2009-06-16 11:55:14 -04:00
|
|
|
g_object_unref (pixbuf);
|
2009-05-13 14:06:24 -04:00
|
|
|
clutter_texture_set_cogl_texture (texture, texdata);
|
|
|
|
|
|
|
|
return CLUTTER_ACTOR (texture);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ShellTextureCache *instance = NULL;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_texture_cache_get_default:
|
|
|
|
*
|
|
|
|
* Return value: (transfer none): The global texture cache
|
|
|
|
*/
|
|
|
|
ShellTextureCache*
|
|
|
|
shell_texture_cache_get_default ()
|
|
|
|
{
|
|
|
|
if (instance == NULL)
|
|
|
|
instance = g_object_new (SHELL_TYPE_TEXTURE_CACHE, NULL);
|
|
|
|
return instance;
|
|
|
|
}
|