summaryrefslogtreecommitdiff
path: root/gtk/updateiconcache.c
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2005-08-15 03:52:34 +0000
committerMatthias Clasen <matthiasc@src.gnome.org>2005-08-15 03:52:34 +0000
commit329fc5da74317d5a2818a305176dcea9c9f8a9a0 (patch)
treed46da656aedacbe7bf959eecc39dbadfd08728e5 /gtk/updateiconcache.c
parente54a45318dfe1a4e0ebd182ac162e12068ad205f (diff)
downloadgtk+-329fc5da74317d5a2818a305176dcea9c9f8a9a0.tar.gz
Store only one copy of the pixel data for symlinked icons. To achieve
2005-08-14 Matthias Clasen <mclasen@redhat.com> * gtk/updateiconcache.c: Store only one copy of the pixel data for symlinked icons. To achieve this, maintain a hashtable mapping pathnames to pixel data, and share the pixel data for all symlinks resolving to the same pathname. When writing out the image data, write out the pixel data only the first time it is met, and store the offset pointing to the first copy for use in all later cases. This reduces the size of the Bluecurve icon cache from 40 to 13MB. (#312972)
Diffstat (limited to 'gtk/updateiconcache.c')
-rw-r--r--gtk/updateiconcache.c226
1 files changed, 200 insertions, 26 deletions
diff --git a/gtk/updateiconcache.c b/gtk/updateiconcache.c
index 17b488d2a0..f38dde7352 100644
--- a/gtk/updateiconcache.c
+++ b/gtk/updateiconcache.c
@@ -33,6 +33,7 @@
#else
#include <utime.h>
#endif
+#include <libgen.h>
#include <glib.h>
#include <glib/gstdio.h>
@@ -87,13 +88,23 @@ is_cache_up_to_date (const gchar *path)
return cache_stat.st_mtime >= path_stat.st_mtime;
}
+typedef struct
+{
+ gboolean has_pixdata;
+ GdkPixdata pixdata;
+ guint32 offset;
+ guint pixel_data_size;
+} ImageData;
+
+static GHashTable *image_data_hash = NULL;
+
typedef struct
{
int flags;
int dir_index;
- gboolean has_pixdata;
- GdkPixdata pixdata;
+ ImageData *image_data;
+ guint pixel_data_size;
int has_embedded_rect;
int x0, y0, x1, y1;
@@ -245,20 +256,177 @@ load_icon_data (Image *image, const char *path)
g_key_file_free (icon_file);
}
+/*
+ * This function was copied from gtkfilesystemunix.c, it should
+ * probably go to GLib
+ */
static void
-maybe_cache_image_data (Image *image, const gchar *path)
+canonicalize_filename (gchar *filename)
{
- if (CAN_CACHE_IMAGE_DATA(image->flags) && !image->has_pixdata)
+ gchar *p, *q;
+ gboolean last_was_slash = FALSE;
+
+ p = filename;
+ q = filename;
+
+ while (*p)
{
- GdkPixbuf *pixbuf;
+ if (*p == G_DIR_SEPARATOR)
+ {
+ if (!last_was_slash)
+ *q++ = G_DIR_SEPARATOR;
+
+ last_was_slash = TRUE;
+ }
+ else
+ {
+ if (last_was_slash && *p == '.')
+ {
+ if (*(p + 1) == G_DIR_SEPARATOR ||
+ *(p + 1) == '\0')
+ {
+ if (*(p + 1) == '\0')
+ break;
+
+ p += 1;
+ }
+ else if (*(p + 1) == '.' &&
+ (*(p + 2) == G_DIR_SEPARATOR ||
+ *(p + 2) == '\0'))
+ {
+ if (q > filename + 1)
+ {
+ q--;
+ while (q > filename + 1 &&
+ *(q - 1) != G_DIR_SEPARATOR)
+ q--;
+ }
+
+ if (*(p + 2) == '\0')
+ break;
+
+ p += 2;
+ }
+ else
+ {
+ *q++ = *p;
+ last_was_slash = FALSE;
+ }
+ }
+ else
+ {
+ *q++ = *p;
+ last_was_slash = FALSE;
+ }
+ }
+
+ p++;
+ }
+
+ if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
+ q--;
+
+ *q = '\0';
+}
+
+static gchar *
+follow_links (const gchar *path)
+{
+ gchar *target;
+ gchar *d, *s;
+ gchar *path2 = NULL;
+
+ path2 = g_strdup (path);
+ while (g_file_test (path2, G_FILE_TEST_IS_SYMLINK))
+ {
+ target = g_file_read_link (path2, NULL);
- pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+ if (target)
+ {
+ if (target[0] == '/')
+ path2 = target;
+ else
+ {
+ d = dirname (path2);
+ s = g_build_path ("/", d, target, NULL);
+ g_free (target);
+ g_free (path2);
+ path2 = s;
+ }
+ }
+ else
+ break;
+ }
+
+ if (strcmp (path, path2) == 0)
+ {
+ g_free (path2);
+ path2 = NULL;
+ }
+
+ return path2;
+}
+
+static void
+maybe_cache_image_data (Image *image,
+ const gchar *path)
+{
+ if (CAN_CACHE_IMAGE_DATA(image->flags) && !image->image_data)
+ {
+ GdkPixbuf *pixbuf;
+ ImageData *idata;
+ gchar *path2;
+
+ idata = g_hash_table_lookup (image_data_hash, path);
+
+ path2 = follow_links (path);
+
+ if (path2)
+ {
+ ImageData *idata2;
+
+ canonicalize_filename (path2);
+
+ idata2 = g_hash_table_lookup (image_data_hash, path2);
+
+ if (idata && idata2 && idata != idata2)
+ g_error ("different idatas found for symlinked '%s' and '%s'\n",
+ path, path2);
+
+ if (idata && !idata2)
+ g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
+
+ if (!idata && idata2)
+ {
+ g_hash_table_insert (image_data_hash, g_strdup (path), idata2);
+ idata = idata2;
+ }
+ }
- if (pixbuf)
+ if (!idata)
+ {
+ idata = g_new0 (ImageData, 1);
+ g_hash_table_insert (image_data_hash, g_strdup (path), idata);
+ if (path2)
+ g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
+ }
+
+ if (!idata->has_pixdata)
{
- image->has_pixdata = TRUE;
- gdk_pixdata_from_pixbuf (&image->pixdata, pixbuf, FALSE);
+ pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+
+ if (pixbuf)
+ {
+ gdk_pixdata_from_pixbuf (&idata->pixdata, pixbuf, FALSE);
+ idata->pixel_data_size = idata->pixdata.length + 8;
+ idata->has_pixdata = TRUE;
+ }
}
+
+ image->image_data = idata;
+
+ if (path2)
+ g_free (path2);
}
}
@@ -312,7 +480,6 @@ scan_directory (const gchar *base_path,
}
retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
-
if (retval)
{
if (g_str_has_suffix (name, ".png"))
@@ -366,7 +533,6 @@ scan_directory (const gchar *base_path,
}
g_free (path);
-
}
g_dir_close (dir);
@@ -474,9 +640,8 @@ gboolean
write_pixdata (FILE *cache, GdkPixdata *pixdata)
{
guint8 *s;
- int len;
- int i;
-
+ guint len;
+ gint i;
/* Type 0 is GdkPixdata */
if (!write_card32 (cache, 0))
@@ -537,10 +702,17 @@ get_image_meta_data_size (Image *image)
guint
get_image_pixel_data_size (Image *image)
{
- if (image->has_pixdata)
- return image->pixdata.length + 8;
+ if (image->pixel_data_size == 0)
+ {
+ if (image->image_data &&
+ image->image_data->has_pixdata)
+ {
+ image->pixel_data_size = image->image_data->pixel_data_size;
+ image->image_data->pixel_data_size = 0;
+ }
+ }
- return 0;
+ return image->pixel_data_size;
}
guint
@@ -553,7 +725,7 @@ get_image_data_size (Image *image)
len += get_image_pixel_data_size (image);
len += get_image_meta_data_size (image);
- if (len > 0)
+ if (len > 0 || (image->image_data && image->image_data->has_pixdata))
len += 8;
return len;
@@ -579,7 +751,7 @@ get_single_node_size (HashNode *node, gboolean include_image_data)
for (list = node->image_list; list; list = list->next)
{
Image *image = list->data;
-
+
len += get_image_data_size (image);
}
@@ -590,7 +762,6 @@ guint
get_bucket_size (HashNode *node)
{
int len = 0;
-
while (node)
{
len += get_single_node_size (node, TRUE);
@@ -652,7 +823,7 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
{
Image *image = list->data;
int image_data_size = get_image_data_size (image);
-
+
/* Directory index */
if (!write_card16 (cache, image->dir_index))
return FALSE;
@@ -668,7 +839,7 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
return FALSE;
data_offset += image_data_size;
}
- else
+ else
{
if (!write_card32 (cache, 0))
return FALSE;
@@ -685,7 +856,7 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
int pixel_data_size = get_image_pixel_data_size (image);
int meta_data_size = get_image_meta_data_size (image);
- if (meta_data_size + pixel_data_size == 0)
+ if (get_image_data_size (image) == 0)
continue;
/* Pixel data */
@@ -693,10 +864,12 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
{
if (!write_card32 (cache, image_data_offset + 8))
return FALSE;
+
+ image->image_data->offset = image_data_offset + 8;
}
else
{
- if (!write_card32 (cache, 0))
+ if (!write_card32 (cache, image->image_data->offset))
return FALSE;
}
@@ -713,7 +886,7 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
if (pixel_data_size > 0)
{
- if (!write_pixdata (cache, &image->pixdata))
+ if (!write_pixdata (cache, &image->image_data->pixdata))
return FALSE;
}
@@ -961,6 +1134,7 @@ build_cache (const gchar *path)
}
files = g_hash_table_new (g_str_hash, g_str_equal);
+ image_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
directories = scan_directory (path, NULL, files, NULL, 0);
@@ -1038,6 +1212,6 @@ main (int argc, char **argv)
g_type_init ();
build_cache (path);
-
+
return 0;
}