コード例 #1
0
/**
 * st_texture_cache_load_icon_name:
 * @cache: The texture cache instance
 * @theme_node: (allow-none): a #StThemeNode
 * @name: Name of a themed icon
 * @icon_type: the type of icon to load
 * @size: Size of themed icon
 *
 * Load a themed icon into a texture. See the #StIconType documentation
 * for an explanation of how @icon_type affects the returned icon. The
 * colors used for symbolic icons are derived from @theme_node.
 *
 * Return Value: (transfer none): A new #ClutterTexture for the icon
 */
ClutterActor *
st_texture_cache_load_icon_name (StTextureCache    *cache,
                                 StThemeNode       *theme_node,
                                 const char        *name,
                                 StIconType         icon_type,
                                 gint               size)
{
  ClutterActor *texture;
  GIcon *themed;
  char **names;

  g_return_val_if_fail (!(icon_type == ST_ICON_SYMBOLIC && theme_node == NULL), NULL);

  switch (icon_type)
    {
    case ST_ICON_SYMBOLIC:
      names = symbolic_names_for_icon (name);
      themed = g_themed_icon_new_from_names (names, -1);
      g_strfreev (names);
      texture = load_gicon_with_colors (cache, themed, size,
                                        st_theme_node_get_icon_colors (theme_node));
      g_object_unref (themed);
      if (texture == NULL)
        {
          /* We don't have an equivalent of image-missing
           * for the symbolic icon theme, so just create a blank
           * actor. */
          texture = (ClutterActor *) create_default_texture ();
          clutter_actor_set_size (texture, size, size);
        }

      return texture;
      break;
    case ST_ICON_FULLCOLOR:
      themed = g_themed_icon_new_with_default_fallbacks (name);
      texture = load_gicon_with_colors (cache, themed, size, NULL);
      g_object_unref (themed);
      if (texture == NULL)
        {
          themed = g_themed_icon_new ("image-missing");
          texture = load_gicon_with_colors (cache, themed, size, NULL);
          g_object_unref (themed);
        }
      return texture;
      break;
    default:
      g_assert_not_reached ();
    }
}
コード例 #2
0
/**
 * st_texture_cache_load_thumbnail:
 * @cache:
 * @size: Size in pixels to use for thumbnail
 * @uri: Source URI
 * @mimetype: Source mime type
 *
 * Asynchronously load a thumbnail image of a URI 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_thumbnail().
 *
 * Returns: (transfer none): A new #ClutterActor
 */
