Index: gdk-pixbuf/io-icns.c
===================================================================
--- gdk-pixbuf/io-icns.c	(revision 0)
+++ gdk-pixbuf/io-icns.c	(revision 0)
@@ -0,0 +1,393 @@
+/* Mac OS X .icns icons loader
+ *
+ * Copyright (c) 2007 Lyonel Vincent <lyonel@ezix.org>
+ * Copyright (c) 2007 Bastien Nocera <hadess@hadess.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "gdk-pixbuf-private.h"
+#include "gdk-pixbuf-io.h"
+
+G_MODULE_EXPORT void fill_vtable (GdkPixbufModule * module);
+G_MODULE_EXPORT void fill_info (GdkPixbufFormat * info);
+
+#define IN /**/
+#define OUT /**/
+#define INOUT /**/
+
+struct IcnsBlockHeader
+{
+  char id[4];
+  guint32 size;			/* caution: bigendian */
+};
+typedef struct IcnsBlockHeader IcnsBlockHeader;
+
+/*
+ * load raw icon data from 'icns' resource
+ *
+ * returns TRUE when successful
+ */
+static gboolean
+load_resources (unsigned size, IN gpointer data, gsize datalen,
+		OUT guchar ** picture, OUT gsize * plen,
+		OUT guchar ** mask, OUT gsize * mlen)
+{
+  IcnsBlockHeader *header = NULL;
+  const char *bytes = NULL;
+  const char *current = NULL;
+  guint32 blocklen = 0;
+  guint32 icnslen = 0;
+  gboolean needs_mask = TRUE;
+
+  if (datalen < 2 * sizeof (guint32))
+    return FALSE;
+  if (!data)
+    return FALSE;
+
+  *picture = *mask = NULL;
+  *plen = *mlen = 0;
+
+  bytes = data;
+  header = (IcnsBlockHeader *) data;
+  if (memcmp (header->id, "icns", 4) != 0)
+    return FALSE;
+
+  icnslen = GUINT32_FROM_BE (header->size);
+  if ((icnslen > datalen) || (icnslen < 2 * sizeof (guint32)))
+    return FALSE;
+
+  current = bytes + sizeof (IcnsBlockHeader);
+  while ((current - bytes < icnslen) && (icnslen - (current - bytes) >= sizeof (IcnsBlockHeader)))
+    {
+      header = (IcnsBlockHeader *) current;
+      blocklen = GUINT32_FROM_BE (header->size);
+
+      /* Check that blocklen isn't garbage */
+      if (blocklen > icnslen - (current - bytes))
+        return FALSE;
+
+      switch (size)
+	{
+	case 256:
+          if (memcmp (header->id, "ic08", 4) == 0)	/* 256x256 icon */
+            {
+	      *picture = (gpointer) (current + sizeof (IcnsBlockHeader));
+	      *plen = blocklen - sizeof (IcnsBlockHeader);
+	    }
+	    needs_mask = FALSE;
+	  break;
+	case 128:
+	  if (memcmp (header->id, "it32", 4) == 0)	/* 128x128 icon */
+	    {
+	      *picture = (gpointer) (current + sizeof (IcnsBlockHeader));
+	      *plen = blocklen - sizeof (IcnsBlockHeader);
+	      if (memcmp (*picture, "\0\0\0\0", 4) == 0)
+		{
+		  *picture += 4;
+		  *plen -= 4;
+		}
+	    }
+	  if (memcmp (header->id, "t8mk", 4) == 0)	/* 128x128 mask */
+	    {
+	      *mask = (gpointer) (current + sizeof (IcnsBlockHeader));
+	      *mlen = blocklen - sizeof (IcnsBlockHeader);
+	    }
+	  break;
+	case 48:
+	  if (memcmp (header->id, "ih32", 4) == 0)	/* 48x48 icon */
+	    {
+	      *picture = (gpointer) (current + sizeof (IcnsBlockHeader));
+	      *plen = blocklen - sizeof (IcnsBlockHeader);
+	    }
+	  if (memcmp (header->id, "h8mk", 4) == 0)	/* 48x48 mask */
+	    {
+	      *mask = (gpointer) (current + sizeof (IcnsBlockHeader));
+	      *mlen = blocklen - sizeof (IcnsBlockHeader);
+	    }
+	  break;
+	case 32:
+	  if (memcmp (header->id, "il32", 4) == 0)	/* 32x32 icon */
+	    {
+	      *picture = (gpointer) (current + sizeof (IcnsBlockHeader));
+	      *plen = blocklen - sizeof (IcnsBlockHeader);
+	    }
+	  if (memcmp (header->id, "l8mk", 4) == 0)	/* 32x32 mask */
+	    {
+	      *mask = (gpointer) (current + sizeof (IcnsBlockHeader));
+	      *mlen = blocklen - sizeof (IcnsBlockHeader);
+	    }
+	  break;
+	case 16:
+	  if (memcmp (header->id, "is32", 4) == 0)	/* 16x16 icon */
+	    {
+	      *picture = (gpointer) (current + sizeof (IcnsBlockHeader));
+	      *plen = blocklen - sizeof (IcnsBlockHeader);
+	    }
+	  if (memcmp (header->id, "s8mk", 4) == 0)	/* 16x16 mask */
+	    {
+	      *mask = (gpointer) (current + sizeof (IcnsBlockHeader));
+	      *mlen = blocklen - sizeof (IcnsBlockHeader);
+	    }
+	  break;
+	default:
+	  return FALSE;
+	}
+
+      current += blocklen;
+    }
+
+  if (!*picture)
+    return FALSE;
+  if (needs_mask && !*mask)
+    return FALSE;
+  return TRUE;
+}
+
+/*
+ * uncompress RLE-encoded bytes into RGBA scratch zone:
+ * if firstbyte >= 0x80, it indicates the number of identical bytes + 125
+ * 	(repeated value is stored next: 1 byte)
+ * otherwise, it indicates the number of non-repeating bytes - 1
+ *	(non-repeating values are stored next: n bytes)
+ */
+static gboolean
+uncompress (unsigned size, INOUT guchar ** source, OUT guchar * target, INOUT gsize * _remaining)
+{
+  guchar *data = *source;
+  gsize remaining;
+  gsize i = 0;
+
+  /* The first time we're called, set remaining */
+  if (*_remaining == 0) {
+    remaining = size * size;
+  } else {
+    remaining = *_remaining;
+  }
+
+  while (remaining > 0)
+    {
+      guint8 count = 0;
+
+      if (data[0] & 0x80)	/* repeating byte */
+	{
+	  count = data[0] - 125;
+
+	  if (count > remaining)
+	    return FALSE;
+
+	  for (i = 0; i < count; i++)
+	    {
+	      *target = data[1];
+	      target += 4;
+	    }
+
+	  data += 2;
+	}
+      else			/* non-repeating bytes */
+	{
+	  count = data[0] + 1;
+
+	  if (count > remaining)
+	    return FALSE;
+
+	  for (i = 0; i < count; i++)
+	    {
+	      *target = data[i + 1];
+	      target += 4;
+	    }
+	  data += count + 1;
+	}
+
+      remaining -= count;
+    }
+
+  *source = data;
+  *_remaining = remaining;
+  return TRUE;
+}
+
+static GdkPixbuf *
+load_icon (unsigned size, IN gpointer data, gsize datalen)
+{
+  guchar *icon = NULL;
+  guchar *mask = NULL;
+  gsize isize = 0, msize = 0, i;
+  guchar *image = NULL;
+
+  if (!load_resources (size, data, datalen, &icon, &isize, &mask, &msize))
+    return NULL;
+
+  /* 256x256 icons don't use RLE or uncompressed data,
+   * They're usually JPEG 2000 images */
+  if (size == 256)
+    {
+      GdkPixbufLoader *loader;
+      GdkPixbuf *pixbuf;
+
+      loader = gdk_pixbuf_loader_new ();
+      if (!gdk_pixbuf_loader_write (loader, icon, isize, NULL)
+	  || !gdk_pixbuf_loader_close (loader, NULL))
+        {
+          g_object_unref (loader);
+          return NULL;
+	}
+
+      pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+      g_object_ref (pixbuf);
+      g_object_unref (loader);
+
+      return pixbuf;
+    }
+
+  g_assert (mask);
+
+  if (msize != size * size)	/* wrong mask size */
+    return NULL;
+
+  image = (guchar *) g_malloc0 (size * size * 4);	/* 4 bytes/pixel = RGBA */
+
+  if (isize == size * size * 4)	/* icon data is uncompressed */
+    for (i = 0; i < size * size; i++)	/* 4 bytes/pixel = ARGB (A: ignored) */
+      {
+	image[i * 4] = icon[4 * i + 1];	/* R */
+	image[i * 4 + 1] = icon[4 * i + 2];	/* G */
+	image[i * 4 + 2] = icon[4 * i + 3];	/* B */
+      }
+  else
+    {
+      guchar *data = icon;
+      gsize remaining = 0;
+
+      /* R */
+      if (!uncompress (size, &data, image, &remaining))
+        goto bail;
+      /* G */
+      if (!uncompress (size, &data, image + 1, &remaining))
+        goto bail;
+      /* B */
+      if (!uncompress (size, &data, image + 2, &remaining))
+        goto bail;
+    }
+
+  for (i = 0; i < size * size; i++)	/* copy mask to alpha channel */
+    image[i * 4 + 3] = mask[i];
+
+  return gdk_pixbuf_new_from_data ((guchar *) image, GDK_COLORSPACE_RGB,	/* RGB image */
+				   TRUE,	/* with alpha channel */
+				   8,	/* 8 bits per sample */
+				   size,	/* width */
+				   size,	/* height */
+				   size * 4,	/* no gap between rows */
+				   (GdkPixbufDestroyNotify)g_free,	/* free() function */
+				   NULL);	/* param to free() function */
+
+bail:
+  g_free (image);
+  return NULL;
+}
+
+static int sizes[] = {
+  256, /* late-Tiger icons */
+  128, /* Standard OS X */
+  48,  /* Not very common */
+  32,  /* Standard Mac OS Classic (8 & 9) */
+  24,  /* OS X toolbars */
+  16   /* used in Mac OS Classic and dialog boxes */
+};
+
+static GdkPixbuf *
+icns_image_load (FILE *f, GError ** error)
+{
+  GByteArray *data;
+  GdkPixbuf *pixbuf = NULL;
+  guint i;
+
+  data = g_byte_array_new ();
+  while (!feof (f))
+    {
+      gint save_errno;
+      guchar buf[4096];
+      gsize bytes;
+
+      bytes = fread (buf, 1, sizeof (buf), f);
+      save_errno = errno;
+      data = g_byte_array_append (data, buf, bytes);
+
+      if (ferror (f))
+        {
+	  g_set_error (error,
+		       G_FILE_ERROR,
+		       g_file_error_from_errno (save_errno),
+		       _("Error reading ICNS image: %s"),
+		       g_strerror (save_errno));
+
+	  g_byte_array_free (data, TRUE);
+
+	  return NULL;
+	}
+    }
+
+  for (i = 0; i < G_N_ELEMENTS(sizes) && !pixbuf; i++)
+    pixbuf = load_icon (sizes[i], data->data, data->len);
+
+  g_byte_array_free (data, TRUE);
+
+  if (!pixbuf)
+    g_set_error (error, GDK_PIXBUF_ERROR,
+		 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+		 _("Could not decode ICNS file"));
+
+  return pixbuf;
+}
+
+void
+fill_vtable (GdkPixbufModule * module)
+{
+  module->load = icns_image_load;
+}
+
+void
+fill_info (GdkPixbufFormat * info)
+{
+  static GdkPixbufModulePattern signature[] = {
+    {"icns", NULL, 100},	/* file begins with 'icns' */
+    {NULL, NULL, 0}
+  };
+  static gchar *mime_types[] = {
+    "image/x-icns",
+    NULL
+  };
+  static gchar *extensions[] = {
+    "icns",
+    NULL
+  };
+
+  info->name = "icns";
+  info->signature = signature;
+  info->description = N_("The ICNS image format");
+  info->mime_types = mime_types;
+  info->extensions = extensions;
+  info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
+  info->license = "GPL";
+  info->disabled = FALSE;
+}
+
Index: gdk-pixbuf/Makefile.am
===================================================================
--- gdk-pixbuf/Makefile.am	(revision 18683)
+++ gdk-pixbuf/Makefile.am	(working copy)
@@ -165,6 +165,14 @@
 libpixbufloader_tga_la_LIBADD = $(module_libs)
 
 #
