Merge branch 'faster-theme-lookup' into 'master'
Faster icon theme lookup See merge request GNOME/gtk!1381
This commit is contained in:
@@ -43,7 +43,6 @@
|
||||
#define GET_UINT16(cache, offset) (GUINT16_FROM_BE (*(guint16 *)((cache) + (offset))))
|
||||
#define GET_UINT32(cache, offset) (GUINT32_FROM_BE (*(guint32 *)((cache) + (offset))))
|
||||
|
||||
|
||||
struct _GtkIconCache {
|
||||
gint ref_count;
|
||||
|
||||
@@ -70,7 +69,7 @@ gtk_icon_cache_unref (GtkIconCache *cache)
|
||||
GTK_NOTE (ICONTHEME, g_message ("unmapping icon cache"));
|
||||
|
||||
if (cache->map)
|
||||
g_mapped_file_unref (cache->map);
|
||||
g_mapped_file_unref (cache->map);
|
||||
g_free (cache);
|
||||
}
|
||||
}
|
||||
@@ -107,7 +106,7 @@ gtk_icon_cache_new_for_path (const gchar *path)
|
||||
/* (will need to check on the Windows DDK part later) */
|
||||
#if ((defined (_MSC_VER) && (_MSC_VER >= 1400 || __MSVCRT_VERSION__ >= 0x0800)) || defined (__MINGW64_VERSION_MAJOR)) && !defined(_WIN64)
|
||||
#undef fstat /* Just in case */
|
||||
#define fstat _fstat32
|
||||
#define fstat _fstat32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -118,7 +117,7 @@ gtk_icon_cache_new_for_path (const gchar *path)
|
||||
if (st.st_mtime < path_st.st_mtime)
|
||||
{
|
||||
GTK_NOTE (ICONTHEME, g_message ("icon cache outdated"));
|
||||
goto done;
|
||||
goto done;
|
||||
}
|
||||
|
||||
map = g_mapped_file_new (cache_filename, FALSE, NULL);
|
||||
@@ -144,7 +143,7 @@ gtk_icon_cache_new_for_path (const gchar *path)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
GTK_NOTE (ICONTHEME, g_message ("found icon cache for %s", path));
|
||||
|
||||
@@ -154,7 +153,7 @@ gtk_icon_cache_new_for_path (const gchar *path)
|
||||
cache->buffer = g_mapped_file_get_contents (map);
|
||||
|
||||
done:
|
||||
g_free (cache_filename);
|
||||
g_free (cache_filename);
|
||||
if (fd >= 0)
|
||||
close (fd);
|
||||
|
||||
@@ -170,18 +169,18 @@ gtk_icon_cache_new (const gchar *data)
|
||||
cache->ref_count = 1;
|
||||
cache->map = NULL;
|
||||
cache->buffer = (gchar *)data;
|
||||
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
static gint
|
||||
get_directory_index (GtkIconCache *cache,
|
||||
const gchar *directory)
|
||||
const gchar *directory)
|
||||
{
|
||||
guint32 dir_list_offset;
|
||||
gint n_dirs;
|
||||
gint i;
|
||||
|
||||
|
||||
dir_list_offset = GET_UINT32 (cache->buffer, 8);
|
||||
|
||||
n_dirs = GET_UINT32 (cache->buffer, dir_list_offset);
|
||||
@@ -191,345 +190,80 @@ get_directory_index (GtkIconCache *cache,
|
||||
guint32 name_offset = GET_UINT32 (cache->buffer, dir_list_offset + 4 + 4 * i);
|
||||
gchar *name = cache->buffer + name_offset;
|
||||
if (strcmp (name, directory) == 0)
|
||||
return i;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
gint
|
||||
gtk_icon_cache_get_directory_index (GtkIconCache *cache,
|
||||
const gchar *directory)
|
||||
GHashTable *
|
||||
gtk_icon_cache_list_icons_in_directory (GtkIconCache *cache,
|
||||
const gchar *directory)
|
||||
{
|
||||
return get_directory_index (cache, directory);
|
||||
}
|
||||
|
||||
static guint
|
||||
icon_name_hash (gconstpointer key)
|
||||
{
|
||||
const signed char *p = key;
|
||||
guint32 h = *p;
|
||||
|
||||
if (h)
|
||||
for (p += 1; *p != '\0'; p++)
|
||||
h = (h << 5) - h + *p;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static gint
|
||||
find_image_offset (GtkIconCache *cache,
|
||||
const gchar *icon_name,
|
||||
gint directory_index)
|
||||
{
|
||||
guint32 hash_offset;
|
||||
guint32 n_buckets;
|
||||
guint32 chain_offset;
|
||||
int hash;
|
||||
guint32 image_list_offset, n_images;
|
||||
int i;
|
||||
|
||||
if (!icon_name)
|
||||
return 0;
|
||||
|
||||
chain_offset = cache->last_chain_offset;
|
||||
if (chain_offset)
|
||||
{
|
||||
guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
|
||||
gchar *name = cache->buffer + name_offset;
|
||||
|
||||
if (strcmp (name, icon_name) == 0)
|
||||
goto find_dir;
|
||||
}
|
||||
|
||||
hash_offset = GET_UINT32 (cache->buffer, 4);
|
||||
n_buckets = GET_UINT32 (cache->buffer, hash_offset);
|
||||
hash = icon_name_hash (icon_name) % n_buckets;
|
||||
|
||||
chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * hash);
|
||||
while (chain_offset != 0xffffffff)
|
||||
{
|
||||
guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
|
||||
gchar *name = cache->buffer + name_offset;
|
||||
|
||||
if (strcmp (name, icon_name) == 0)
|
||||
{
|
||||
cache->last_chain_offset = chain_offset;
|
||||
goto find_dir;
|
||||
}
|
||||
|
||||
chain_offset = GET_UINT32 (cache->buffer, chain_offset);
|
||||
}
|
||||
|
||||
cache->last_chain_offset = 0;
|
||||
return 0;
|
||||
|
||||
find_dir:
|
||||
/* We've found an icon list, now check if we have the right icon in it */
|
||||
image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8);
|
||||
n_images = GET_UINT32 (cache->buffer, image_list_offset);
|
||||
|
||||
for (i = 0; i < n_images; i++)
|
||||
{
|
||||
if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * i) ==
|
||||
directory_index)
|
||||
return image_list_offset + 4 + 8 * i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint
|
||||
gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
|
||||
const gchar *icon_name,
|
||||
gint directory_index)
|
||||
{
|
||||
guint32 image_offset;
|
||||
|
||||
image_offset = find_image_offset (cache, icon_name, directory_index);
|
||||
|
||||
if (!image_offset)
|
||||
return 0;
|
||||
|
||||
return GET_UINT16 (cache->buffer, image_offset + 2);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_icon_cache_has_icons (GtkIconCache *cache,
|
||||
const gchar *directory)
|
||||
{
|
||||
int directory_index;
|
||||
guint32 hash_offset, n_buckets;
|
||||
guint32 chain_offset;
|
||||
guint32 image_list_offset, n_images;
|
||||
int i, j;
|
||||
|
||||
directory_index = get_directory_index (cache, directory);
|
||||
|
||||
if (directory_index == -1)
|
||||
return FALSE;
|
||||
|
||||
hash_offset = GET_UINT32 (cache->buffer, 4);
|
||||
n_buckets = GET_UINT32 (cache->buffer, hash_offset);
|
||||
|
||||
for (i = 0; i < n_buckets; i++)
|
||||
{
|
||||
chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * i);
|
||||
while (chain_offset != 0xffffffff)
|
||||
{
|
||||
image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8);
|
||||
n_images = GET_UINT32 (cache->buffer, image_list_offset);
|
||||
|
||||
for (j = 0; j < n_images; j++)
|
||||
{
|
||||
if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * j) ==
|
||||
directory_index)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
chain_offset = GET_UINT32 (cache->buffer, chain_offset);
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_icon_cache_add_icons (GtkIconCache *cache,
|
||||
const gchar *directory,
|
||||
GHashTable *hash_table)
|
||||
{
|
||||
int directory_index;
|
||||
guint32 hash_offset, n_buckets;
|
||||
guint32 chain_offset;
|
||||
guint32 image_list_offset, n_images;
|
||||
int i, j;
|
||||
|
||||
directory_index = get_directory_index (cache, directory);
|
||||
|
||||
if (directory_index == -1)
|
||||
return;
|
||||
|
||||
hash_offset = GET_UINT32 (cache->buffer, 4);
|
||||
n_buckets = GET_UINT32 (cache->buffer, hash_offset);
|
||||
|
||||
for (i = 0; i < n_buckets; i++)
|
||||
{
|
||||
chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * i);
|
||||
while (chain_offset != 0xffffffff)
|
||||
{
|
||||
guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
|
||||
gchar *name = cache->buffer + name_offset;
|
||||
|
||||
image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8);
|
||||
n_images = GET_UINT32 (cache->buffer, image_list_offset);
|
||||
|
||||
for (j = 0; j < n_images; j++)
|
||||
{
|
||||
if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * j) ==
|
||||
directory_index)
|
||||
g_hash_table_insert (hash_table, name, NULL);
|
||||
}
|
||||
|
||||
chain_offset = GET_UINT32 (cache->buffer, chain_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_icon_cache_has_icon (GtkIconCache *cache,
|
||||
const gchar *icon_name)
|
||||
{
|
||||
guint32 hash_offset;
|
||||
guint32 n_buckets;
|
||||
guint32 chain_offset;
|
||||
gint hash;
|
||||
|
||||
hash_offset = GET_UINT32 (cache->buffer, 4);
|
||||
n_buckets = GET_UINT32 (cache->buffer, hash_offset);
|
||||
|
||||
hash = icon_name_hash (icon_name) % n_buckets;
|
||||
|
||||
chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * hash);
|
||||
while (chain_offset != 0xffffffff)
|
||||
{
|
||||
guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
|
||||
gchar *name = cache->buffer + name_offset;
|
||||
|
||||
if (strcmp (name, icon_name) == 0)
|
||||
return TRUE;
|
||||
|
||||
chain_offset = GET_UINT32 (cache->buffer, chain_offset);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_icon_cache_has_icon_in_directory (GtkIconCache *cache,
|
||||
const gchar *icon_name,
|
||||
const gchar *directory)
|
||||
{
|
||||
guint32 hash_offset;
|
||||
guint32 n_buckets;
|
||||
guint32 chain_offset;
|
||||
gint hash;
|
||||
gboolean found_icon = FALSE;
|
||||
gint directory_index;
|
||||
guint32 hash_offset, n_buckets;
|
||||
guint32 chain_offset;
|
||||
guint32 image_list_offset, n_images;
|
||||
int i, j;
|
||||
GHashTable *icons = NULL;
|
||||
|
||||
directory_index = get_directory_index (cache, directory);
|
||||
|
||||
if (directory_index == -1)
|
||||
return FALSE;
|
||||
|
||||
return NULL;
|
||||
|
||||
hash_offset = GET_UINT32 (cache->buffer, 4);
|
||||
n_buckets = GET_UINT32 (cache->buffer, hash_offset);
|
||||
|
||||
hash = icon_name_hash (icon_name) % n_buckets;
|
||||
|
||||
chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * hash);
|
||||
while (chain_offset != 0xffffffff)
|
||||
for (i = 0; i < n_buckets; i++)
|
||||
{
|
||||
guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
|
||||
gchar *name = cache->buffer + name_offset;
|
||||
chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * i);
|
||||
while (chain_offset != 0xffffffff)
|
||||
{
|
||||
guint32 flags = 0;
|
||||
|
||||
if (strcmp (name, icon_name) == 0)
|
||||
{
|
||||
found_icon = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
chain_offset = GET_UINT32 (cache->buffer, chain_offset);
|
||||
image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8);
|
||||
n_images = GET_UINT32 (cache->buffer, image_list_offset);
|
||||
|
||||
for (j = 0; j < n_images; j++)
|
||||
{
|
||||
if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * j) ==
|
||||
directory_index)
|
||||
{
|
||||
flags = GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * j + 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags != 0)
|
||||
{
|
||||
guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
|
||||
const char *name = cache->buffer + name_offset;
|
||||
char *converted_name;
|
||||
guint32 hash_flags = 0;
|
||||
|
||||
/* Icons named foo.symbolic.png are stored in the cache as "foo.symbolic" with ICON_CACHE_FLAG_PNG,
|
||||
* but we convert it internally to ICON_CACHE_FLAG_SYMBOLIC_PNG.
|
||||
* Otherwise we use the same enum values and names as on disk. */
|
||||
if (g_str_has_suffix (name, ".symbolic") && (flags & ICON_CACHE_FLAG_PNG_SUFFIX) != 0)
|
||||
{
|
||||
flags |= ICON_CACHE_FLAG_SYMBOLIC_PNG_SUFFIX;
|
||||
flags &= ~ICON_CACHE_FLAG_PNG_SUFFIX;
|
||||
converted_name = g_strndup (name, strlen(name) - 9);
|
||||
}
|
||||
else
|
||||
converted_name = g_strdup (name);
|
||||
|
||||
if (!icons)
|
||||
icons = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
|
||||
hash_flags = GPOINTER_TO_INT (g_hash_table_lookup (icons, converted_name));
|
||||
g_hash_table_replace (icons, converted_name, GUINT_TO_POINTER (hash_flags|flags));
|
||||
}
|
||||
|
||||
chain_offset = GET_UINT32 (cache->buffer, chain_offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (found_icon)
|
||||
{
|
||||
guint32 image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8);
|
||||
guint32 n_images = GET_UINT32 (cache->buffer, image_list_offset);
|
||||
guint32 image_offset = image_list_offset + 4;
|
||||
gint i;
|
||||
for (i = 0; i < n_images; i++)
|
||||
{
|
||||
guint16 index = GET_UINT16 (cache->buffer, image_offset);
|
||||
|
||||
if (index == directory_index)
|
||||
return TRUE;
|
||||
image_offset += 8;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
return icons;
|
||||
}
|
||||
|
||||
static void
|
||||
pixbuf_destroy_cb (guchar *pixels,
|
||||
gpointer data)
|
||||
{
|
||||
GtkIconCache *cache = data;
|
||||
|
||||
gtk_icon_cache_unref (cache);
|
||||
}
|
||||
|
||||
GdkPixbuf *
|
||||
gtk_icon_cache_get_icon (GtkIconCache *cache,
|
||||
const gchar *icon_name,
|
||||
gint directory_index)
|
||||
{
|
||||
guint32 offset, image_data_offset, pixel_data_offset;
|
||||
guint32 length, type;
|
||||
GdkPixbuf *pixbuf;
|
||||
GdkPixdata pixdata;
|
||||
GError *error = NULL;
|
||||
|
||||
offset = find_image_offset (cache, icon_name, directory_index);
|
||||
|
||||
if (!offset)
|
||||
return NULL;
|
||||
|
||||
image_data_offset = GET_UINT32 (cache->buffer, offset + 4);
|
||||
|
||||
if (!image_data_offset)
|
||||
return NULL;
|
||||
|
||||
pixel_data_offset = GET_UINT32 (cache->buffer, image_data_offset);
|
||||
|
||||
type = GET_UINT32 (cache->buffer, pixel_data_offset);
|
||||
|
||||
if (type != 0)
|
||||
{
|
||||
GTK_NOTE (ICONTHEME, g_message ("invalid pixel data type %u", type));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
length = GET_UINT32 (cache->buffer, pixel_data_offset + 4);
|
||||
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
if (!gdk_pixdata_deserialize (&pixdata, length,
|
||||
(guchar *)(cache->buffer + pixel_data_offset + 8),
|
||||
&error))
|
||||
{
|
||||
GTK_NOTE (ICONTHEME, g_message ("could not deserialize data: %s", error->message));
|
||||
g_error_free (error);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
|
||||
pixbuf = gdk_pixbuf_new_from_data (pixdata.pixel_data, GDK_COLORSPACE_RGB,
|
||||
(pixdata.pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA,
|
||||
8, pixdata.width, pixdata.height, pixdata.rowstride,
|
||||
(GdkPixbufDestroyNotify)pixbuf_destroy_cb,
|
||||
cache);
|
||||
if (!pixbuf)
|
||||
{
|
||||
GTK_NOTE (ICONTHEME, g_message ("could not convert pixdata to pixbuf: %s", error->message));
|
||||
g_error_free (error);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gtk_icon_cache_ref (cache);
|
||||
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,30 +21,26 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* These are (mostly, see below) the on disk flags for each icon file, don't change */
|
||||
typedef enum
|
||||
{
|
||||
ICON_CACHE_FLAG_NONE = 0,
|
||||
ICON_CACHE_FLAG_XPM_SUFFIX = 1 << 0,
|
||||
ICON_CACHE_FLAG_SVG_SUFFIX = 1 << 1,
|
||||
ICON_CACHE_FLAG_PNG_SUFFIX = 1 << 2,
|
||||
ICON_CACHE_FLAG_HAS_ICON_FILE = 1 << 3,
|
||||
|
||||
/* This is a virtual flag we recreate in memory as the file format actually stores .symbolic.png as png */
|
||||
ICON_CACHE_FLAG_SYMBOLIC_PNG_SUFFIX = 1 << 4,
|
||||
} IconCacheFlag;
|
||||
|
||||
|
||||
typedef struct _GtkIconCache GtkIconCache;
|
||||
|
||||
GtkIconCache *gtk_icon_cache_new (const gchar *data);
|
||||
GtkIconCache *gtk_icon_cache_new_for_path (const gchar *path);
|
||||
gint gtk_icon_cache_get_directory_index (GtkIconCache *cache,
|
||||
GHashTable *gtk_icon_cache_list_icons_in_directory (GtkIconCache *cache,
|
||||
const gchar *directory);
|
||||
gboolean gtk_icon_cache_has_icon (GtkIconCache *cache,
|
||||
const gchar *icon_name);
|
||||
gboolean gtk_icon_cache_has_icon_in_directory (GtkIconCache *cache,
|
||||
const gchar *icon_name,
|
||||
const gchar *directory);
|
||||
gboolean gtk_icon_cache_has_icons (GtkIconCache *cache,
|
||||
const gchar *directory);
|
||||
void gtk_icon_cache_add_icons (GtkIconCache *cache,
|
||||
const gchar *directory,
|
||||
GHashTable *hash_table);
|
||||
|
||||
gint gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
|
||||
const gchar *icon_name,
|
||||
gint directory_index);
|
||||
GdkPixbuf *gtk_icon_cache_get_icon (GtkIconCache *cache,
|
||||
const gchar *icon_name,
|
||||
gint directory_index);
|
||||
|
||||
GtkIconCache *gtk_icon_cache_ref (GtkIconCache *cache);
|
||||
void gtk_icon_cache_unref (GtkIconCache *cache);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user