[gtk+/wip/window-scales: 107/108] icontheme: Add support for high resolution icons



commit 6cb020cc3d8b38eabc2a8dba8b4c010a30d427dd
Author: Carlos Garnacho <carlos lanedo com>
Date:   Fri May 10 18:06:00 2013 +0200

    icontheme: Add support for high resolution icons
    
    An optional OutputScale integer key has been added to index.theme
    subdirs description, so icon themes may provide icons that are
    more suitable to render at a (typically 2x) integer upscaled
    resolution. This way it is possible to make eg. a 16x16 2x icon has a
    real size of 32x32, but contains a similar level of detail to the
    16x16 icon so things don't look any more cluttered on high-dpi
    screens.
    
    The pixbuf lookup has changed so it prefers a minimal scale change
    that yields the minimal real size difference, so if looking up for
    a 16x16 icon at 2x, it would first prefer 16x16 2x, then 32x32, and
    then any other icon that's closest to match
    
    There is now *_for_scale() variants for all GtkIconTheme ways
    to directly or indirectly fetch a GdkPixbuf.
    
    This is based on code by Carlos Garnacho with changes by Alexander
    Larsson

 gtk/gtkicontheme.c |  351 +++++++++++++++++++++++++++++++++++++++++++---------
 gtk/gtkicontheme.h |   28 ++++
 2 files changed, 320 insertions(+), 59 deletions(-)
---
diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c
index 9a8e4b3..62839a9 100644
--- a/gtk/gtkicontheme.c
+++ b/gtk/gtkicontheme.c
@@ -253,11 +253,13 @@ struct _GtkIconInfo
    */
   IconThemeDirType dir_type;
   gint dir_size;
+  gint dir_scale;
   gint threshold;
 
   /* Parameters influencing the scaled icon
    */
   gint desired_size;
+  gint desired_scale;
   guint raw_coordinates : 1;
   guint forced_size     : 1;
   guint emblems_applied : 1;
@@ -295,6 +297,7 @@ typedef struct
   int min_size;
   int max_size;
   int threshold;
+  int scale;
 
   char *dir;
   char *subdir;