+# The .icns loader
+#
+libpixbufloader_static_icns_la_SOURCES = io-icns.c
+libpixbufloader_icns_la_SOURCES = io-icns.c
+libpixbufloader_icns_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libpixbufloader_icns_la_LIBADD = $(module_libs)
+
+#
 # The PCX loader
 #
 libstatic_pixbufloader_pcx_la_SOURCES = io-pcx.c
@@ -262,6 +270,12 @@
 PCX_LIB = libpixbufloader-pcx.la
 endif
 
+if INCLUDE_ICNS
+STATIC_ICNS_LIB = libpixbufloader-static-icns.la
+else
+ICNS_LIB = libpixbufloader-icns.la
+endif
+
 if BUILD_DYNAMIC_MODULES
 
 loader_LTLIBRARIES = 	\
@@ -278,6 +292,7 @@
 	$(XPM_LIB)	\
 	$(XBM_LIB)	\
 	$(TGA_LIB)	\
+	$(ICNS_LIB)	\
 	$(PCX_LIB)
 
 
@@ -297,6 +312,7 @@
 	$(STATIC_XPM_LIB)	\
 	$(STATIC_XBM_LIB)	\
 	$(STATIC_TGA_LIB)	\
+	$(STATIC_ICNS_LIB)	\
 	$(STATIC_PCX_LIB)
 
 builtin_objs = @INCLUDED_LOADER_OBJ@
Index: configure.in
===================================================================
--- configure.in	(revision 18683)
+++ configure.in	(working copy)
@@ -886,7 +886,7 @@
    fi
 fi
 
-all_loaders="png,bmp,wbmp,gif,ico,ani,jpeg,pnm,ras,tiff,xpm,xbm,tga,pcx"
+all_loaders="png,bmp,wbmp,gif,ico,ani,jpeg,pnm,ras,tiff,xpm,xbm,tga,pcx,icns"
 included_loaders=""
 # If no loaders specified, include all
 if test "x$with_included_loaders" = xyes ; then
@@ -930,6 +930,7 @@
 AM_CONDITIONAL(INCLUDE_XBM, [test x"$INCLUDE_xbm" = xyes])
 AM_CONDITIONAL(INCLUDE_TGA, [test x"$INCLUDE_tga" = xyes])
 AM_CONDITIONAL(INCLUDE_PCX, [test x"$INCLUDE_pcx" = xyes])
+AM_CONDITIONAL(INCLUDE_ICNS, [test x"$INCLUDE_icns" = xyes])
 
 AC_HEADER_SYS_WAIT
 
