From e4170661b9228cb3e09dbc1cd789e15ef44f4ef5 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 3 Feb 2020 10:35:45 +0100 Subject: [PATCH] IconTheme: Make icon lookups faster Traditionally the icon lookup for a theme has been: lookup (icon_name, size): best_directory = NULL forearch theme foreach directory in theme if dir_size_matches (directory, size) && dir_has_icon (directory, icon-name) best_directory = chose_best_size_dir (best_directory, directory) if best_directory return icon from best_directory However, it turns out that there are a lot of subdirectories which have the same size, as they differ only in the (essentially useless) "context" value. For example the "16x16/apps" subdirectory is essentially the same as the "16x16/actions" one. So, instead rathern than keeping all the directories as separate we store the all the directories with the same size as a single entity (DirSize) and the icon lookup in that DirSize looks up not only which suffix to use for that icon but also which subdir it is in. Additionally we keep a hashtable with all icon names that are available in the entire theme (i.e. all DirSizes), which allows use both to store each icon name only once, but also to do a quick negative lookup and early exit in case we're looking up an icon that doesn't exist. This is pretty common because we often look up sets of icons like "image-png-symbolic", "image-png", "image", expecting some to fail. This brings down the time of the initial css validation from 20msec to 15msec for me when running icon-factory. --- gtk/gtkicontheme.c | 679 ++++++++++++++++++++++++--------------------- 1 file changed, 370 insertions(+), 309 deletions(-) diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c index ae33375bce..1ec6715a75 100644 --- a/gtk/gtkicontheme.c +++ b/gtk/gtkicontheme.c @@ -326,29 +326,36 @@ typedef struct gchar *display_name; gchar *comment; - /* In search order */ - GList *dirs; + GArray *dir_sizes; /* IconThemeDirSize */ + GArray *dirs; /* IconThemeDir */ + GHashTable *icons; /* name (owned) -> name */ } IconTheme; typedef struct { - IconThemeDirType type; - GQuark context; + guint16 dir_index; /* index in dirs */ + guint8 best_suffix; + guint8 best_suffix_no_svg; +} IconThemeFile; +typedef struct +{ + IconThemeDirType type; gint size; gint min_size; gint max_size; gint threshold; gint scale; - gboolean is_resource; - - gchar *dir; - gchar *subdir; - gint subdir_index; - GtkIconCache *cache; + GArray *icon_files; + GHashTable *icon_hash; /* name (unowned) -> file index */ +} IconThemeDirSize; - GHashTable *icons; +typedef struct +{ + GQuark context; + gboolean is_resource; + char *path; /* e.g. "/usr/share/icons/hicolor/32x32/apps" */ } IconThemeDir; typedef struct @@ -368,6 +375,9 @@ typedef struct static void gtk_icon_theme_finalize (GObject *object); static void gtk_icon_theme_dispose (GObject *object); +static IconTheme *theme_new (const char *theme_name, + GKeyFile *theme_file); +static void theme_dir_size_destroy (IconThemeDirSize *dir_size); static void theme_dir_destroy (IconThemeDir *dir); static void theme_destroy (IconTheme *theme); static GtkIcon * theme_lookup_icon (IconTheme *theme, @@ -387,8 +397,6 @@ static void theme_subdir_load (GtkIconTheme *self, static void do_theme_change (GtkIconTheme *self); static void blow_themes (GtkIconTheme *self); static gboolean rescan_themes (GtkIconTheme *self); -static IconSuffix theme_dir_get_icon_suffix (IconThemeDir *dir, - const gchar *icon_name); static GtkIcon * icon_new (IconThemeDirType type, gint dir_size, gint dir_scale); @@ -1508,47 +1516,31 @@ insert_theme (GtkIconTheme *self, g_free (path); } - if (theme_file || strcmp (theme_name, FALLBACK_ICON_THEME) == 0) + if (theme_file == NULL) { - theme = g_new0 (IconTheme, 1); - theme->name = g_strdup (theme_name); - self->themes = g_list_prepend (self->themes, theme); - if (!theme_file) + if (strcmp (theme_name, FALLBACK_ICON_THEME) == 0) { theme_file = g_key_file_new (); g_key_file_set_list_separator (theme_file, ','); g_key_file_load_from_data (theme_file, builtin_hicolor_index, -1, 0, NULL); } + else + return; } - if (theme_file == NULL) - return; - - theme->display_name = - g_key_file_get_locale_string (theme_file, "Icon Theme", "Name", NULL, NULL); - if (!theme->display_name) - g_warning ("Theme file for %s has no name", theme_name); - dirs = g_key_file_get_string_list (theme_file, "Icon Theme", "Directories", NULL, NULL); if (!dirs) { g_warning ("Theme file for %s has no directories", theme_name); - self->themes = g_list_remove (self->themes, theme); - g_free (theme->name); - g_free (theme->display_name); - g_free (theme); g_key_file_free (theme_file); return; } scaled_dirs = g_key_file_get_string_list (theme_file, "Icon Theme", "ScaledDirectories", NULL, NULL); - theme->comment = - g_key_file_get_locale_string (theme_file, - "Icon Theme", "Comment", - NULL, NULL); + theme = theme_new (theme_name, theme_file); + self->themes = g_list_prepend (self->themes, theme); - theme->dirs = NULL; for (i = 0; dirs[i] != NULL; i++) theme_subdir_load (self, theme, theme_file, dirs[i]); @@ -1557,11 +1549,10 @@ insert_theme (GtkIconTheme *self, for (i = 0; scaled_dirs[i] != NULL; i++) theme_subdir_load (self, theme, theme_file, scaled_dirs[i]); } + g_strfreev (dirs); g_strfreev (scaled_dirs); - theme->dirs = g_list_reverse (theme->dirs); - themes = g_key_file_get_string_list (theme_file, "Icon Theme", "Inherits", @@ -2639,18 +2630,6 @@ gtk_icon_theme_has_icon (GtkIconTheme *self, ensure_valid_themes (self, FALSE); - for (l = self->dir_mtimes; l; l = l->next) - { - IconThemeDirMtime *dir_mtime = l->data; - GtkIconCache *cache = dir_mtime->cache; - - if (cache && gtk_icon_cache_has_icon (cache, icon_name)) - { - res = TRUE; - goto out; - } - } - for (l = self->themes; l; l = l->next) { if (theme_has_icon (l->data, icon_name)) @@ -2697,10 +2676,10 @@ gint * gtk_icon_theme_get_icon_sizes (GtkIconTheme *self, const gchar *icon_name) { - GList *l, *d; + GList *l; + int i; GHashTable *sizes; gint *result, *r; - guint suffix; g_return_val_if_fail (GTK_IS_ICON_THEME (self), NULL); @@ -2713,21 +2692,21 @@ gtk_icon_theme_get_icon_sizes (GtkIconTheme *self, for (l = self->themes; l; l = l->next) { IconTheme *theme = l->data; - for (d = theme->dirs; d; d = d->next) + + for (i = 0; i < theme->dir_sizes->len; i++) { - IconThemeDir *dir = d->data; + IconThemeDirSize *dir_size = &g_array_index (theme->dir_sizes, IconThemeDirSize, i); - if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL)) + if (dir_size->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir_size->size), NULL, NULL)) continue; - suffix = theme_dir_get_icon_suffix (dir, icon_name); - if (suffix != ICON_SUFFIX_NONE) - { - if (suffix == ICON_SUFFIX_SVG) - g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL); - else - g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL); - } + if (!g_hash_table_contains (dir_size->icon_hash, icon_name)) + continue; + + if (dir_size->type == ICON_THEME_DIR_SCALABLE) + g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL); + else + g_hash_table_insert (sizes, GINT_TO_POINTER (dir_size->size), NULL); } } @@ -2895,57 +2874,92 @@ gtk_icon_theme_rescan_if_needed (GtkIconTheme *self) return retval; } +static IconTheme * +theme_new (const char *theme_name, + GKeyFile *theme_file) +{ + IconTheme *theme; + + theme = g_new0 (IconTheme, 1); + theme->name = g_strdup (theme_name); + theme->dir_sizes = g_array_new (FALSE, FALSE, sizeof (IconThemeDirSize)); + theme->dirs = g_array_new (FALSE, FALSE, sizeof (IconThemeDir)); + theme->icons = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + theme->display_name = + g_key_file_get_locale_string (theme_file, "Icon Theme", "Name", NULL, NULL); + if (!theme->display_name) + g_warning ("Theme file for %s has no name", theme_name); + + theme->comment = + g_key_file_get_locale_string (theme_file, + "Icon Theme", "Comment", + NULL, NULL); + return theme; +} + static void theme_destroy (IconTheme *theme) { + gsize i; + + g_free (theme->name); g_free (theme->display_name); g_free (theme->comment); - g_free (theme->name); - g_list_free_full (theme->dirs, (GDestroyNotify) theme_dir_destroy); + for (i = 0; i < theme->dir_sizes->len; i++) + theme_dir_size_destroy (&g_array_index (theme->dir_sizes, IconThemeDirSize, i)); + g_array_free (theme->dir_sizes, TRUE); + + for (i = 0; i < theme->dirs->len; i++) + theme_dir_destroy (&g_array_index (theme->dirs, IconThemeDir, i)); + g_array_free (theme->dirs, TRUE); + g_hash_table_destroy (theme->icons); g_free (theme); } static void -theme_dir_destroy (IconThemeDir *dir) +theme_dir_size_destroy (IconThemeDirSize *dir) { - if (dir->cache) - gtk_icon_cache_unref (dir->cache); - if (dir->icons) - g_hash_table_destroy (dir->icons); + if (dir->icon_hash) + g_hash_table_destroy (dir->icon_hash); + if (dir->icon_files) + g_array_free (dir->icon_files, TRUE); +} - g_free (dir->dir); - g_free (dir->subdir); - g_free (dir); +static void +theme_dir_destroy (IconThemeDir *dir) +{ + g_free (dir->path); } static int -theme_dir_size_difference (IconThemeDir *dir, - gint size, - gint scale) +theme_dir_size_difference (IconThemeDirSize *dir_size, + gint size, + gint scale) { gint scaled_size, scaled_dir_size; gint min, max; scaled_size = size * scale; - scaled_dir_size = dir->size * dir->scale; + scaled_dir_size = dir_size->size * dir_size->scale; - switch (dir->type) + switch (dir_size->type) { case ICON_THEME_DIR_FIXED: return abs (scaled_size - scaled_dir_size); case ICON_THEME_DIR_SCALABLE: - 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 (scaled_size < (dir_size->min_size * dir_size->scale)) + return (dir_size->min_size * dir_size->scale) - scaled_size; + if (size > (dir_size->max_size * dir_size->scale)) + return scaled_size - (dir_size->max_size * dir_size->scale); return 0; case ICON_THEME_DIR_THRESHOLD: - min = (dir->size - dir->threshold) * dir->scale; - max = (dir->size + dir->threshold) * dir->scale; + min = (dir_size->size - dir_size->threshold) * dir_size->scale; + max = (dir_size->size + dir_size->threshold) * dir_size->scale; if (scaled_size < min) return min - scaled_size; if (scaled_size > max) @@ -3022,53 +3036,12 @@ best_suffix (IconSuffix suffix, return ICON_SUFFIX_NONE; } -static IconSuffix -theme_dir_get_icon_suffix (IconThemeDir *dir, - const gchar *icon_name) -{ - IconSuffix suffix, symbolic_suffix; - - if (dir->cache) - { - int icon_name_len = strlen (icon_name); - - if (icon_name_is_symbolic (icon_name, icon_name_len)) - { - /* Look for foo-symbolic.symbolic.png, as the cache only stores the ".png" suffix */ - char *icon_name_with_prefix = g_strconcat (icon_name, ".symbolic", NULL); - symbolic_suffix = (IconSuffix)gtk_icon_cache_get_icon_flags (dir->cache, - icon_name_with_prefix, - dir->subdir_index); - g_free (icon_name_with_prefix); - - if (symbolic_suffix & ICON_SUFFIX_PNG) - suffix = ICON_SUFFIX_SYMBOLIC_PNG; - else - suffix = (IconSuffix)gtk_icon_cache_get_icon_flags (dir->cache, - icon_name, - dir->subdir_index); - } - else - suffix = (IconSuffix)gtk_icon_cache_get_icon_flags (dir->cache, - icon_name, - dir->subdir_index); - - suffix = suffix & ~HAS_ICON_FILE; - } - else - suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name)); - - GTK_NOTE (ICONTHEME, g_message ("get icon suffix%s: %u", dir->cache ? " (cached)" : "", suffix)); - - return suffix; -} - /* returns TRUE if dir_a is a better match */ static gboolean -compare_dir_matches (IconThemeDir *dir_a, gint difference_a, - IconThemeDir *dir_b, gint difference_b, - gint requested_size, - gint requested_scale) +compare_dir_size_matches (IconThemeDirSize *dir_a, gint difference_a, + IconThemeDirSize *dir_b, gint difference_b, + gint requested_size, + gint requested_scale) { gint diff_a; gint diff_b; @@ -3135,73 +3108,70 @@ theme_lookup_icon (IconTheme *theme, gint scale, gboolean allow_svg) { - GList *dirs, *l; - IconThemeDir *dir, *min_dir; - gchar *file; - gint min_difference, difference; - IconSuffix suffix; + IconThemeDirSize *min_dir_size; + IconThemeFile *min_file; + gint min_difference; IconSuffix min_suffix; + int i; - min_difference = G_MAXINT; - min_dir = NULL; + /* Its not uncommon with misses, so we do an early check which allows us do + do a lot less work. */ + if (!g_hash_table_contains (theme->icons, icon_name)) + return FALSE; - dirs = theme->dirs; + min_difference = G_MAXINT; + min_dir_size = NULL; - l = dirs; - while (l != NULL) + for (i = 0; i < theme->dir_sizes->len; i++) { - dir = l->data; - - GTK_NOTE (ICONTHEME, g_message ("look up icon dir %s", dir->dir)); - suffix = theme_dir_get_icon_suffix (dir, icon_name); - if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE) - { - difference = theme_dir_size_difference (dir, size, scale); - if (min_dir == NULL || - compare_dir_matches (dir, difference, - min_dir, min_difference, - size, scale)) - { - min_dir = dir; - min_suffix = suffix; - min_difference = difference; - } - } + IconThemeDirSize *dir_size = &g_array_index (theme->dir_sizes, IconThemeDirSize, i); + IconThemeFile *file; + guint best_suffix; + gint difference; + gpointer file_index; - l = l->next; - } + if (!g_hash_table_lookup_extended (dir_size->icon_hash, icon_name, NULL, &file_index)) + continue; - if (min_dir) - { - GtkIcon *icon; + file = &g_array_index (dir_size->icon_files, IconThemeFile, GPOINTER_TO_INT(file_index)); - icon = icon_new (min_dir->type, min_dir->size, min_dir->scale); - icon->min_size = min_dir->min_size; - icon->max_size = min_dir->max_size; - suffix = min_suffix; - suffix = best_suffix (suffix, allow_svg); - g_assert (suffix != ICON_SUFFIX_NONE); + if (allow_svg) + best_suffix = file->best_suffix; + else + best_suffix = file->best_suffix_no_svg; - if (min_dir->dir) - { - file = g_strconcat (icon_name, string_from_suffix (suffix), NULL); - icon->filename = g_build_filename (min_dir->dir, file, NULL); + if (best_suffix == ICON_SUFFIX_NONE) + continue; - icon->is_svg = suffix == ICON_SUFFIX_SVG; - icon->is_resource = min_dir->is_resource; - g_free (file); - } - else + difference = theme_dir_size_difference (dir_size, size, scale); + if (min_dir_size == NULL || + compare_dir_size_matches (dir_size, difference, + min_dir_size, min_difference, + size, scale)) { - icon->filename = NULL; + min_dir_size = dir_size; + min_file = file; + min_suffix = best_suffix; + min_difference = difference; } + } - if (min_dir->cache) - { - icon->cache_pixbuf = gtk_icon_cache_get_icon (min_dir->cache, icon_name, - min_dir->subdir_index); - } + if (min_dir_size) + { + GtkIcon *icon; + IconThemeDir *dir = &g_array_index (theme->dirs, IconThemeDir, min_file->dir_index); + gchar *filename; + + icon = icon_new (min_dir_size->type, min_dir_size->size, min_dir_size->scale); + icon->min_size = min_dir_size->min_size; + icon->max_size = min_dir_size->max_size; + + filename = g_strconcat (icon_name, string_from_suffix (min_suffix), NULL); + icon->filename = g_build_filename (dir->path, filename, NULL); + icon->is_svg = min_suffix == ICON_SUFFIX_SVG; + icon->is_resource = dir->is_resource; + g_free (filename); return icon; } @@ -3214,22 +3184,31 @@ theme_list_icons (IconTheme *theme, GHashTable *icons, GQuark context) { - GList *l = theme->dirs; - IconThemeDir *dir; + int i; - while (l != NULL) + for (i = 0; i < theme->dir_sizes->len; i++) { - dir = l->data; + IconThemeDirSize *dir_size = &g_array_index (theme->dir_sizes, IconThemeDirSize, i); + GHashTableIter iter; + gpointer key, value; - if (context == dir->context || - context == 0) + g_hash_table_iter_init (&iter, dir_size->icon_hash); + while (g_hash_table_iter_next (&iter, &key, &value)) { - if (dir->cache) - gtk_icon_cache_add_icons (dir->cache, dir->subdir, icons); - else - g_hash_table_foreach (dir->icons, add_key_to_hash, icons); + char *icon_name = key; + gint file_index = GPOINTER_TO_INT (value); + + if (context != 0) + { + IconThemeFile *file = &g_array_index (dir_size->icon_files, IconThemeFile, file_index); + IconThemeDir *dir = &g_array_index (theme->dirs, IconThemeDir, file->dir_index); + + if (dir->context != context) + continue; + } + + g_hash_table_insert (icons, icon_name, NULL); } - l = l->next; } } @@ -3237,25 +3216,7 @@ static gboolean theme_has_icon (IconTheme *theme, const gchar *icon_name) { - GList *l; - - for (l = theme->dirs; l; l = l->next) - { - IconThemeDir *dir = l->data; - - if (dir->cache) - { - if (gtk_icon_cache_has_icon (dir->cache, icon_name)) - return TRUE; - } - else - { - if (g_hash_table_lookup (dir->icons, icon_name) != NULL) - return TRUE; - } - } - - return FALSE; + return g_hash_table_contains (theme->icons, icon_name); } static GHashTable * @@ -3298,6 +3259,164 @@ scan_directory (GtkIconTheme *self, return icons; } +static GHashTable * +scan_resource_directory (GtkIconTheme *self, + char *full_dir) +{ + GHashTable *icons = NULL; + char **children; + int i; + + GTK_DISPLAY_NOTE (self->display, ICONTHEME, + g_message ("scanning resource directory %s", full_dir)); + + children = g_resources_enumerate_children (full_dir, 0, NULL); + + for (i = 0; children != NULL && children[i]; i++) + { + const char *name = children[i]; + gchar *base_name; + IconSuffix suffix, hash_suffix; + + suffix = suffix_from_name (name); + if (suffix == ICON_SUFFIX_NONE) + continue; + + if (!icons) + icons = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + base_name = strip_suffix (name); + + hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (icons, base_name)); + /* takes ownership of base_name */ + g_hash_table_replace (icons, base_name, GUINT_TO_POINTER (hash_suffix|suffix)); + } + + return icons; +} + +static gboolean +theme_dir_size_equal (IconThemeDirSize *a, + IconThemeDirSize *b) +{ + return + a->type == b->type && + a->size == b->size && + a->min_size == b->min_size && + a->max_size == b->max_size && + a->threshold == b->threshold && + a->scale == b->scale; + } + +static guint32 +theme_ensure_dir_size (IconTheme *theme, + IconThemeDirType type, + gint size, + gint min_size, + gint max_size, + gint threshold, + gint scale) +{ + guint32 index; + IconThemeDirSize new = { 0 }; + + new.type = type; + new.size = size; + new.min_size = min_size; + new.max_size = max_size; + new.threshold = threshold; + new.scale = scale; + + for (index = 0; index < theme->dir_sizes->len; index++) + { + IconThemeDirSize *dir_size = &g_array_index (theme->dir_sizes, IconThemeDirSize, index); + + if (theme_dir_size_equal (dir_size, &new)) + return index; + } + + new.icon_files = g_array_new (FALSE, FALSE, sizeof (IconThemeFile)); + new.icon_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + + index = theme->dir_sizes->len; + g_array_append_val (theme->dir_sizes, new); + + return index; +} + +static guint32 +theme_add_icon_dir (IconTheme *theme, + GQuark context, + gboolean is_resource, + char *path /* takes ownership */) +{ + IconThemeDir new_dir = { 0 }; + guint32 dir_index; + + new_dir.context = context; + new_dir.is_resource = is_resource; + new_dir.path = path; + + dir_index = theme->dirs->len; + g_array_append_val (theme->dirs, new_dir); + return dir_index; +} + +static void +theme_add_icon_file (IconTheme *theme, + const char *icon_name, + guint suffixes, + IconThemeDirSize *dir_size, + guint dir_index) +{ + IconThemeFile new_file = { 0 }; + guint index; + char *owned_icon_name = NULL; + + if (g_hash_table_contains (dir_size->icon_hash, icon_name)) + return; + + owned_icon_name = g_hash_table_lookup (theme->icons, icon_name); + if (owned_icon_name == NULL) + { + owned_icon_name = g_strdup (icon_name); + g_hash_table_insert (theme->icons, owned_icon_name, owned_icon_name); + } + + new_file.dir_index = dir_index; + new_file.best_suffix = best_suffix (suffixes, TRUE); + new_file.best_suffix_no_svg = best_suffix (suffixes, FALSE); + + index = dir_size->icon_files->len; + g_array_append_val (dir_size->icon_files, new_file); + + g_hash_table_insert (dir_size->icon_hash, owned_icon_name, GINT_TO_POINTER(index)); + +} + +static void +theme_add_dir_with_icons (IconTheme *theme, + IconThemeDirSize *dir_size, + GQuark context, + gboolean is_resource, + char *path /* takes ownership */, + GHashTable *icons) +{ + GHashTableIter iter; + gpointer key, value; + guint32 dir_index; + + dir_index = theme_add_icon_dir (theme, context, is_resource, path); + + g_hash_table_iter_init (&iter, icons); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + const char *icon_name = key; + guint suffixes = GPOINTER_TO_INT(value); + theme_add_icon_file (theme, icon_name, suffixes, dir_size, dir_index); + } +} + static void theme_subdir_load (GtkIconTheme *self, IconTheme *theme, @@ -3306,7 +3425,6 @@ theme_subdir_load (GtkIconTheme *self, { GList *d; gchar *type_string; - IconThemeDir *dir; IconThemeDirType type; gchar *context_string; GQuark context; @@ -3314,16 +3432,17 @@ theme_subdir_load (GtkIconTheme *self, gint min_size; gint max_size; gint threshold; - gchar *full_dir; GError *error = NULL; IconThemeDirMtime *dir_mtime; + guint32 dir_size_index; + IconThemeDirSize *dir_size; gint scale; size = g_key_file_get_integer (theme_file, subdir, "Size", &error); if (error) { g_error_free (error); - g_warning ("Theme directory %s of theme %s has no size field\n", + g_warning ("Theme directory %s of theme %s has no size field\n", subdir, theme->name); return; } @@ -3342,14 +3461,6 @@ theme_subdir_load (GtkIconTheme *self, g_free (type_string); } - context = 0; - context_string = g_key_file_get_string (theme_file, subdir, "Context", NULL); - if (context_string) - { - context = g_quark_from_string (context_string); - g_free (context_string); - } - if (g_key_file_has_key (theme_file, subdir, "MaxSize", NULL)) max_size = g_key_file_get_integer (theme_file, subdir, "MaxSize", NULL); else @@ -3370,8 +3481,20 @@ theme_subdir_load (GtkIconTheme *self, else scale = 1; + dir_size_index = theme_ensure_dir_size (theme, type, size, min_size, max_size, threshold, scale); + dir_size = &g_array_index (theme->dir_sizes, IconThemeDirSize, dir_size_index); + + context = 0; + context_string = g_key_file_get_string (theme_file, subdir, "Context", NULL); + if (context_string) + { + context = g_quark_from_string (context_string); + g_free (context_string); + } + for (d = self->dir_mtimes; d; d = d->next) { + gchar *full_dir; dir_mtime = (IconThemeDirMtime *)d->data; if (!dir_mtime->exists) @@ -3382,9 +3505,7 @@ theme_subdir_load (GtkIconTheme *self, /* First, see if we have a cache for the directory */ if (dir_mtime->cache != NULL || g_file_test (full_dir, G_FILE_TEST_IS_DIR)) { - gboolean has_icons; - GtkIconCache *dir_cache; - GHashTable *icon_table = NULL; + GHashTable *icons = NULL; if (dir_mtime->cache == NULL) { @@ -3393,109 +3514,49 @@ theme_subdir_load (GtkIconTheme *self, } if (dir_mtime->cache != NULL) - { - dir_cache = dir_mtime->cache; - has_icons = gtk_icon_cache_has_icons (dir_cache, subdir); - } + icons = gtk_icon_cache_list_icons_in_directory (dir_mtime->cache, subdir); else - { - dir_cache = NULL; - icon_table = scan_directory (self, full_dir); - has_icons = icon_table != NULL; - } - - if (!has_icons) - { - g_assert (!icon_table); - g_free (full_dir); - continue; - } + icons = scan_directory (self, full_dir); - dir = g_new0 (IconThemeDir, 1); - dir->type = type; - dir->is_resource = FALSE; - dir->context = context; - dir->size = size; - dir->min_size = min_size; - dir->max_size = max_size; - dir->threshold = threshold; - dir->dir = full_dir; - dir->subdir = g_strdup (subdir); - dir->scale = scale; - dir->icons = icon_table; - - if (dir_cache) + if (icons) { - dir->cache = gtk_icon_cache_ref (dir_cache); - dir->subdir_index = gtk_icon_cache_get_directory_index (dir->cache, dir->subdir); + theme_add_dir_with_icons (theme, + dir_size, + context, + FALSE, + g_steal_pointer (&full_dir), + icons); + g_hash_table_destroy (icons); } - else - { - dir_cache = NULL; - dir->subdir_index = -1; - } - - theme->dirs = g_list_prepend (theme->dirs, dir); } - else - g_free (full_dir); + + g_free (full_dir); } if (strcmp (theme->name, FALLBACK_ICON_THEME) == 0) { for (d = self->resource_paths; d; d = d->next) { - int i; - char **children; + GHashTable *icons; + gchar *full_dir; /* Force a trailing / here, to avoid extra copies in GResource */ full_dir = g_build_filename ((const gchar *)d->data, subdir, " ", NULL); full_dir[strlen (full_dir) - 1] = '\0'; - children = g_resources_enumerate_children (full_dir, 0, NULL); - - if (!children) + icons = scan_resource_directory (self, full_dir); + if (icons) { - g_free (full_dir); - continue; + theme_add_dir_with_icons (theme, + dir_size, + context, + TRUE, + g_steal_pointer (&full_dir), + icons); + g_hash_table_destroy (icons); } - dir = g_new0 (IconThemeDir, 1); - dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - dir->type = type; - dir->is_resource = TRUE; - dir->context = context; - dir->size = size; - dir->min_size = min_size; - dir->max_size = max_size; - dir->threshold = threshold; - dir->dir = full_dir; - dir->subdir = g_strdup (subdir); - dir->scale = scale; - dir->cache = NULL; - dir->subdir_index = -1; - - for (i = 0; children[i]; i++) - { - gchar *base_name; - IconSuffix suffix, hash_suffix; - - suffix = suffix_from_name (children[i]); - if (suffix == ICON_SUFFIX_NONE) - continue; - - base_name = strip_suffix (children[i]); - - hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name)); - /* takes ownership of base_name */ - g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix|suffix)); - } - g_strfreev (children); - - if (g_hash_table_size (dir->icons) > 0) - theme->dirs = g_list_prepend (theme->dirs, dir); - else - theme_dir_destroy (dir); + g_free (full_dir); } } } -- 2.30.2