/** * st_texture_cache_load_from_data: * @cache: The texture cache instance * @data: Image data in PNG, GIF, etc format * @len: length of @data * @size: Size in pixels to use for the resulting texture * @error: Return location for error * * Synchronously creates an image from @data. The image is scaled down * to fit the available width and height dimensions, 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 data loaded if it was * generated succesfully, %NULL otherwise */ ClutterActor * st_texture_cache_load_from_data (StTextureCache *cache, const guchar *data, gsize len, int size, GError **error) { ClutterTexture *texture; CoglHandle texdata; GdkPixbuf *pixbuf; char *key; char *checksum; texture = create_default_texture (cache); clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size); checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len); key = g_strdup_printf (CACHE_PREFIX_COMPRESSED_CHECKSUM "checksum=%s,size=%d", checksum, size); g_free (checksum); texdata = g_hash_table_lookup (cache->priv->keyed_cache, key); if (texdata == NULL) { pixbuf = impl_load_pixbuf_data (data, len, size, size, error); if (!pixbuf) { g_object_unref (texture); g_free (key); return NULL; } texdata = pixbuf_to_cogl_handle (pixbuf); g_object_unref (pixbuf); set_texture_cogl_texture (texture, texdata); g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), texdata); } g_free (key); set_texture_cogl_texture (texture, texdata); return CLUTTER_ACTOR (texture); }
static void on_pixbuf_loaded (GObject *source, GAsyncResult *result, gpointer user_data) { GSList *iter; StTextureCache *cache; AsyncTextureLoadData *data; GdkPixbuf *pixbuf; GError *error = NULL; CoglHandle texdata = NULL; data = user_data; cache = ST_TEXTURE_CACHE (source); g_hash_table_remove (cache->priv->outstanding_requests, data->key); pixbuf = load_pixbuf_async_finish (cache, result, &error); if (pixbuf == NULL) goto out; texdata = pixbuf_to_cogl_handle (pixbuf, data->enforced_square); g_object_unref (pixbuf); if (data->policy != ST_TEXTURE_CACHE_POLICY_NONE) { gpointer orig_key, value; if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, data->key, &orig_key, &value)) { cogl_handle_ref (texdata); g_hash_table_insert (cache->priv->keyed_cache, g_strdup (data->key), texdata); } } for (iter = data->textures; iter; iter = iter->next) { ClutterTexture *texture = iter->data; set_texture_cogl_texture (texture, texdata); } out: if (texdata) cogl_handle_unref (texdata); texture_load_data_destroy (data); g_free (data); g_clear_error (&error); }
/** * st_texture_cache_load_recent_thumbnail: * @cache: * @size: Size in pixels to use for thumbnail * @info: Recent item info * * Asynchronously load a thumbnail image of a #GtkRecentInfo into a texture. The * returned texture object will be a new instance; however, its texture data * may be shared with other objects. This implies the texture data is cached. * * The current caching policy is permanent; to uncache, you must explicitly * call st_texture_cache_unref_recent_thumbnail(). * * Returns: (transfer none): A new #ClutterActor */ ClutterActor * st_texture_cache_load_recent_thumbnail (StTextureCache *cache, int size, GtkRecentInfo *info) { ClutterTexture *texture; AsyncTextureLoadData *data; char *key; CoglHandle texdata; const char *uri; uri = gtk_recent_info_get_uri (info); /* Don't attempt to load thumbnails for non-local URIs */ if (!g_str_has_prefix (uri, "file://")) { GIcon *icon = icon_for_recent (info); return st_texture_cache_load_gicon (cache, icon, size); } texture = CLUTTER_TEXTURE (clutter_texture_new ()); clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size); key = g_strdup_printf (CACHE_PREFIX_THUMBNAIL_URI "uri=%s,size=%d", uri, size); texdata = g_hash_table_lookup (cache->priv->keyed_cache, key); if (!texdata) { data = g_new0 (AsyncTextureLoadData, 1); data->key = g_strdup (key); data->policy = ST_TEXTURE_CACHE_POLICY_FOREVER; data->thumbnail = TRUE; data->recent_info = gtk_recent_info_ref (info); data->width = size; data->height = size; data->textures = g_slist_prepend (data->textures, g_object_ref (texture)); load_recent_thumbnail_async (cache, info, size, NULL, on_pixbuf_loaded, data); } else { set_texture_cogl_texture (texture, texdata); } g_free (key); return CLUTTER_ACTOR (texture); }
static void finish_texture_load (AsyncTextureLoadData *data, GdkPixbuf *pixbuf) { GSList *iter; StTextureCache *cache; CoglTexture *texdata = NULL; cache = data->cache; g_hash_table_remove (cache->priv->outstanding_requests, data->key); if (pixbuf == NULL) goto out; texdata = pixbuf_to_cogl_texture (pixbuf); if (!texdata) goto out; if (data->policy != ST_TEXTURE_CACHE_POLICY_NONE) { gpointer orig_key, value; if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, data->key, &orig_key, &value)) { cogl_object_ref (texdata); g_hash_table_insert (cache->priv->keyed_cache, g_strdup (data->key), texdata); } } for (iter = data->textures; iter; iter = iter->next) { ClutterTexture *texture = iter->data; set_texture_cogl_texture (texture, texdata); } out: if (texdata) cogl_object_unref (texdata); texture_load_data_free (data); }
static ClutterActor * load_from_pixbuf (GdkPixbuf *pixbuf) { ClutterTexture *texture; CoglTexture *texdata; int width = gdk_pixbuf_get_width (pixbuf); int height = gdk_pixbuf_get_height (pixbuf); texture = create_default_texture (); clutter_actor_set_size (CLUTTER_ACTOR (texture), width, height); texdata = pixbuf_to_cogl_texture (pixbuf); set_texture_cogl_texture (texture, texdata); cogl_object_unref (texdata); return CLUTTER_ACTOR (texture); }
/** * st_texture_cache_load_from_raw: * @cache: a #StTextureCache * @data: (array length=len): raw pixel data * @len: the length of @data * @has_alpha: whether @data includes an alpha channel * @width: width in pixels of @data * @height: width in pixels of @data * @rowstride: rowstride of @data * @size: size of icon to return * * Creates (or retrieves from cache) an icon based on raw pixel data. * * Return value: (transfer none): a new #ClutterActor displaying a * pixbuf created from @data and the other parameters. **/ ClutterActor * st_texture_cache_load_from_raw (StTextureCache *cache, const guchar *data, gsize len, gboolean has_alpha, int width, int height, int rowstride, int size, GError **error) { ClutterTexture *texture; CoglHandle texdata; char *key; char *checksum; texture = create_default_texture (); clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size); /* In theory, two images of with different width and height could have the same * pixel data and thus hash the same. (Say, a 16x16 and a 8x32 blank image.) * We ignore this for now. If anybody hits this problem they should use * GChecksum directly to compute a checksum including the width and height. */ checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len); key = g_strdup_printf (CACHE_PREFIX_RAW_CHECKSUM "checksum=%s", checksum); g_free (checksum); texdata = g_hash_table_lookup (cache->priv->keyed_cache, key); if (texdata == NULL) { texdata = cogl_texture_new_from_data (width, height, COGL_TEXTURE_NONE, has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, COGL_PIXEL_FORMAT_ANY, rowstride, data); g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), texdata); } g_free (key); set_texture_cogl_texture (texture, texdata); return CLUTTER_ACTOR (texture); }
/** * create_texture_and_ensure_request: * @cache: * @key: A cache key * @size: Size in pixels * @request: (out): If no request is outstanding, one will be created and returned here * @texture: (out): A new texture, also added to the request * * Check for any outstanding load for the data represented by @key. If there * is already a request pending, append it to that request to avoid loading * the data multiple times. * * Returns: %TRUE iff there is already a request pending */ static gboolean create_texture_and_ensure_request (StTextureCache *cache, const char *key, guint size, AsyncTextureLoadData **request, ClutterActor **texture) { CoglHandle texdata; AsyncTextureLoadData *pending; gboolean had_pending; *texture = (ClutterActor *) create_default_texture (cache); clutter_actor_set_size (*texture, size, size); texdata = g_hash_table_lookup (cache->priv->keyed_cache, key); if (texdata != NULL) { /* We had this cached already, just set the texture and we're done. */ set_texture_cogl_texture (CLUTTER_TEXTURE (*texture), texdata); return TRUE; } pending = g_hash_table_lookup (cache->priv->outstanding_requests, key); had_pending = pending != NULL; if (pending == NULL) { /* Not cached and no pending request, create it */ *request = g_new0 (AsyncTextureLoadData, 1); (*request)->key = g_strdup (key); g_hash_table_insert (cache->priv->outstanding_requests, g_strdup (key), *request); } else *request = pending; /* Regardless of whether there was a pending request, prepend our texture here. */ (*request)->textures = g_slist_prepend ((*request)->textures, g_object_ref (*texture)); return had_pending; }
/** * st_texture_cache_load_from_raw: * @cache: a #StTextureCache * @data: raw pixel data * @len: the length of @data * @has_alpha: whether @data includes an alpha channel * @width: width in pixels of @data * @height: width in pixels of @data * @rowstride: rowstride of @data * @size: size of icon to return * * Creates (or retrieves from cache) an icon based on raw pixel data. * * Return value: (transfer none): a new #ClutterActor displaying a * pixbuf created from @data and the other parameters. **/ ClutterActor * st_texture_cache_load_from_raw (StTextureCache *cache, const guchar *data, gsize len, gboolean has_alpha, int width, int height, int rowstride, int size, GError **error) { ClutterTexture *texture; CoglHandle texdata; char *key; char *checksum; texture = create_default_texture (cache); clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size); /* In theory, two images of different size could have the same * pixel data. We ignore that theory. */ checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len); key = g_strdup_printf (CACHE_PREFIX_RAW_CHECKSUM "checksum=%s,size=%d", checksum, size); g_free (checksum); texdata = g_hash_table_lookup (cache->priv->keyed_cache, key); if (texdata == NULL) { texdata = cogl_texture_new_from_data (width, height, COGL_TEXTURE_NONE, has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, COGL_PIXEL_FORMAT_ANY, rowstride, data); g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), texdata); } g_free (key); set_texture_cogl_texture (texture, texdata); return CLUTTER_ACTOR (texture); }
/** * st_texture_cache_load_from_pixbuf: * @pixbuf: A #GdkPixbuf * @size: int * * Converts a #GdkPixbuf into a #ClutterTexture. * * Return value: (transfer none): A new #ClutterActor */ ClutterActor * st_texture_cache_load_from_pixbuf (GdkPixbuf *pixbuf, int size) { ClutterTexture *texture; CoglTexture *texdata; ClutterActor *actor; texture = create_default_texture (); actor = CLUTTER_ACTOR (texture); clutter_actor_set_size (actor, size, size); texdata = pixbuf_to_cogl_texture (pixbuf); set_texture_cogl_texture (texture, texdata); cogl_object_unref (texdata); return actor; }
/** * st_texture_cache_load_uri_sync: * * @cache: The texture cache instance * @policy: Requested lifecycle of cached data * @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 * st_texture_cache_load_uri_sync (StTextureCache *cache, StTextureCachePolicy policy, const gchar *uri, int available_width, int available_height, GError **error) { CoglHandle texdata; ClutterTexture *texture; texdata = st_texture_cache_load_uri_sync_to_cogl_texture (cache, policy, uri, available_width, available_height, error); if (texdata == COGL_INVALID_HANDLE) return NULL; texture = create_default_texture (cache); set_texture_cogl_texture (texture, texdata); cogl_handle_unref (texdata); return CLUTTER_ACTOR (texture); }
/** * st_texture_cache_load_from_raw: * @cache: a #StTextureCache * @data: (array length=len): raw pixel data * @len: the length of @data * @has_alpha: whether @data includes an alpha channel * @width: width in pixels of @data * @height: width in pixels of @data * @rowstride: rowstride of @data * @size: size of icon to return * * Creates (or retrieves from cache) an icon based on raw pixel data. * * Return value: (transfer none): a new #ClutterActor displaying a * pixbuf created from @data and the other parameters. **/ ClutterActor * st_texture_cache_load_from_raw (StTextureCache *cache, const guchar *data, gsize len, gboolean has_alpha, int width, int height, int rowstride, int size, GError **error) { ClutterTexture *texture; CoglTexture *texdata; texture = create_default_texture (); clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size); texdata = data_to_cogl_texture (data, has_alpha, width, height, rowstride); set_texture_cogl_texture (texture, texdata); return CLUTTER_ACTOR (texture); }
/** * ensure_request: * @cache: * @key: A cache key * @policy: Cache policy * @request: (out): If no request is outstanding, one will be created and returned here * @texture: A texture to be added to the request * * Check for any outstanding load for the data represented by @key. If there * is already a request pending, append it to that request to avoid loading * the data multiple times. * * Returns: %TRUE iff there is already a request pending */ static gboolean ensure_request (StTextureCache *cache, const char *key, StTextureCachePolicy policy, AsyncTextureLoadData **request, ClutterActor *texture) { CoglTexture *texdata; AsyncTextureLoadData *pending; gboolean had_pending; texdata = g_hash_table_lookup (cache->priv->keyed_cache, key); if (texdata != NULL) { /* We had this cached already, just set the texture and we're done. */ set_texture_cogl_texture (CLUTTER_TEXTURE (texture), texdata); return TRUE; } pending = g_hash_table_lookup (cache->priv->outstanding_requests, key); had_pending = pending != NULL; if (pending == NULL) { /* Not cached and no pending request, create it */ *request = g_new0 (AsyncTextureLoadData, 1); if (policy != ST_TEXTURE_CACHE_POLICY_NONE) g_hash_table_insert (cache->priv->outstanding_requests, g_strdup (key), *request); } else *request = pending; /* Regardless of whether there was a pending request, prepend our texture here. */ (*request)->textures = g_slist_prepend ((*request)->textures, g_object_ref (texture)); return had_pending; }
static void on_pixbuf_loaded (GObject *source, GAsyncResult *result, gpointer user_data) { GSList *iter; StTextureCache *cache; AsyncTextureLoadData *data; GdkPixbuf *pixbuf; GError *error = NULL; CoglHandle texdata = NULL; data = user_data; cache = ST_TEXTURE_CACHE (source); g_hash_table_remove (cache->priv->outstanding_requests, data->key); pixbuf = load_pixbuf_async_finish (cache, result, &error); if (pixbuf == NULL) pixbuf = load_pixbuf_fallback (data); if (pixbuf == NULL) goto out; texdata = pixbuf_to_cogl_handle (pixbuf); g_object_unref (pixbuf); if (data->policy != ST_TEXTURE_CACHE_POLICY_NONE) { gpointer orig_key, value; if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, data->key, &orig_key, &value)) { cogl_handle_ref (texdata); g_hash_table_insert (cache->priv->keyed_cache, g_strdup (data->key), texdata); } } for (iter = data->textures; iter; iter = iter->next) { ClutterTexture *texture = iter->data; set_texture_cogl_texture (texture, texdata); } out: if (texdata) cogl_handle_unref (texdata); g_free (data->key); if (data->icon) { gtk_icon_info_free (data->icon_info); g_object_unref (data->icon); } else if (data->uri) g_free (data->uri); if (data->recent_info) gtk_recent_info_unref (data->recent_info); if (data->mimetype) g_free (data->mimetype); /* Alternatively we could weakref and just do nothing if the texture is destroyed */ for (iter = data->textures; iter; iter = iter->next) { ClutterTexture *texture = iter->data; g_object_unref (texture); } g_clear_error (&error); g_free (data); }