ClutterActor *
st_texture_cache_load_thumbnail (StTextureCache    *cache,
                                 int                size,
                                 const char        *uri,
                                 const char        *mimetype)
{
  ClutterTexture *texture;
  AsyncTextureLoadData *data;
  char *key;
  CoglHandle texdata;

  /* Don't attempt to load thumbnails for non-local URIs */
  if (!g_str_has_prefix (uri, "file://"))
    {
      GIcon *icon = icon_for_mimetype (mimetype);
      return st_texture_cache_load_gicon (cache, icon, size);
    }

  texture = create_default_texture (cache);
  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->uri = g_strdup (uri);
      data->mimetype = g_strdup (mimetype);
      data->thumbnail = TRUE;
      data->width = size;
      data->height = size;
      data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
      load_thumbnail_async (cache, uri, mimetype, size, NULL, on_pixbuf_loaded, data);
    }
  else
    {
      set_texture_cogl_texture (texture, texdata);
    }

  g_free (key);
  return CLUTTER_ACTOR (texture);
}
コード例 #3
0
/**
 * 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);
}
コード例 #4
0
ファイル: st-texture-cache.c プロジェクト: joequant/Cinnamon
/**
 * st_texture_cache_load_uri_async:
 * @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 *
st_texture_cache_load_uri_async (StTextureCache *cache,
                                 const gchar    *uri,
                                 int             available_width,
                                 int             available_height)
{
  ClutterActor *texture;
  AsyncTextureLoadData *request;
  StTextureCachePolicy policy;
  gchar *key;

  int width = available_width == -1 ? -1 : available_width * cache->priv->scale;
  int height = available_height == -1 ? -1 : available_height * cache->priv->scale;

  key = g_strconcat (CACHE_PREFIX_URI, uri, NULL);

  policy = ST_TEXTURE_CACHE_POLICY_NONE; /* XXX */

  texture = (ClutterActor *) create_default_texture ();

  if (ensure_request (cache, key, policy, &request, texture))
    {
      /* If there's an outstanding request, we've just added ourselves to it */
      free (key);
    }
  else
    {
      /* Else, make a new request */

      request->cache = cache;
      /* Transfer ownership of key */
      request->key = key;
      request->uri = g_strdup (uri);
      request->policy = policy;
      request->width = width;
      request->height = height;

      load_texture_async (cache, request);
    }

  ensure_monitor_for_uri (cache, uri);

  return CLUTTER_ACTOR (texture);
}
コード例 #5
0
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);
}
コード例 #6
0
/**
 * st_texture_cache_load_file_async:
 * @cache: The texture cache instance
 * @file: a #GFile 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
 * @scale: scale factor of the display
 *
 * 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 *
st_texture_cache_load_file_async (StTextureCache *cache,
                                  GFile          *file,
                                  int             available_width,
                                  int             available_height,
                                  int             scale)
{
  ClutterActor *texture;
  AsyncTextureLoadData *request;
  StTextureCachePolicy policy;
  gchar *key;

  key = g_strdup_printf (CACHE_PREFIX_FILE "%u", g_file_hash (file));

  policy = ST_TEXTURE_CACHE_POLICY_NONE; /* XXX */

  texture = (ClutterActor *) create_default_texture ();

  if (ensure_request (cache, key, policy, &request, texture))
    {
      /* If there's an outstanding request, we've just added ourselves to it */
      g_free (key);
    }
  else
    {
      /* Else, make a new request */

      request->cache = cache;
      /* Transfer ownership of key */
      request->key = key;
      request->file = g_object_ref (file);
      request->policy = policy;
      request->width = available_width;
      request->height = available_height;
      request->scale = scale;

      load_texture_async (cache, request);
    }

  ensure_monitor_for_file (cache, file);

  return CLUTTER_ACTOR (texture);
}
コード例 #7
0
/**
 * 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);
}
コード例 #8
0
/**
 * 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;
}
コード例 #9
0
/**
 * 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);
}
コード例 #10
0
ファイル: st-texture-cache.c プロジェクト: joequant/Cinnamon
/**
 * 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;
}
コード例 #11
0
/**
 * 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);
}
コード例 #12
0
/**
 * st_texture_cache_load_uri_async:
 *
 * @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 *
st_texture_cache_load_uri_async (StTextureCache *cache,
                                 const gchar    *uri,
                                 int             available_width,
                                 int             available_height)
{
  ClutterTexture *texture;
  AsyncTextureLoadData *data;

  texture = create_default_texture (cache);

  data = g_new0 (AsyncTextureLoadData, 1);
  data->key = g_strconcat (CACHE_PREFIX_URI, uri, NULL);
  data->policy = ST_TEXTURE_CACHE_POLICY_NONE;
  data->uri = g_strdup (uri);
  data->width = available_width;
  data->height = available_height;
  data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
  load_uri_pixbuf_async (cache, uri, available_width, available_height, NULL, on_pixbuf_loaded, data);

  return CLUTTER_ACTOR (texture);
}
コード例 #13
0
ファイル: st-texture-cache.c プロジェクト: joequant/Cinnamon
/**
 * 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);
}
コード例 #14
0
/**
 * st_texture_cache_load_gicon:
 * @cache: The texture cache instance
 * @theme_node: (nullable): The #StThemeNode to use for colors, or NULL
 *                            if the icon must not be recolored
 * @icon: the #GIcon to load
 * @size: Size of themed
 * @scale: Scale factor of display
 *
 * This method returns a new #ClutterActor 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, or %NULL if not found
 */