@@ -333,6 +336,7 @@ static void         theme_destroy     (IconTheme        *theme);
 static GtkIconInfo *theme_lookup_icon (IconTheme        *theme,
                                       const char       *icon_name,
                                       int               size,
+                                       gint              scale,
                                       gboolean          allow_svg,
                                       gboolean          use_default_icons);
 static void         theme_list_icons  (IconTheme        *theme,
@@ -1365,11 +1369,11 @@ _gtk_icon_theme_ensure_builtin_cache (void)
   IconThemeDir *dir;
   static IconThemeDir dirs[5] = 
     {
-      { ICON_THEME_DIR_THRESHOLD, 0, 16, 16, 16, 2, NULL, "16", -1, NULL, NULL, NULL },
-      { ICON_THEME_DIR_THRESHOLD, 0, 20, 20, 20, 2, NULL, "20", -1,  NULL, NULL, NULL },
-      { ICON_THEME_DIR_THRESHOLD, 0, 24, 24, 24, 2, NULL, "24", -1, NULL, NULL, NULL },
-      { ICON_THEME_DIR_THRESHOLD, 0, 32, 32, 32, 2, NULL, "32", -1, NULL, NULL, NULL },
-      { ICON_THEME_DIR_THRESHOLD, 0, 48, 48, 48, 2, NULL, "48", -1, NULL, NULL, NULL }
+      { ICON_THEME_DIR_THRESHOLD, 0, 16, 16, 16, 2, 1, NULL, "16", -1, NULL, NULL, NULL },
+      { ICON_THEME_DIR_THRESHOLD, 0, 20, 20, 20, 2, 1, NULL, "20", -1,  NULL, NULL, NULL },
+      { ICON_THEME_DIR_THRESHOLD, 0, 24, 24, 24, 2, 1, NULL, "24", -1, NULL, NULL, NULL },
+      { ICON_THEME_DIR_THRESHOLD, 0, 32, 32, 32, 2, 1, NULL, "32", -1, NULL, NULL, NULL },
+      { ICON_THEME_DIR_THRESHOLD, 0, 48, 48, 48, 2, 1, NULL, "48", -1, NULL, NULL, NULL }
     };
   gint i;
 
@@ -1597,6 +1601,7 @@ static GtkIconInfo *
 choose_icon (GtkIconTheme       *icon_theme,
             const gchar        *icon_names[],
             gint                size,
+             gint                scale,
             GtkIconLookupFlags  flags)
 {
   GtkIconThemePrivate *priv;
@@ -1651,7 +1656,7 @@ choose_icon (GtkIconTheme       *icon_theme,
       for (l = priv->themes; l; l = l->next)
         {
           IconTheme *theme = l->data;
-          icon_info = theme_lookup_icon (theme, icon_names[0], size, allow_svg, use_builtin);
+          icon_info = theme_lookup_icon (theme, icon_names[0], size, scale, allow_svg, use_builtin);
           if (icon_info)
             goto out;
         }
@@ -1663,7 +1668,7 @@ choose_icon (GtkIconTheme       *icon_theme,
       
       for (i = 0; icon_names[i]; i++)
         {
-          icon_info = theme_lookup_icon (theme, icon_names[i], size, allow_svg, use_builtin);
+          icon_info = theme_lookup_icon (theme, icon_names[i], size, scale, allow_svg, use_builtin);
           if (icon_info)
             goto out;
         }
@@ -1697,6 +1702,7 @@ choose_icon (GtkIconTheme       *icon_theme,
          DestroyIcon (hIcon);
           icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
           icon_info->dir_size = size;
+          icon_info->dir_scale = 1;
        }
       g_strfreev (resources);
     }
@@ -1721,12 +1727,14 @@ choose_icon (GtkIconTheme       *icon_theme,
 
       icon_info->dir_type = ICON_THEME_DIR_UNTHEMED;
       icon_info->dir_size = size;
+      icon_info->dir_scale = 1;
     }
 
  out:
   if (icon_info)
     {
       icon_info->desired_size = size;
+      icon_info->desired_scale = scale;
       icon_info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
 
       icon_info->key.icon_names = g_strdupv ((char **)icon_names);
@@ -1800,12 +1808,51 @@ gtk_icon_theme_lookup_icon (GtkIconTheme       *icon_theme,
                            gint                size,
                            GtkIconLookupFlags  flags)
 {
+  g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
+  g_return_val_if_fail (icon_name != NULL, NULL);
+  g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
+                       (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
+
+  GTK_NOTE (ICONTHEME, 
+           g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
+
+  return gtk_icon_theme_lookup_icon_for_scale (icon_theme, icon_name,
+                                               size, 1, flags);
+}
+
+/**
+ * gtk_icon_theme_lookup_icon_for_scale:
+ * @icon_theme: a #GtkIconTheme
+ * @icon_name: the name of the icon to lookup
+ * @size: desired icon size
+ * @scale: the desired scale
+ * @flags: flags modifying the behavior of the icon lookup
+ *
+ * Looks up a named icon for a particular window scale and returns a
+ * structure containing information such as the filename of the
+ * icon. The icon can then be rendered into a pixbuf using
+ * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon() combines
+ * these two steps if all you need is the pixbuf.)
+ *
+ * Return value: (transfer full): a #GtkIconInfo object containing
+ * information about the icon, or %NULL if the icon wasn't found.
+ *
+ * Since: 3.10
+ */
+GtkIconInfo *
+gtk_icon_theme_lookup_icon_for_scale (GtkIconTheme       *icon_theme,
+                                      const gchar        *icon_name,
+                                      gint                size,
+                                      gint                scale,
+                                      GtkIconLookupFlags  flags)
+{
   GtkIconInfo *info;
 
   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
   g_return_val_if_fail (icon_name != NULL, NULL);
   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
                        (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
+  g_return_val_if_fail (scale >= 1, NULL);
 
   GTK_NOTE (ICONTHEME, 
            g_print ("gtk_icon_theme_lookup_icon %s\n", icon_name));
@@ -1831,7 +1878,7 @@ gtk_icon_theme_lookup_icon (GtkIconTheme       *icon_theme,
         }
       names[dashes + 1] = NULL;
    
-      info = choose_icon (icon_theme, (const gchar **) names, size, flags);
+      info = choose_icon (icon_theme, (const gchar **) names, size, scale, flags);
       
       g_strfreev (names);
     }
@@ -1842,7 +1889,7 @@ gtk_icon_theme_lookup_icon (GtkIconTheme       *icon_theme,
       names[0] = icon_name;
       names[1] = NULL;
 
-      info = choose_icon (icon_theme, names, size, flags);
+      info = choose_icon (icon_theme, names, size, scale, flags);
     }
 
   return info;
@@ -1882,9 +1929,50 @@ gtk_icon_theme_choose_icon (GtkIconTheme       *icon_theme,
   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
                        (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
 
-  return choose_icon (icon_theme, icon_names, size, flags);
+  return choose_icon (icon_theme, icon_names, size, 1, flags);
 }
 
+/**
+ * gtk_icon_theme_choose_icon_for_scale:
+ * @icon_theme: a #GtkIconTheme
+ * @icon_names: (array zero-terminated=1): %NULL-terminated array of
+ *     icon names to lookup
+ * @size: desired icon size
+ * @scale: desired scale
+ * @flags: flags modifying the behavior of the icon lookup
+ * 
+ * Looks up a named icon for a particular window scale and returns a
+ * structure containing information such as the filename of the
+ * icon. The icon can then be rendered into a pixbuf using
+ * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon() combines
+ * these two steps if all you need is the pixbuf.)
+ *
+ * If @icon_names contains more than one name, this function 
+ * tries them all in the given order before falling back to 
+ * inherited icon themes.
+ * 
+ * Return value: (transfer full): a #GtkIconInfo object containing information
+ * about the icon, or %NULL if the icon wasn't found.
+ *
+ * Since: 3.10
+ */
+GtkIconInfo *
+gtk_icon_theme_choose_icon_for_scale (GtkIconTheme       *icon_theme,
+                                      const gchar        *icon_names[],
+                                      gint                size,
+                                      gint                scale,
+                                      GtkIconLookupFlags  flags)
+{
+  g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
+  g_return_val_if_fail (icon_names != NULL, NULL);
+  g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
+                       (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
+  g_return_val_if_fail (scale >= 1, NULL);
+
+  return choose_icon (icon_theme, icon_names, size, scale, flags);
+}
+
+
 /* Error quark */
 GQuark
 gtk_icon_theme_error_quark (void)
@@ -1929,6 +2017,56 @@ gtk_icon_theme_load_icon (GtkIconTheme         *icon_theme,
                          GtkIconLookupFlags    flags,
                          GError              **error)
 {
+  g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
+  g_return_val_if_fail (icon_name != NULL, NULL);
+  g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
+                       (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  return gtk_icon_theme_load_icon_for_scale (icon_theme, icon_name,
+                                             size, 1, flags, error);
+}
+
+/**
+ * gtk_icon_theme_load_icon_for_scale:
+ * @icon_theme: a #GtkIconTheme
+ * @icon_name: the name of the icon to lookup
+ * @size: the desired icon size. The resulting icon may not be
+ *     exactly this size; see gtk_icon_info_load_icon().
+ * @scale: desired scale
+ * @flags: flags modifying the behavior of the icon lookup
+ * @error: (allow-none): Location to store error information on failure,
+ *     or %NULL.
+ *
+ * Looks up an icon in an icon theme for a particular window scale,
+ * scales it to the given size and renders it into a pixbuf. This is a
+ * convenience function; if more details about the icon are needed,
+ * use gtk_icon_theme_lookup_icon() followed by
+ * gtk_icon_info_load_icon().
+ *
+ * Note that you probably want to listen for icon theme changes and
+ * update the icon. This is usually done by connecting to the
+ * GtkWidget::style-set signal. If for some reason you do not want to
+ * update the icon when the icon theme changes, you should consider
+ * using gdk_pixbuf_copy() to make a private copy of the pixbuf
+ * returned by this function. Otherwise GTK+ may need to keep the old
+ * icon theme loaded, which would be a waste of memory.
+ *
+ * Return value: (transfer full): the rendered icon; this may be a
+ *     newly created icon or a new reference to an internal icon, so
+ *     you must not modify the icon. Use g_object_unref() to release
+ *     your reference to the icon. %NULL if the icon isn't found.
+ *
+ * Since: 3.10
+ **/
+GdkPixbuf *
+gtk_icon_theme_load_icon_for_scale (GtkIconTheme        *icon_theme,
+                                    const gchar         *icon_name,
+                                    gint                 size,
+                                    gint                 scale,
+                                    GtkIconLookupFlags   flags,
+                                    GError             **error)
+{
   GtkIconInfo *icon_info;
   GdkPixbuf *pixbuf = NULL;
   
@@ -1937,9 +2075,10 @@ gtk_icon_theme_load_icon (GtkIconTheme         *icon_theme,
   g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
                        (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
-  
-  icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size,
-                                         flags | GTK_ICON_LOOKUP_USE_BUILTIN);
+  g_return_val_if_fail (scale >= 1, NULL);
+
+  icon_info = gtk_icon_theme_lookup_icon_for_scale (icon_theme, icon_name, size, scale,
+                                                    flags | GTK_ICON_LOOKUP_USE_BUILTIN);
   if (!icon_info)
     {
       g_set_error (error, GTK_ICON_THEME_ERROR,  GTK_ICON_THEME_NOT_FOUND,
@@ -2375,31 +2514,43 @@ theme_dir_destroy (IconThemeDir *dir)
 }
 
 static int
-theme_dir_size_difference (IconThemeDir *dir, int size, gboolean *smaller)
+theme_dir_size_difference (IconThemeDir *dir,
+                           int           size,
+                           gint          scale,
+                           gboolean     *smaller,
+                           gint         *scale_diff)
 {
+  int scaled_size, scaled_dir_size;
   int min, max;
+
+  scaled_size = size * scale;
+  scaled_dir_size = dir->size * dir->scale;
+  *scale_diff = abs (scale - dir->scale);
+
   switch (dir->type)
     {
     case ICON_THEME_DIR_FIXED:
-      *smaller = size < dir->size;
-      return abs (size - dir->size);
+      *smaller = scaled_size < scaled_dir_size;
+      return abs (scaled_size - scaled_dir_size);
       break;
     case ICON_THEME_DIR_SCALABLE:
-      *smaller = size < dir->min_size;
-      if (size < dir->min_size)
-       return dir->min_size - size;
-      if (size > dir->max_size)
-       return size - dir->max_size;
+      *smaller = scaled_size < (dir->min_size * dir->scale);
+      if (scaled_size < (dir->min_size * dir->scale))
+       return (dir->min_size * dir->scale) - scaled_size;
+      if (size > (dir->max_size * dir->scale))
+       return scaled_size - (dir->max_size * dir->scale);
+      /* If the range covers the scaled_size then there really is no scale difference */
+      *scale_diff = 0;
       return 0;
       break;
     case ICON_THEME_DIR_THRESHOLD:
-      min = dir->size - dir->threshold;
-      max = dir->size + dir->threshold;
-      *smaller = size < min;
-      if (size < min)
-       return min - size;
-      if (size > max)
-       return size - max;
+      min = (dir->size - dir->threshold) * dir->scale;
+      max = (dir->size + dir->threshold) * dir->scale;
+      *smaller = scaled_size < min;
+      if (scaled_size < min)
+       return min - scaled_size;
+      if (scaled_size > max)
+       return scaled_size - max;
       return 0;
       break;
     case ICON_THEME_DIR_UNTHEMED:
@@ -2490,6 +2641,7 @@ static GtkIconInfo *
 theme_lookup_icon (IconTheme          *theme,
                   const char         *icon_name,
                   int                 size,
+                   gint                scale,
                   gboolean            allow_svg,
                   gboolean            use_builtin)
 {
@@ -2497,11 +2649,13 @@ theme_lookup_icon (IconTheme          *theme,
   IconThemeDir *dir, *min_dir;
   char *file;
   int min_difference, difference;
+  int min_scale_diff, scale_diff;
   BuiltinIcon *closest_builtin = NULL;
   gboolean smaller, has_larger, match;
   IconSuffix suffix;
 
   min_difference = G_MAXINT;
+  min_scale_diff = G_MAXINT;
   min_dir = NULL;
   has_larger = FALSE;
   match = FALSE;
@@ -2534,9 +2688,10 @@ theme_lookup_icon (IconTheme          *theme,
       suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
       if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE)
        {
-         difference = theme_dir_size_difference (dir, size, &smaller);
+         difference = theme_dir_size_difference (dir, size, scale,
+                                                  &smaller, &scale_diff);
 
-         if (difference == 0)
+         if (difference == 0 && scale_diff == 0)
            {
               if (dir->type == ICON_THEME_DIR_SCALABLE)
                 {
@@ -2553,8 +2708,8 @@ theme_lookup_icon (IconTheme          *theme,
                 {
                   /* for a matching non-scalable dir keep
                    * going and look for a closer match
-                   */             
-                  difference = abs (size - dir->size);
+                   */
+                  difference = abs (size * scale - dir->size * dir->scale);
                   if (!match || difference < min_difference)
                     {
                       match = TRUE;
@@ -2564,24 +2719,29 @@ theme_lookup_icon (IconTheme          *theme,
                   if (difference == 0)
                     break;
                 }
-           } 
-  
+           }
+
           if (!match)
             {
              if (!has_larger)
                {
-                 if (difference < min_difference || smaller)
+                 if (difference < min_difference ||
+                      (difference == min_difference && (scale_diff < min_scale_diff)) ||
+                      smaller)
                    {
                      min_difference = difference;
+                      min_scale_diff = scale_diff;
                      min_dir = dir;
                      has_larger = smaller;
                    }
                }
              else
                {
-                 if (difference < min_difference && smaller)
+                 if ((difference < min_difference ||
+                       (difference == min_difference && scale_diff < min_scale_diff)) && smaller)
                    {
                      min_difference = difference;
+                      min_scale_diff = scale_diff;
                      min_dir = dir;
                    }
                }
@@ -2663,6 +2823,7 @@ theme_lookup_icon (IconTheme          *theme,
 
       icon_info->dir_type = min_dir->type;
       icon_info->dir_size = min_dir->size;
+      icon_info->dir_scale = min_dir->scale;
       icon_info->threshold = min_dir->threshold;
       
       return icon_info;
@@ -2881,6 +3042,7 @@ theme_subdir_load (GtkIconTheme *icon_theme,
   char *full_dir;
   GError *error = NULL;
   IconThemeDirMtime *dir_mtime;
+  int scale;
 
   size = g_key_file_get_integer (theme_file, subdir, "Size", &error);
   if (error)
@@ -2928,6 +3090,11 @@ theme_subdir_load (GtkIconTheme *icon_theme,
   else
     threshold = 2;
 
+  if (g_key_file_has_key (theme_file, subdir, "OutputScale", NULL))
+    scale = g_key_file_get_integer (theme_file, subdir, "OutputScale", NULL);
+  else
+    scale = 1;
+
   for (d = icon_theme->priv->dir_mtimes; d; d = d->next)
     {
       dir_mtime = (IconThemeDirMtime *)d->data;
@@ -2956,6 +3123,8 @@ theme_subdir_load (GtkIconTheme *icon_theme,
          dir->dir = full_dir;
          dir->icon_data = NULL;
          dir->subdir = g_strdup (subdir);
+          dir->scale = scale;
+
          if (dir_mtime->cache != NULL)
             {
              dir->cache = _gtk_icon_cache_ref (dir_mtime->cache);
@@ -3071,8 +3240,10 @@ icon_info_dup (GtkIconInfo *icon_info)
   dup->data = icon_data_dup (icon_info->data);
   dup->dir_type = icon_info->dir_type;
   dup->dir_size = icon_info->dir_size;
+  dup->dir_scale = icon_info->dir_scale;
   dup->threshold = icon_info->threshold;
   dup->desired_size = icon_info->desired_size;
+  dup->desired_scale = icon_info->desired_scale;
   dup->raw_coordinates = icon_info->raw_coordinates;
   dup->forced_size = icon_info->forced_size;
   dup->emblems_applied = icon_info->emblems_applied;
@@ -3088,6 +3259,7 @@ icon_info_new_builtin (BuiltinIcon *icon)
   icon_info->cache_pixbuf = g_object_ref (icon->pixbuf);
   icon_info->dir_type = ICON_THEME_DIR_THRESHOLD;
   icon_info->dir_size = icon->size;
+  icon_info->dir_scale = 1;
   icon_info->threshold = 2;
 
   return icon_info;
@@ -3181,7 +3353,10 @@ gtk_icon_info_class_init (GtkIconInfoClass *klass)
  * to a larger icon. These icons will be given
  * the same base size as the larger icons to which
  * they are attached.
- * 
+ *
+ * Note that for scaled icons the base size does
+ * not include the base scale.
+ *
  * Return value: the base size, or 0, if no base
  *  size is known for the icon.
  *
@@ -3196,6 +3371,27 @@ gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
 }
 
 /**
+ * gtk_icon_info_get_base_scale:
+ * @icon_info: a #GtkIconInfo
+ *
+ * Gets the base scale for the icon. The base scale is a scale for the
+ * icon that was specified by the icon theme creator. For instance an
+ * icon drawn for a high-dpi screen with window-scale 2 for a base
+ * size of 32 will be 64 pixels tall and have a base_scale of 2.
+ *
+ * Return value: the base scale.
+ *
+ * Since: 3.10
+ **/
+gint
+gtk_icon_info_get_base_scale (GtkIconInfo *icon_info)
+{
+  g_return_val_if_fail (icon_info != NULL, 0);
+
+  return icon_info->dir_scale;
+}
+
+/**
  * gtk_icon_info_get_filename:
  * @icon_info: a #GtkIconInfo
  * 
@@ -3355,6 +3551,7 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo  *icon_info,
                                   gboolean      scale_only)
 {
   int image_width, image_height;
+  int scaled_desired_size;
   GdkPixbuf *source_pixbuf;
   gboolean is_svg;
 
@@ -3379,6 +3576,8 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo  *icon_info,
   if (icon_info->icon_file && !icon_info->loadable)
     icon_info->loadable = G_LOADABLE_ICON (g_file_icon_new (icon_info->icon_file));
 
+  scaled_desired_size = icon_info->desired_size * icon_info->desired_scale;
+
   is_svg = FALSE;
   if (G_IS_FILE_ICON (icon_info->loadable))
     {
@@ -3406,20 +3605,21 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo  *icon_info,
     {
       GInputStream *stream;
 
-      icon_info->scale = icon_info->desired_size / 1000.;
+      icon_info->scale = scaled_desired_size / 1000.;
 
       if (scale_only)
        return TRUE;
-      
+
+      /* TODO: We should have a load_at_scale */
       stream = g_loadable_icon_load (icon_info->loadable,
-                                     icon_info->desired_size,
+                                     scaled_desired_size,
                                      NULL, NULL,
                                      &icon_info->load_error);
       if (stream)
         {
           icon_info->pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
-                                                                   icon_info->desired_size,
-                                                                   icon_info->desired_size,
+                                                                   scaled_desired_size,
+                                                                   scaled_desired_size,
                                                                    TRUE,
                                                                    NULL,
                                                                    &icon_info->load_error);
@@ -3442,19 +3642,19 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo  *icon_info,
   if (icon_info->forced_size)
     icon_info->scale = -1;
   else if (icon_info->dir_type == ICON_THEME_DIR_FIXED)
-    icon_info->scale = 1.0;
+    icon_info->scale = (double) icon_info->desired_scale / icon_info->dir_scale;
   else if (icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
     {
-      if (icon_info->desired_size >= icon_info->dir_size - icon_info->threshold &&
-         icon_info->desired_size <= icon_info->dir_size + icon_info->threshold)
-       icon_info->scale = 1.0;
+      if (scaled_desired_size  >= (icon_info->dir_size - icon_info->threshold) * icon_info->dir_scale &&
+         scaled_desired_size <= (icon_info->dir_size + icon_info->threshold) * icon_info->dir_scale)
+        icon_info->scale = (double) icon_info->desired_scale / icon_info->dir_scale;
       else if (icon_info->dir_size > 0)
-       icon_info->scale =(gdouble) icon_info->desired_size / icon_info->dir_size;
+       icon_info->scale =(gdouble) scaled_desired_size / (icon_info->dir_size * icon_info->dir_scale);
     }
   else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
     {
       if (icon_info->dir_size > 0)
-       icon_info->scale = (gdouble) icon_info->desired_size / icon_info->dir_size;
+       icon_info->scale = (gdouble) scaled_desired_size / (icon_info->dir_size * icon_info->dir_scale);
     }
 
   if (icon_info->scale >= 0. && scale_only)
@@ -3470,8 +3670,9 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo  *icon_info,
     {
       GInputStream *stream;
 
+      /* TODO: We should have a load_at_scale */
       stream = g_loadable_icon_load (icon_info->loadable,
-                                     icon_info->desired_size,
+                                     scaled_desired_size,
                                      NULL, NULL,
                                      &icon_info->load_error);
       if (stream)
@@ -3495,7 +3696,7 @@ icon_info_ensure_scale_and_pixbuf (GtkIconInfo  *icon_info,
     {
       gint image_size = MAX (image_width, image_height);
       if (image_size > 0)
-       icon_info->scale = (gdouble)icon_info->desired_size / (gdouble)image_size;
+       icon_info->scale = (gdouble)scaled_desired_size / (gdouble)image_size;
       else
        icon_info->scale = 1.0;
       
@@ -3910,8 +4111,8 @@ _gtk_icon_info_load_symbolic_internal (GtkIconInfo  *icon_info,
 
   stream = g_memory_input_stream_new_from_data (data, -1, g_free);
   pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
-                                                icon_info->desired_size,
-                                                icon_info->desired_size,
+                                                icon_info->desired_size * icon_info->desired_scale,
+                                                icon_info->desired_size * icon_info->desired_scale,
                                                 TRUE,
                                                 NULL,
                                                 error);
@@ -4851,6 +5052,37 @@ gtk_icon_theme_lookup_by_gicon (GtkIconTheme       *icon_theme,
                                 gint                size,
                                 GtkIconLookupFlags  flags)
 {
+  return gtk_icon_theme_lookup_by_gicon_for_scale (icon_theme, icon,
+                                                   size, 1, flags);
+}
+
+
+/**
+ * gtk_icon_theme_lookup_by_gicon_for_scale:
+ * @icon_theme: a #GtkIconTheme
+ * @icon: the #GIcon to look up
+ * @size: desired icon size
+ * @scale: the desired scale
+ * @flags: flags modifying the behavior of the icon lookup
+ *
+ * Looks up an icon and returns a structure containing
+ * information such as the filename of the icon.
+ * The icon can then be rendered into a pixbuf using
+ * gtk_icon_info_load_icon_for_scale ().
+ *
+ * Return value: (transfer full): a #GtkIconInfo structure containing 
+ *     information about the icon, or %NULL if the icon 
+ *     wasn't found. Unref with g_object_unref()
+ *
+ * Since: 2.14
+ */
+GtkIconInfo *
+gtk_icon_theme_lookup_by_gicon_for_scale (GtkIconTheme       *icon_theme,
+                                          GIcon              *icon,
+                                          gint                size,
+                                          gint                scale,
+                                          GtkIconLookupFlags  flags)
+{
   GtkIconInfo *info;
 
   g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
@@ -4874,6 +5106,7 @@ gtk_icon_theme_lookup_by_gicon (GtkIconTheme       *icon_theme,
       info->dir_type = ICON_THEME_DIR_UNTHEMED;
       info->dir_size = size;
       info->desired_size = size;
+      info->desired_scale = scale;
       info->threshold = 2;
       info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
 
@@ -4884,7 +5117,7 @@ gtk_icon_theme_lookup_by_gicon (GtkIconTheme       *icon_theme,
       const gchar **names;
 
       names = (const gchar **)g_themed_icon_get_names (G_THEMED_ICON (icon));
-      info = gtk_icon_theme_choose_icon (icon_theme, names, size, flags);
+      info = gtk_icon_theme_choose_icon_for_scale (icon_theme, names, size, scale, flags);
 
       return info;
     }
@@ -4898,7 +5131,7 @@ gtk_icon_theme_lookup_by_gicon (GtkIconTheme       *icon_theme,
         _gtk_numerable_icon_set_background_icon_size (GTK_NUMERABLE_ICON (icon), size / 2);
 
       base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
-      base_info = gtk_icon_theme_lookup_by_gicon (icon_theme, base, size, flags);
+      base_info = gtk_icon_theme_lookup_by_gicon_for_scale (icon_theme, base, size, scale, flags);
       if (base_info)
         {
           info = icon_info_dup (base_info);
@@ -4909,7 +5142,7 @@ gtk_icon_theme_lookup_by_gicon (GtkIconTheme       *icon_theme,
             {
               emblem = g_emblem_get_icon (G_EMBLEM (l->data));
              /* always force size for emblems */
-              emblem_info = gtk_icon_theme_lookup_by_gicon (icon_theme, emblem, size / 2, flags | 
GTK_ICON_LOOKUP_FORCE_SIZE);
+              emblem_info = gtk_icon_theme_lookup_by_gicon_for_scale (icon_theme, emblem, size / 2, scale, 
flags | GTK_ICON_LOOKUP_FORCE_SIZE);
               if (emblem_info)
                 info->emblem_infos = g_slist_prepend (info->emblem_infos, emblem_info);
             }
@@ -4928,17 +5161,17 @@ gtk_icon_theme_lookup_by_gicon (GtkIconTheme       *icon_theme,
       if ((flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0)
        {
          gint width, height, max;
-         gdouble scale;
+         gdouble pixbuf_scale;
          GdkPixbuf *scaled;
 
          width = gdk_pixbuf_get_width (pixbuf);
          height = gdk_pixbuf_get_height (pixbuf);
          max = MAX (width, height);
-         scale = (gdouble) size / (gdouble) max;
+         pixbuf_scale = (gdouble) size * scale / (gdouble) max;
 
          scaled = gdk_pixbuf_scale_simple (pixbuf,
-                                           0.5 + width * scale,
-                                           0.5 + height * scale,
+                                           0.5 + width * pixbuf_scale,
+                                           0.5 + height * pixbuf_scale,
                                            GDK_INTERP_BILINEAR);
 
          info = gtk_icon_info_new_for_pixbuf (icon_theme, scaled);
diff --git a/gtk/gtkicontheme.h b/gtk/gtkicontheme.h
index 443457d..0902909 100644
--- a/gtk/gtkicontheme.h
+++ b/gtk/gtkicontheme.h
@@ -179,23 +179,49 @@ GtkIconInfo * gtk_icon_theme_lookup_icon           (GtkIconTheme
                                                    const gchar                 *icon_name,
                                                    gint                         size,
                                                    GtkIconLookupFlags           flags);
+GDK_AVAILABLE_IN_3_10
+GtkIconInfo * gtk_icon_theme_lookup_icon_for_scale (GtkIconTheme                *icon_theme,
+                                                    const gchar                 *icon_name,
+                                                    gint                         size,
+                                                    gint                         scale,
+                                                    GtkIconLookupFlags           flags);
+
 GDK_AVAILABLE_IN_ALL
 GtkIconInfo * gtk_icon_theme_choose_icon           (GtkIconTheme                *icon_theme,
                                                    const gchar                 *icon_names[],
                                                    gint                         size,
                                                    GtkIconLookupFlags           flags);
+GDK_AVAILABLE_IN_3_10
+GtkIconInfo * gtk_icon_theme_choose_icon_for_scale (GtkIconTheme                *icon_theme,
+                                                   const gchar                 *icon_names[],
+                                                   gint                         size,
+                                                    gint                         scale,
+                                                   GtkIconLookupFlags           flags);
 GDK_AVAILABLE_IN_ALL
 GdkPixbuf *   gtk_icon_theme_load_icon             (GtkIconTheme                *icon_theme,
                                                    const gchar                 *icon_name,
                                                    gint                         size,
                                                    GtkIconLookupFlags           flags,
                                                    GError                     **error);
+GDK_AVAILABLE_IN_3_10
+GdkPixbuf *   gtk_icon_theme_load_icon_for_scale   (GtkIconTheme                *icon_theme,
+                                                    const gchar                 *icon_name,
+                                                    gint                         size,
+                                                    gint                         scale,
+                                                    GtkIconLookupFlags           flags,
+                                                    GError                     **error);
 
 GDK_AVAILABLE_IN_ALL
 GtkIconInfo * gtk_icon_theme_lookup_by_gicon       (GtkIconTheme                *icon_theme,
                                                     GIcon                       *icon,
                                                     gint                         size,
                                                     GtkIconLookupFlags           flags);
+GtkIconInfo * gtk_icon_theme_lookup_by_gicon_for_scale (GtkIconTheme             *icon_theme,
+                                                        GIcon                    *icon,
+                                                        gint                      size,
+                                                        gint                      scale,
+                                                        GtkIconLookupFlags        flags);
+
 
 GDK_AVAILABLE_IN_ALL
 GList *       gtk_icon_theme_list_icons            (GtkIconTheme                *icon_theme,
@@ -226,6 +252,8 @@ GtkIconInfo *         gtk_icon_info_new_for_pixbuf     (GtkIconTheme  *icon_them
 
 GDK_AVAILABLE_IN_ALL
 gint                  gtk_icon_info_get_base_size      (GtkIconInfo   *icon_info);
+GDK_AVAILABLE_IN_3_10
+gint                  gtk_icon_info_get_base_scale     (GtkIconInfo   *icon_info);
 GDK_AVAILABLE_IN_ALL
 const gchar *         gtk_icon_info_get_filename       (GtkIconInfo   *icon_info);
 GDK_AVAILABLE_IN_ALL


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]