summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stefw@collabora.co.uk>2011-09-29 21:57:30 +0200
committerMatthias Clasen <mclasen@redhat.com>2011-12-16 15:34:41 -0500
commit9769f3d7d99f63b98f6fb3022b789d3fb8fb7001 (patch)
tree48ff5db132630273cd8fee6d241d02383cb52a9e
parent280916fe764cfe0e79fc0cac978ea65c64d1682e (diff)
downloadgdk-pixbuf-9769f3d7d99f63b98f6fb3022b789d3fb8fb7001.tar.gz
ico: Skip compressed icon images
* Previously we were selecting the largest icon image and then failing if it was compressed or it was somehow different than we expected. * Instead we should go back to the next largest and so on and see if there's one we can read. https://bugzilla.gnome.org/show_bug.cgi?id=652498
-rw-r--r--gdk-pixbuf/io-ico.c165
1 files changed, 96 insertions, 69 deletions
diff --git a/gdk-pixbuf/io-ico.c b/gdk-pixbuf/io-ico.c
index da00d1cb6..b73211ced 100644
--- a/gdk-pixbuf/io-ico.c
+++ b/gdk-pixbuf/io-ico.c
@@ -124,6 +124,14 @@ struct headerpair {
Negative = 0 -> bottom up BMP */
};
+/* Score the various parts of the icon */
+struct ico_direntry_data {
+ gint ImageScore;
+ gint DIBoffset;
+ gint x_hot;
+ gint y_hot;
+};
+
struct ico_progressive_state {
GdkPixbufModuleSizeFunc size_func;
GdkPixbufModulePreparedFunc prepared_func;
@@ -153,10 +161,8 @@ struct ico_progressive_state {
gint y_hot;
struct headerpair Header; /* Decoded (BE->CPU) header */
-
+ GList *entries;
gint DIBoffset;
- gint ImageScore;
-
GdkPixbuf *pixbuf; /* Our "target" */
};
@@ -178,13 +184,24 @@ context_free (struct ico_progressive_state *context)
g_free (context->LineBuf);
context->LineBuf = NULL;
g_free (context->HeaderBuf);
-
+ g_list_free_full (context->entries, g_free);
if (context->pixbuf)
g_object_unref (context->pixbuf);
g_free (context);
}
+static gint
+compare_direntry_scores (gconstpointer a,
+ gconstpointer b)
+{
+ const struct ico_direntry_data *ia = a;
+ const struct ico_direntry_data *ib = b;
+
+ /* Backwards, so largest first */
+ return ib->ImageScore - ia->ImageScore;
+}
+
static void DecodeHeader(guchar *Data, gint Bytes,
struct ico_progressive_state *State,
GError **error)
@@ -193,13 +210,14 @@ static void DecodeHeader(guchar *Data, gint Bytes,
in an .ICO. As a simple heuristic, we select the image which occupies the
largest number of bytes.
*/
-
+ struct ico_direntry_data *entry;
gint IconCount = 0; /* The number of icon-versions in the file */
guchar *BIH; /* The DIB for the used icon */
guchar *Ptr;
gint I;
guint16 imgtype; /* 1 = icon, 2 = cursor */
-
+ GList *l;
+
/* Step 1: The ICO header */
/* First word should be 0 according to specs */
@@ -244,68 +262,87 @@ static void DecodeHeader(guchar *Data, gint Bytes,
}
if (Bytes < State->HeaderSize)
return;
-
- /* We now have all the "short-specs" of the versions
- So we iterate through them and select the best one */
-
- State->ImageScore = 0;
- State->DIBoffset = 0;
- Ptr = Data + 6;
+
+ /* Now iterate through the ICONDIRENTRY structures, and sort them by
+ * which one we think is "best" (essentially the largest) */
+ g_list_free_full (State->entries, g_free);
+ State->entries = 0;
+ Ptr = Data + 6;
for (I=0;I<IconCount;I++) {
- int ThisScore;
-
- ThisScore = (Ptr[11] << 24) + (Ptr[10] << 16) + (Ptr[9] << 8) + (Ptr[8]);
-
- if (ThisScore>=State->ImageScore) {
- State->ImageScore = ThisScore;
- State->x_hot = (Ptr[5] << 8) + Ptr[4];
- State->y_hot = (Ptr[7] << 8) + Ptr[6];
- State->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+
- (Ptr[13]<<8) + (Ptr[12]);
-
- }
-
-
- Ptr += 16;
+ entry = g_new0 (struct ico_direntry_data, 1);
+ entry->ImageScore = (Ptr[11] << 24) + (Ptr[10] << 16) + (Ptr[9] << 8) + (Ptr[8]);
+ entry->x_hot = (Ptr[5] << 8) + Ptr[4];
+ entry->y_hot = (Ptr[7] << 8) + Ptr[6];
+ entry->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+
+ (Ptr[13]<<8) + (Ptr[12]);
+ State->entries = g_list_insert_sorted (State->entries, entry, compare_direntry_scores);
+ Ptr += 16;
}
- if (State->DIBoffset < 0) {
- g_set_error_literal (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
- _("Invalid header in icon"));
- return;
- }
+ /* Now go through and find one we can parse */
+ entry = NULL;
+ for (l = State->entries; l != NULL; l = g_list_next (l)) {
+ entry = l->data;
- /* We now have a winner, pointed to in State->DIBoffset,
- so we know how many bytes are in the "header" part. */
-
- State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
+ if (entry->DIBoffset < 0) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Invalid header in icon"));
+ return;
+ }
- if (State->HeaderSize < 0) {
- g_set_error_literal (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
- _("Invalid header in icon"));
- return;
- }
+ /* We know how many bytes are in the "header" part. */
+ State->HeaderSize = entry->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
- if (State->HeaderSize>State->BytesInHeaderBuf) {
- guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
- if (!tmp) {
+ if (State->HeaderSize < 0) {
g_set_error_literal (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
- _("Not enough memory to load icon"));
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Invalid header in icon"));
return;
}
- State->HeaderBuf = tmp;
- State->BytesInHeaderBuf = State->HeaderSize;
- }
- if (Bytes<State->HeaderSize)
- return;
-
- BIH = Data+State->DIBoffset;
+
+ if (State->HeaderSize>State->BytesInHeaderBuf) {
+ guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
+ if (!tmp) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Not enough memory to load icon"));
+ return;
+ }
+ State->HeaderBuf = tmp;
+ State->BytesInHeaderBuf = State->HeaderSize;
+ }
+ if (Bytes<State->HeaderSize)
+ return;
+
+ BIH = Data+entry->DIBoffset;
+
+ /* A compressed icon, try the next one */
+ if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
+ || (BIH[19] != 0))
+ continue;
+
+ /* If we made it to here then we have selected a BIH structure
+ * in a format that we can parse */
+ break;
+ }
+
+ /* No valid icon found, because all are compressed? */
+ if (l == NULL) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Compressed icons are not supported"));
+ return;
+ }
+
+ /* This is the one we're going with */
+ State->DIBoffset = entry->DIBoffset;
+ State->x_hot = entry->x_hot;
+ State->y_hot = entry->y_hot;
#ifdef DUMPBIH
DumpBIH(BIH);
@@ -374,16 +411,6 @@ static void DecodeHeader(guchar *Data, gint Bytes,
if (Bytes < State->HeaderSize)
return;
- if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
- || (BIH[19] != 0)) {
- /* FIXME: is this the correct message? */
- g_set_error_literal (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
- _("Compressed icons are not supported"));
- return;
- }
-
/* Negative heights mean top-down pixel-order */
if (State->Header.height < 0) {
State->Header.height = -State->Header.height;