ClutterActor *
st_texture_cache_load_gicon (StTextureCache    *cache,
                             StThemeNode       *theme_node,
                             GIcon             *icon,
                             gint               size,
                             gint               scale)
{
  AsyncTextureLoadData *request;
  ClutterActor *texture;
  char *gicon_string;
  char *key;
  GtkIconTheme *theme;
  GtkIconInfo *info;
  StTextureCachePolicy policy;
  StIconColors *colors = NULL;
  StIconStyle icon_style = ST_ICON_STYLE_REQUESTED;
  GtkIconLookupFlags lookup_flags;

  if (theme_node)
    {
      colors = st_theme_node_get_icon_colors (theme_node);
      icon_style = st_theme_node_get_icon_style (theme_node);
    }

  /* Do theme lookups in the main thread to avoid thread-unsafety */
  theme = cache->priv->icon_theme;

  lookup_flags = GTK_ICON_LOOKUP_USE_BUILTIN;

  if (icon_style == ST_ICON_STYLE_REGULAR)
    lookup_flags |= GTK_ICON_LOOKUP_FORCE_REGULAR;
  else if (icon_style == ST_ICON_STYLE_SYMBOLIC)
    lookup_flags |= GTK_ICON_LOOKUP_FORCE_SYMBOLIC;

  if (clutter_get_default_text_direction () == CLUTTER_TEXT_DIRECTION_RTL)
    lookup_flags |= GTK_ICON_LOOKUP_DIR_RTL;
  else
    lookup_flags |= GTK_ICON_LOOKUP_DIR_LTR;

  info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, size, scale, lookup_flags);
  if (info == NULL)
    return NULL;

  gicon_string = g_icon_to_string (icon);
  /* A return value of NULL indicates that the icon can not be serialized,
   * so don't have a unique identifier for it as a cache key, and thus can't
   * be cached. If it is cachable, we hardcode a policy of FOREVER here for
   * now; we should actually blow this away on icon theme changes probably */
  policy = gicon_string != NULL ? ST_TEXTURE_CACHE_POLICY_FOREVER
                                : ST_TEXTURE_CACHE_POLICY_NONE;
  if (colors)
    {
      /* This raises some doubts about the practice of using string keys */
      key = g_strdup_printf (CACHE_PREFIX_ICON "%s,size=%d,scale=%d,style=%d,colors=%2x%2x%2x%2x,%2x%2x%2x%2x,%2x%2x%2x%2x,%2x%2x%2x%2x",
                             gicon_string, size, scale, icon_style,
                             colors->foreground.red, colors->foreground.blue, colors->foreground.green, colors->foreground.alpha,
                             colors->warning.red, colors->warning.blue, colors->warning.green, colors->warning.alpha,
                             colors->error.red, colors->error.blue, colors->error.green, colors->error.alpha,
                             colors->success.red, colors->success.blue, colors->success.green, colors->success.alpha);
    }
  else
    {
      key = g_strdup_printf (CACHE_PREFIX_ICON "%s,size=%d,scale=%d,style=%d",
                             gicon_string, size, scale, icon_style);
    }
  g_free (gicon_string);

  texture = (ClutterActor *) create_default_texture ();
  clutter_actor_set_size (texture, size * scale, size * scale);

  if (ensure_request (cache, key, policy, &request, texture))
    {
      /* If there's an outstanding request, we've just added ourselves to it */
      g_object_unref (info);
      g_free (key);
    }
  else
    {
      /* Else, make a new request */

      request->cache = cache;
      /* Transfer ownership of key */
      request->key = key;
      request->policy = policy;
      request->colors = colors ? st_icon_colors_ref (colors) : NULL;
      request->icon_info = info;
      request->width = request->height = size;
      request->scale = scale;

      load_texture_async (cache, request);
    }

  return CLUTTER_ACTOR (texture);
}