? .icosave.c.swp
Index: Makefile.am
===================================================================
RCS file: /cvs/gnome/gimp/plug-ins/winicon/Makefile.am,v
retrieving revision 1.3
diff -u -p -u -p -r1.3 Makefile.am
--- Makefile.am	4 Feb 2005 22:31:58 -0000	1.3
+++ Makefile.am	3 Oct 2006 07:55:35 -0000
@@ -18,6 +18,8 @@ libexecdir = $(gimpplugindir)/plug-ins
 
 libexec_PROGRAMS = winicon
 
+winicon_CFLAGS = $(PNG_CFLAGS)
+
 winicon_SOURCES = \
 	icodialog.c	\
 	icodialog.h	\
@@ -41,6 +43,7 @@ LDADD = \
 	$(libgimpcolor)		\
 	$(libgimpmath)		\
 	$(libgimpbase)		\
+	$(LIBPNG)		\
 	$(GTK_LIBS)		\
 	$(RT_LIBS)		\
 	$(INTLLIBS)
Index: icodialog.c
===================================================================
RCS file: /cvs/gnome/gimp/plug-ins/winicon/icodialog.c,v
retrieving revision 1.17
diff -u -p -u -p -r1.17 icodialog.c
--- icodialog.c	24 Aug 2006 15:07:03 -0000	1.17
+++ icodialog.c	3 Oct 2006 07:55:35 -0000
@@ -19,22 +19,25 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+
 #include <config.h>
 
 #include <libgimp/gimp.h>
 #include <libgimp/gimpui.h>
 
-#define ICO_DBG
+/* #define ICO_DBG */
 
-#include "icodialog.h"
 #include "main.h"
+#include "icodialog.h"
+#include "icosave.h"
 
 #include "libgimp/stdplugins-intl.h"
 
+static void   ico_bpp_changed (GtkWidget *combo,
+                               GObject   *hbox);
+static void   ico_toggle_compress (GtkWidget *checkbox,
+                                   GObject   *hbox);
 
-static GtkWidget * ico_preview_new   (gint32     layer);
-static void        combo_bpp_changed (GtkWidget *combo,
-                                      GObject   *hbox);
 
 
 static GtkWidget *
@@ -55,75 +58,13 @@ ico_preview_new (gint32 layer)
 }
 
 
-/* This function creates and returns an hbox for an icon,
-   which then gets added to the dialog's main vbox. */
-static GtkWidget *
-ico_create_icon_hbox (GtkWidget *icon_preview,
-                      gint32     layer,
-                      gint       layer_num)
-{
-  static GtkSizeGroup *size = NULL;
-
-  GtkWidget *hbox;
-  GtkWidget *vbox;
-  GtkWidget *alignment;
-  GtkWidget *combo;
-
-  hbox = gtk_hbox_new (FALSE, 6);
-
-  alignment = gtk_alignment_new (1.0, 0.5, 0, 0);
-  gtk_box_pack_start (GTK_BOX (hbox), alignment, FALSE, FALSE, 0);
-  gtk_widget_show (alignment);
-
-  /* To make life easier for the callbacks, we store the
-     layer's ID and stacking number with the hbox. */
-
-  g_object_set_data (G_OBJECT (hbox),
-                     "icon_layer", GINT_TO_POINTER (layer));
-  g_object_set_data (G_OBJECT (hbox),
-                     "icon_layer_num", GINT_TO_POINTER (layer_num));
-
-  g_object_set_data (G_OBJECT (hbox), "icon_preview", icon_preview);
-  gtk_container_add (GTK_CONTAINER (alignment), icon_preview);
-  gtk_widget_show (icon_preview);
-
-  if (! size)
-    size = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
-
-  gtk_size_group_add_widget (size, alignment);
-
-  vbox = gtk_vbox_new (FALSE, 0);
-  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
-  gtk_widget_show (vbox);
-
-  combo = gimp_int_combo_box_new (_("1 bpp, 1-bit alpha, 2-slot palette"),   1,
-                                  _("4 bpp, 1-bit alpha, 16-slot palette"),  4,
-                                  _("8 bpp, 1-bit alpha, 256-slot palette"), 8,
-                                  _("32 bpp, 8-bit alpha, no palette"),      32,
-                                  NULL);
-  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), 32);
-
-  g_signal_connect (combo, "changed",
-                    G_CALLBACK (combo_bpp_changed),
-                    hbox);
-
-  g_object_set_data (G_OBJECT (hbox), "icon_menu", combo);
-
-  gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
-  gtk_widget_show (combo);
-
-  return hbox;
-}
-
-
 GtkWidget *
-ico_specs_dialog_new (gint num_layers)
+ico_dialog_new (IcoSaveInfo *info)
 {
   GtkWidget *dialog;
   GtkWidget *vbox;
   GtkWidget *frame;
   GtkWidget *scrolledwindow;
-  gint      *icon_depths, i;
 
   dialog = gimp_dialog_new (_("Save as Windows Icon"), "winicon",
                             NULL, 0,
@@ -149,11 +90,7 @@ ico_specs_dialog_new (gint num_layers)
      for later comparison.
   */
 
-  icon_depths = g_new (gint, 2 * num_layers);
-  for (i = 0; i < 2 * num_layers; i++)
-    icon_depths[i] = 32;
-
-  g_object_set_data (G_OBJECT (dialog), "icon_depths", icon_depths);
+  g_object_set_data (G_OBJECT (dialog), "save_info", info);
 
   frame = gimp_frame_new (_("Icon Details"));
   gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
@@ -177,13 +114,85 @@ ico_specs_dialog_new (gint num_layers)
   return dialog;
 }
 
+/* This function creates and returns an hbox for an icon,
+   which then gets added to the dialog's main vbox. */
+static GtkWidget *
+ico_create_icon_hbox (GtkWidget   *icon_preview,
+                      gint32       layer,
+                      gint         layer_num,
+                      IcoSaveInfo *info)
+{
+  static GtkSizeGroup *size = NULL;
+
+  GtkWidget *hbox;
+  GtkWidget *vbox;
+  GtkWidget *alignment;
+  GtkWidget *combo;
+  GtkWidget *checkbox;
+
+  hbox = gtk_hbox_new (FALSE, 6);
+
+  alignment = gtk_alignment_new (1.0, 0.5, 0, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), alignment, FALSE, FALSE, 0);
+  gtk_widget_show (alignment);
+
+  /* To make life easier for the callbacks, we store the
+     layer's ID and stacking number with the hbox. */
+
+  g_object_set_data (G_OBJECT (hbox),
+                     "icon_layer", GINT_TO_POINTER (layer));
+  g_object_set_data (G_OBJECT (hbox),
+                     "icon_layer_num", GINT_TO_POINTER (layer_num));
+
+  g_object_set_data (G_OBJECT (hbox), "icon_preview", icon_preview);
+  gtk_container_add (GTK_CONTAINER (alignment), icon_preview);
+  gtk_widget_show (icon_preview);
+
+  if (! size)
+    size = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+  gtk_size_group_add_widget (size, alignment);
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+  gtk_widget_show (vbox);
+
+  combo = gimp_int_combo_box_new (_("1 bpp, 1-bit alpha, 2-slot palette"),   1,
+                                  _("4 bpp, 1-bit alpha, 16-slot palette"),  4,
+                                  _("8 bpp, 1-bit alpha, 256-slot palette"), 8,
+                                  _("24 bpp, 1-bit alpha, no palette"),     24,
+                                  _("32 bpp, 8-bit alpha, no palette"),     32,
+                                  NULL);
+  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+                                 info->depths[layer_num]);
+
+  g_signal_connect (combo, "changed",
+                    G_CALLBACK (ico_bpp_changed),
+                    hbox);
+
+  g_object_set_data (G_OBJECT (hbox), "icon_menu", combo);
+
+  gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
+  gtk_widget_show (combo);
+
+  checkbox = gtk_check_button_new_with_mnemonic ("Compressed (PNG)");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox),
+                                info->compress[layer_num]);
+  g_signal_connect (checkbox, "toggled",
+                    G_CALLBACK (ico_toggle_compress), hbox);
+  gtk_box_pack_start (GTK_BOX (vbox), checkbox, FALSE, FALSE, 0);
+  gtk_widget_show (checkbox);
+
+  return hbox;
+}
+
 static GtkWidget *
-ico_specs_dialog_get_layer_preview (GtkWidget *dialog,
-                                    gint32     layer)
+ico_dialog_get_layer_preview (GtkWidget *dialog,
+                              gint32     layer)
 {
   GtkWidget *preview;
   GtkWidget *icon_hbox;
-  gchar      key[MAXLEN];
+  gchar      key[ICO_MAXBUF];
 
   g_snprintf (key, sizeof (key), "layer_%i_hbox", layer);
   icon_hbox = g_object_get_data (G_OBJECT (dialog), key);
@@ -205,34 +214,12 @@ ico_specs_dialog_get_layer_preview (GtkW
   return preview;
 }
 
-void
-ico_specs_dialog_add_icon (GtkWidget *dialog,
-                           gint32     layer,
-                           gint       layer_num)
-{
-  GtkWidget *vbox;
-  GtkWidget *hbox;
-  GtkWidget *preview;
-  gchar      key[MAXLEN];
-
-  vbox = g_object_get_data (G_OBJECT (dialog), "icons_vbox");
-
-  preview = ico_preview_new (layer);
-  hbox = ico_create_icon_hbox (preview, layer, layer_num);
-  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
-  gtk_widget_show (hbox);
-
-  /* Let's make the hbox accessible through the layer ID */
-  g_snprintf (key, sizeof (key), "layer_%i_hbox", layer);
-  g_object_set_data (G_OBJECT (dialog), key, hbox);
-}
-
-void
-ico_specs_dialog_update_icon_preview (GtkWidget *dialog,
-                                      gint32     layer,
-                                      gint       bpp)
+static void
+ico_dialog_update_icon_preview (GtkWidget *dialog,
+                                gint32     layer,
+                                gint       bpp)
 {
-  GtkWidget *preview = ico_specs_dialog_get_layer_preview (dialog, layer);
+  GtkWidget *preview = ico_dialog_get_layer_preview (dialog, layer);
   GdkPixbuf *pixbuf;
   gint       w       = gimp_drawable_width (layer);
   gint       h       = gimp_drawable_height (layer);
@@ -249,6 +236,101 @@ ico_specs_dialog_update_icon_preview (Gt
       gint32        tmp_image;
       gint32        tmp_layer;
       guchar       *buffer;
+      guchar       *cmap;
+      gint          num_colors;
+
+      image = gimp_drawable_get_image (layer);
+
+      tmp_image = gimp_image_new (w, h, gimp_image_base_type (image));
+      gimp_image_undo_disable (tmp_image);
+
+      if (gimp_drawable_is_indexed (layer))
+        {
+          cmap = gimp_image_get_colormap (image, &num_colors);
+          gimp_image_set_colormap (tmp_image, cmap, num_colors);
+          g_free (cmap);
+        }
+
+      tmp_layer = gimp_layer_new (tmp_image, "temporary", w, h,
+                                  gimp_drawable_type (layer),
+                                  100, GIMP_NORMAL_MODE);
+      gimp_image_add_layer (tmp_image, tmp_layer, 0);
+
+      drawable = gimp_drawable_get (layer);
+      tmp      = gimp_drawable_get (tmp_layer);
+
+      gimp_pixel_rgn_init (&src_pixel_rgn, drawable, 0, 0, w, h, FALSE, FALSE);
+      gimp_pixel_rgn_init (&dst_pixel_rgn, tmp,      0, 0, w, h, TRUE, FALSE);
+
+      buffer = g_malloc (w * h * 4);
+      gimp_pixel_rgn_get_rect (&src_pixel_rgn, buffer, 0, 0, w, h);
+      gimp_pixel_rgn_set_rect (&dst_pixel_rgn, buffer, 0, 0, w, h);
+
+      gimp_drawable_detach (tmp);
+      gimp_drawable_detach (drawable);
+
+      if (gimp_drawable_is_indexed (layer))
+        gimp_image_convert_rgb (tmp_image);
+
+      gimp_image_convert_indexed (tmp_image,
+                                  GIMP_FS_DITHER, GIMP_MAKE_PALETTE,
+                                  1 <<bpp, TRUE, FALSE, "dummy");
+
+      cmap = gimp_image_get_colormap (tmp_image, &num_colors);
+      if ( num_colors == (1 << bpp) &&
+           !ico_cmap_contains_black (cmap, num_colors))
+        {
+          /* Windows icons with color maps need the color black.
+           * We need to eliminate one more color to make room for black.
+           */
+          if (gimp_drawable_is_indexed (layer))
+            {
+              g_free (cmap);
+              cmap = gimp_image_get_colormap (image, &num_colors);
+              gimp_image_set_colormap (tmp_image, cmap, num_colors);
+            }
+          else if (gimp_drawable_is_gray (layer))
+            {
+              gimp_image_convert_grayscale (tmp_image);
+            }
+          else
+            {
+              gimp_image_convert_rgb (tmp_image);
+            }
+
+          tmp = gimp_drawable_get (tmp_layer);
+          gimp_pixel_rgn_init (&dst_pixel_rgn,
+                               tmp, 0, 0, w, h, TRUE, FALSE);
+          gimp_pixel_rgn_set_rect (&dst_pixel_rgn, buffer, 0, 0, w, h);
+          gimp_drawable_detach (tmp);
+
+          if (!gimp_drawable_is_rgb (layer))
+            gimp_image_convert_rgb (tmp_image);
+
+          gimp_image_convert_indexed (tmp_image,
+                                      GIMP_FS_DITHER, GIMP_MAKE_PALETTE,
+                                      (1<<bpp) - 1, TRUE, FALSE, "dummy");
+        }
+      g_free (cmap);
+      g_free (buffer);
+
+      pixbuf = gimp_drawable_get_thumbnail (tmp_layer,
+                                            MIN (w, 128), MIN (h, 128),
+                                            GIMP_PIXBUF_SMALL_CHECKS);
+
+      gimp_image_delete (tmp_image);
+    }
+  else if (bpp == 24)
+    {
+      GimpDrawable *drawable;
+      GimpDrawable *tmp;
+      GimpPixelRgn  src_pixel_rgn, dst_pixel_rgn;
+      gint32        image;
+      gint32        tmp_image;
+      gint32        tmp_layer;
+      guchar       *buffer;
+      GimpParam    *return_vals;
+      gint          n_return_vals;
 
       image = gimp_drawable_get_image (layer);
 
@@ -287,9 +369,14 @@ ico_specs_dialog_update_icon_preview (Gt
       if (gimp_drawable_is_indexed (layer))
         gimp_image_convert_rgb (tmp_image);
 
-      gimp_image_convert_indexed (tmp_image,
-                                  GIMP_FS_DITHER, GIMP_MAKE_PALETTE,
-                                  1 << bpp, TRUE, FALSE, "dummy");
+      return_vals =
+        gimp_run_procedure ("plug-in-threshold-alpha", &n_return_vals,
+                            GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+                            GIMP_PDB_IMAGE, tmp_image,
+                            GIMP_PDB_DRAWABLE, tmp_layer,
+                            GIMP_PDB_INT32, ICO_ALPHA_THRESHOLD,
+                            GIMP_PDB_END);
+      gimp_destroy_params (return_vals, n_return_vals);
 
       pixbuf = gimp_drawable_get_thumbnail (tmp_layer,
                                             MIN (w, 128), MIN (h, 128),
@@ -308,30 +395,75 @@ ico_specs_dialog_update_icon_preview (Gt
   g_object_unref (pixbuf);
 }
 
+void
+ico_dialog_add_icon (GtkWidget *dialog,
+                     gint32     layer,
+                     gint       layer_num)
+{
+  GtkWidget   *vbox;
+  GtkWidget   *hbox;
+  GtkWidget   *preview;
+  gchar        key[ICO_MAXBUF];
+  IcoSaveInfo *info;
+
+  vbox = g_object_get_data (G_OBJECT (dialog), "icons_vbox");
+  info = g_object_get_data (G_OBJECT (dialog), "save_info");
+
+  preview = ico_preview_new (layer);
+  hbox = ico_create_icon_hbox (preview, layer, layer_num, info);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+  gtk_widget_show (hbox);
+
+  /* Let's make the hbox accessible through the layer ID */
+  g_snprintf (key, sizeof (key), "layer_%i_hbox", layer);
+  g_object_set_data (G_OBJECT (dialog), key, hbox);
+
+  ico_dialog_update_icon_preview (dialog, layer, info->depths[layer_num]);
+}
+
 static void
-combo_bpp_changed (GtkWidget *combo,
-                   GObject   *hbox)
+ico_bpp_changed (GtkWidget *combo,
+                 GObject   *hbox)
 {
-  GtkWidget *dialog = gtk_widget_get_toplevel (combo);
-  gint32     layer;
-  gint       layer_num;
-  gint       bpp;
-  gint      *icon_depths;
+  GtkWidget   *dialog;
+  gint32       layer;
+  gint         layer_num;
+  gint         bpp;
+  IcoSaveInfo *info;
+
+
+  dialog = gtk_widget_get_toplevel (combo);
+
 
   gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &bpp);
 
-  icon_depths = g_object_get_data (G_OBJECT (dialog), "icon_depths");
-  if (! icon_depths)
-    {
-      D(("Something's wrong -- can't get icon_depths array from dialog\n"));
-      return;
-    }
+  info = g_object_get_data (G_OBJECT (dialog), "save_info");
+  g_assert (info);
 
   layer     = GPOINTER_TO_INT (g_object_get_data (hbox, "icon_layer"));
   layer_num = GPOINTER_TO_INT (g_object_get_data (hbox, "icon_layer_num"));
 
   /* Update vector entry for later when we're actually saving,
      and update the preview right away ... */
-  icon_depths[layer_num] = bpp;
-  ico_specs_dialog_update_icon_preview (dialog, layer, bpp);
+  info->depths[layer_num] = bpp;
+  ico_dialog_update_icon_preview (dialog, layer, bpp);
+}
+
+static void
+ico_toggle_compress (GtkWidget *checkbox,
+                     GObject   *hbox)
+{
+  GtkWidget   *dialog;
+  gint         layer_num;
+  IcoSaveInfo *info;
+
+  dialog = gtk_widget_get_toplevel (checkbox);
+
+  info = g_object_get_data (G_OBJECT (dialog), "save_info");
+  g_assert (info);
+  layer_num = GPOINTER_TO_INT (g_object_get_data (hbox, "icon_layer_num"));
+
+  /* Update vector entry for later when we're actually saving */
+  info->compress[layer_num] =
+    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox));
 }
Index: icodialog.h
===================================================================
RCS file: /cvs/gnome/gimp/plug-ins/winicon/icodialog.h,v
retrieving revision 1.2
diff -u -p -u -p -r1.2 icodialog.h
--- icodialog.h	10 Nov 2004 14:44:58 -0000	1.2
+++ icodialog.h	3 Oct 2006 07:55:35 -0000
@@ -23,13 +23,9 @@
 #define __ICO_DIALOG_H__
 
 
-GtkWidget * ico_specs_dialog_new                 (gint       num_layers);
-void        ico_specs_dialog_add_icon            (GtkWidget *dialog,
-                                                  gint32     layer,
-                                                  gint       layer_num);
-void        ico_specs_dialog_update_icon_preview (GtkWidget *dialog,
-                                                  gint32     layer,
-                                                  gint       bpp);
-
+GtkWidget * ico_dialog_new                 (IcoSaveInfo *info);
+void        ico_dialog_add_icon            (GtkWidget *dialog,
+                                            gint32     layer,
+                                            gint       layer_num);
 
 #endif /* __ICO_DIALOG_H__ */
Index: icoload.c
===================================================================
RCS file: /cvs/gnome/gimp/plug-ins/winicon/icoload.c,v
retrieving revision 1.16
diff -u -p -u -p -r1.16 icoload.c
--- icoload.c	28 Aug 2006 08:11:57 -0000	1.16
+++ icoload.c	3 Oct 2006 07:55:35 -0000
@@ -27,10 +27,13 @@
 #include <glib/gstdio.h>
 
 #include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+#include <png.h>
 
 /* #define ICO_DBG */
 
 #include "main.h"
+#include "icoload.h"
 
 #include "libgimp/stdplugins-intl.h"
 
@@ -55,13 +58,6 @@ static gint       ico_read_int16 (FILE  
 static gint       ico_read_int32 (FILE        *fp,
                                   guint32     *data,
                                   gint         count);
-static gboolean   ico_init       (const gchar *filename,
-                                  MsIcon      *ico);
-static void       ico_read_entry (MsIcon      *ico,
-                                  MsIconEntry *entry);
-static void       ico_read_data  (MsIcon      *ico,
-                                  gint         icon_num);
-
 
 static gint
 ico_read_int32 (FILE    *fp,
@@ -124,128 +120,211 @@ ico_read_int8 (FILE   *fp,
 }
 
 
-static gboolean
-ico_init (const gchar *filename,
-          MsIcon      *ico)
+static guint32
+ico_read_init (FILE *fp)
 {
-  memset (ico, 0, sizeof (MsIcon));
-
-  if (! (ico->fp = g_fopen (filename, "rb")))
-    {
-      g_message (_("Could not open '%s' for reading: %s"),
-                 gimp_filename_to_utf8 (filename), g_strerror (errno));
-      return FALSE;
-    }
-
-  ico->filename = filename;
-
-  ico->cp += ico_read_int16 (ico->fp, &ico->reserved, 1);
-  ico->cp += ico_read_int16 (ico->fp, &ico->resource_type, 1);
-
-  /* Icon files use 1 as resource type, that's  what I wrote this for.
-     From descriptions on the web it seems as if this loader should
-     also be able to handle Win 3.11 - Win 95 cursor files (.cur),
-     which use resource type 2. I haven't tested this though. */
-  if (ico->reserved != 0 ||
-      (ico->resource_type != 1 && ico->resource_type != 2))
+  IcoFileHeader header;
+  /* read and check file header */
+  if (!ico_read_int16 (fp, &header.reserved, 1)
+      || !ico_read_int16 (fp, &header.resource_type, 1)
+      || !ico_read_int16 (fp, &header.icon_count, 1)
+      || header.reserved != 0
+      || header.resource_type != 1)
     {
-      ico_cleanup (ico);
-      return FALSE;
+      return 0;
     }
-
-  return TRUE;
+  return header.icon_count;
 }
 
 
-static void
-ico_read_entry (MsIcon      *ico,
-                MsIconEntry *entry)
+gboolean
+ico_read_size (FILE        *fp,
+               IcoLoadInfo *info)
 {
-  g_return_if_fail (ico != NULL);
-  g_return_if_fail (entry != NULL);
-
-  ico->cp += ico_read_int8 (ico->fp, &entry->width, 1);
-  ico->cp += ico_read_int8 (ico->fp, &entry->height, 1);
-  ico->cp += ico_read_int8 (ico->fp, &entry->num_colors, 1);
-  ico->cp += ico_read_int8 (ico->fp, &entry->reserved, 1);
+  png_structp png_ptr;
+  png_infop   info_ptr;
+  png_uint_32 w, h;
+  gint32      bpp;
+  gint32      color_type;
+  guint32     magic;
 
-  ico->cp += ico_read_int16 (ico->fp, &entry->num_planes, 1);
-  ico->cp += ico_read_int16 (ico->fp, &entry->bpp, 1);
+  if ( fseek (fp, info->offset, SEEK_SET) < 0 )
+    return FALSE;
 
-  ico->cp += ico_read_int32 (ico->fp, &entry->size, 1);
-  ico->cp += ico_read_int32 (ico->fp, &entry->offset, 1);
-
-  D(("Read entry with w: "
-     "%i, h: %i, num_colors: %i, bpp: %i, size %i, offset %i\n",
-     entry->width, entry->height, entry->num_colors, entry->bpp,
-     entry->size, entry->offset));
+  ico_read_int32 (fp, &magic, 1);
+  if (magic == ICO_PNG_MAGIC)
+    {
+      png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL,
+                                        NULL);
+      if ( !png_ptr )
+        return FALSE;
+      info_ptr = png_create_info_struct (png_ptr);
+      if ( !info_ptr )
+        {
+          png_destroy_read_struct (&png_ptr, NULL, NULL);
+          return FALSE;
+        }
+      if (setjmp (png_jmpbuf (png_ptr)))
+        {
+          png_destroy_read_struct (&png_ptr, NULL, NULL);
+          return FALSE;
+        }
+      png_init_io (png_ptr, fp);
+      png_set_sig_bytes (png_ptr, 4);
+      png_read_info (png_ptr, info_ptr);
+      png_get_IHDR (png_ptr, info_ptr, &w, &h, &bpp, &color_type,
+                    NULL, NULL, NULL);
+      png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+      info->width = w;
+      info->height = h;
+      D(("ico_read_size: PNG: %ix%i\n", info->width, info->height));
+      return TRUE;
+    }
+  else
+    {
+      if (ico_read_int32 (fp, &info->width, 1)
+          && ico_read_int32 (fp, &info->height, 1))
+        {
+          info->height /= 2;
+          D(("ico_read_size: ICO: %ix%i\n", info->width, info->height));
+          return TRUE;
+        }
+      else
+        {
+          info->width = 0;
+          info->height = 0;
+          return FALSE;
+        }
+    }
 }
 
+static IcoLoadInfo*
+ico_read_info (FILE *fp,
+               gint  icon_count)
+{
+  gint            i;
+  IcoFileEntry   *entries;
+  IcoLoadInfo    *info;
+
+  /* read icon entries */
+  entries = g_new (IcoFileEntry, icon_count);
+  if ( fread (entries, sizeof(IcoFileEntry), icon_count, fp) <= 0 )
+    {
+      g_free (entries);
+      return NULL;
+    }
 
-static void
-ico_read_data (MsIcon *ico,
-               gint    icon_num)
-{
-  MsIconData *data;
-  MsIconEntry *entry;
-  gint length;
-
-  g_return_if_fail (ico != NULL);
-
-  D(("Reading data for icon %i ------------------------------\n", icon_num));
-
-  entry = &ico->icon_dir[icon_num];
-  data = &ico->icon_data[icon_num];
-
-  ico->cp = entry->offset;
-
-  if (fseek (ico->fp, entry->offset, SEEK_SET) < 0)
-    return;
-
-  D(("  starting at offset %i\n", entry->offset));
-
-  ico->cp += ico_read_int32 (ico->fp, &data->header_size, 1);
-  ico->cp += ico_read_int32 (ico->fp, &data->width, 1);
-  ico->cp += ico_read_int32 (ico->fp, &data->height, 1);
+  info = g_new (IcoLoadInfo, icon_count);
+  for (i = 0; i < icon_count; i++)
+    {
+      info[i].width = entries[i].width;
+      info[i].height = entries[i].height;
+      info[i].bpp = GUINT16_FROM_LE (entries[i].bpp);
+      info[i].size = GUINT32_FROM_LE (entries[i].size);
+      info[i].offset = GUINT32_FROM_LE (entries[i].offset);
 
-  ico->cp += ico_read_int16 (ico->fp, &data->planes, 1);
-  ico->cp += ico_read_int16 (ico->fp, &data->bpp, 1);
+      if (info[i].width == 0 || info[i].height == 0)
+        {
+          ico_read_size (fp, info+i);
+        }
 
-  ico->cp += ico_read_int32 (ico->fp, &data->compression, 1);
-  ico->cp += ico_read_int32 (ico->fp, &data->image_size, 1);
-  ico->cp += ico_read_int32 (ico->fp, &data->x_res, 1);
-  ico->cp += ico_read_int32 (ico->fp, &data->y_res, 1);
-  ico->cp += ico_read_int32 (ico->fp, &data->used_clrs, 1);
-  ico->cp += ico_read_int32 (ico->fp, &data->important_clrs, 1);
+      D(("ico_read_info: %ix%i (%i bits, size: %i, offset: %i)\n",
+         info[i].width, info[i].height, info[i].bpp,
+         info[i].size, info[i].offset));
+    }
 
-  D(("  header size %i, "
-     "w %i, h %i, planes %i, size %i, bpp %i, used %i, imp %i.\n",
-     data->header_size, data->width, data->height,
-     data->planes, data->image_size, data->bpp,
-     data->used_clrs, data->important_clrs));
+  g_free (entries);
+  return info;
+}
 
-  if (data->bpp <= 8)
+static gboolean
+ico_read_png (FILE    *fp,
+              guint32  header,
+              guchar  *buffer,
+              gint     maxsize,
+              gint    *width,
+              gint    *height)
+{
+  png_structp   png_ptr;
+  png_infop     info;
+  png_uint_32   w;
+  png_uint_32   h;
+  gint32        bit_depth;
+  gint32        color_type;
+  guint32     **rows;
+  gint          i;
+
+  png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+  if ( !png_ptr )
+    return FALSE;
+  info = png_create_info_struct (png_ptr);
+  if ( !info )
     {
-      if (data->used_clrs == 0)
-        data->used_clrs = (1 << data->bpp);
-
-      D(("  allocating a %i-slot palette for %i bpp.\n",
-         data->used_clrs, data->bpp));
+      png_destroy_read_struct (&png_ptr, NULL, NULL);
+      return FALSE;
+    }
 
-      data->palette = g_new0 (guint32, data->used_clrs);
-      ico->cp += ico_read_int8 (ico->fp,
-                                (guint8 *) data->palette, data->used_clrs * 4);
+  if (setjmp (png_jmpbuf (png_ptr)))
+    {
+      png_destroy_read_struct (&png_ptr, &info, NULL);
+      return FALSE;
     }
 
-  data->xor_map = ico_alloc_map (entry->width, entry->height,
-                                 data->bpp, &length);
-  ico->cp += ico_read_int8 (ico->fp, data->xor_map, length);
-  D(("  length of xor_map: %i\n", length));
+  png_init_io (png_ptr, fp);
+  png_set_sig_bytes (png_ptr, 4);
+  png_read_info (png_ptr, info);
+  png_get_IHDR (png_ptr, info, &w, &h, &bit_depth, &color_type,
+                NULL, NULL, NULL);
+  if (w*h*4 > maxsize)
+    {
+      png_destroy_read_struct (&png_ptr, &info, NULL);
+      return FALSE;
+    }
+  D(("ico_read_png: %ix%i, %i bits, %i type\n", (gint)w, (gint)h,
+     bit_depth, color_type));
+  switch (color_type)
+    {
+    case PNG_COLOR_TYPE_GRAY:
+      png_set_gray_1_2_4_to_8 (png_ptr);
+      if ( bit_depth == 16 )
+        png_set_strip_16 (png_ptr);
+      png_set_gray_to_rgb (png_ptr);
+      png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER);
+      break;
+    case PNG_COLOR_TYPE_GRAY_ALPHA:
+      png_set_gray_1_2_4_to_8 (png_ptr);
+      if ( bit_depth == 16 )
+        png_set_strip_16 (png_ptr);
+      png_set_gray_to_rgb (png_ptr);
+      break;
+    case PNG_COLOR_TYPE_PALETTE:
+      png_set_palette_to_rgb (png_ptr);
+      if (png_get_valid (png_ptr, info, PNG_INFO_tRNS))
+        png_set_tRNS_to_alpha (png_ptr);
+      else
+        png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER);
+      break;
+    case PNG_COLOR_TYPE_RGB:
+      if (bit_depth == 16)
+        png_set_strip_16 (png_ptr);
+      png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER);
+      break;
+    case PNG_COLOR_TYPE_RGB_ALPHA:
+      if (bit_depth == 16)
+        png_set_strip_16 (png_ptr);
+      break;
+    }
 
-  /* Read in and_map. It's padded out to 32 bits per line: */
-  data->and_map = ico_alloc_map (entry->width, entry->height, 1, &length);
-  ico->cp += ico_read_int8 (ico->fp, data->and_map, length);
-  D(("  length of and_map: %i\n", length));
+  *width = w;
+  *height = h;
+  rows = g_new (guint32*, h);
+  rows[0] = (guint32*) buffer;
+  for (i = 1; i < h; i++)
+    rows[i] = rows[i-1] + w;
+  png_read_image (png_ptr, (png_bytepp) rows);
+  png_destroy_read_struct (&png_ptr, &info, NULL);
+  g_free (rows);
+  return TRUE;
 }
 
 gint
@@ -294,9 +373,9 @@ ico_get_nibble_from_data (const guint8 *
 }
 
 gint
-ico_get_byte_from_data (guint8 *data,
-                        gint    line_width,
-                        gint    byte)
+ico_get_byte_from_data (const guint8 *data,
+                        gint          line_width,
+                        gint          byte)
 {
   gint line;
   gint width32;
@@ -310,47 +389,89 @@ ico_get_byte_from_data (guint8 *data,
   return data[line * width32 * 4 + offset];
 }
 
-static gint32
-ico_load_layer (gint32  image,
-                MsIcon *ico,
-                gint    i)
-{
-  GimpDrawable *drawable;
-  GimpPixelRgn  pixel_rgn;
-  MsIconData   *idata;
-  MsIconEntry  *ientry;
-  guint8       *xor_map;
-  guint8       *and_map;
-  guint32      *palette;
-  guint32      *dest_vec;
-  gint32        layer;
-  gchar         buf[MAXLEN];
-  gint          x, y, w, h;
+static gboolean
+ico_read_icon (FILE    *fp,
+               guint32  header_size,
+               guchar  *buffer,
+               gint     maxsize,
+               gint    *width,
+               gint    *height)
+{
+  IcoFileDataHeader   data;
+  gint                length;
+  gint                x, y, w, h;
+  gint                bytespp;
+  guchar             *xor_map, *and_map;
+  guint32            *palette;
+  guint32            *dest_vec;
+
+  palette = NULL;
+
+  data.header_size = header_size;
+  ico_read_int32 (fp, &data.width, 1);
+  ico_read_int32 (fp, &data.height, 1);
+  ico_read_int16 (fp, &data.planes, 1);
+  ico_read_int16 (fp, &data.bpp, 1);
+  ico_read_int32 (fp, &data.compression, 1);
+  ico_read_int32 (fp, &data.image_size, 1);
+  ico_read_int32 (fp, &data.x_res, 1);
+  ico_read_int32 (fp, &data.y_res, 1);
+  ico_read_int32 (fp, &data.used_clrs, 1);
+  ico_read_int32 (fp, &data.important_clrs, 1);
+
+  D(("  header size %i, "
+     "w %i, h %i, planes %i, size %i, bpp %i, used %i, imp %i.\n",
+     data.header_size, data.width, data.height,
+     data.planes, data.image_size, data.bpp,
+     data.used_clrs, data.important_clrs));
+
+  if (data.planes != 1
+      || data.compression != 0)
+    {
+      D(("skipping image: invalid header\n"));
+      return FALSE;
+    }
 
-  idata = &ico->icon_data[i];
-  ientry = &ico->icon_dir[i];
-  xor_map = ico->icon_data[i].xor_map;
-  and_map = ico->icon_data[i].and_map;
-  palette = ico->icon_data[i].palette;
-  w = ico->icon_dir[i].width;
-  h = ico->icon_dir[i].height;
+  if (data.bpp != 1 && data.bpp != 4
+      && data.bpp != 8 && data.bpp != 24
+      && data.bpp != 32)
+    {
+      D(("skipping image: invalid depth: %i\n", data.bpp));
+      return FALSE;
+    }
 
-  gimp_progress_update ((gdouble) i / (gdouble) ico->icon_count);
+  if (data.width * data.height * 2 > maxsize)
+    {
+      D(("skipping image: too large\n"));
+      return FALSE;
+    }
 
-  if (w <= 0 || h <= 0)
-    return -1;
+  w = data.width;
+  h = data.height / 2;
 
-  g_snprintf (buf, sizeof (buf), _("Icon #%i"), i + 1);
+  if (data.bpp <= 8)
+    {
+      if (data.used_clrs == 0)
+        data.used_clrs = (1 << data.bpp);
 
-  layer = gimp_layer_new (image, buf, w, h,
-                          GIMP_RGBA_IMAGE, 100, GIMP_NORMAL_MODE);
+      D(("  allocating a %i-slot palette for %i bpp.\n",
+         data.used_clrs, data.bpp));
 
-  gimp_image_add_layer (image, layer, i);
-  drawable = gimp_drawable_get (layer);
+      palette = g_new0 (guint32, data.used_clrs);
+      ico_read_int8 (fp, (guint8 *) palette, data.used_clrs * 4);
+    }
 
-  dest_vec = g_new (guint32, w * h);
+  xor_map = ico_alloc_map (w, h, data.bpp, &length);
+  ico_read_int8 (fp, xor_map, length);
+  D(("  length of xor_map: %i\n", length));
 
-  switch (idata->bpp)
+  /* Read in and_map. It's padded out to 32 bits per line: */
+  and_map = ico_alloc_map (w, h, 1, &length);
+  ico_read_int8 (fp, and_map, length);
+  D(("  length of and_map: %i\n", length));
+
+  dest_vec = (guint32 *) buffer;
+  switch (data.bpp)
     {
     case 1:
       for (y = 0; y < h; y++)
@@ -411,7 +532,7 @@ ico_load_layer (gint32  image,
 
     default:
       {
-        gint bytespp = idata->bpp/8;
+        gint bytespp = data.bpp/8;
 
         for (y = 0; y < h; y++)
           for (x = 0; x < w; x++)
@@ -422,7 +543,7 @@ ico_load_layer (gint32  image,
               G_VAL_GIMP (dest) = xor_map[(y * w + x) * bytespp + 1];
               R_VAL_GIMP (dest) = xor_map[(y * w + x) * bytespp + 2];
 
-              if (idata->bpp < 32)
+              if (data.bpp < 32)
                 {
                   if (ico_get_bit_from_data (and_map, w, y * w + x))
                     A_VAL_GIMP (dest) = 0;
@@ -436,79 +557,134 @@ ico_load_layer (gint32  image,
             }
       }
     }
+  if (palette)
+    g_free (palette);
+  g_free (xor_map);
+  g_free (and_map);
+  *width = w;
+  *height = h;
+  return TRUE;
+}
 
-  gimp_pixel_rgn_init (&pixel_rgn, drawable,
-                       0, 0, drawable->width, drawable->height,
-                       TRUE, FALSE);
-  gimp_pixel_rgn_set_rect (&pixel_rgn, (const guchar *) dest_vec,
-                           0, 0, drawable->width, drawable->height);
+gint32
+ico_load_layer (FILE        *fp,
+                gint32       image,
+                gint32       icon_num,
+                guchar      *buffer,
+                gint         maxsize,
+                IcoLoadInfo *info)
+{
+  gint          width, height;
+  gint32        layer;
+  guint32       first_bytes;
+  GimpDrawable *drawable;
+  GimpPixelRgn  pixel_rgn;
+  gchar         buf [ICO_MAXBUF];
 
-  g_free (dest_vec);
+  if ( fseek (fp, info->offset, SEEK_SET) < 0
+       || !ico_read_int32 (fp, &first_bytes, 1) )
+    return -1;
 
+  if (first_bytes == ICO_PNG_MAGIC)
+    {
+      if (!ico_read_png (fp, first_bytes, buffer, maxsize, &width, &height))
+        return -1;
+    }
+  else
+    {
+      if (!ico_read_icon (fp, first_bytes, buffer, maxsize, &width, &height))
+        return -1;
+    }
+
+
+  /* read successfully. add to image */
+  g_snprintf (buf, sizeof (buf), _("Icon #%i"), icon_num+1);
+  layer = gimp_layer_new (image, buf, width, height,
+                          GIMP_RGBA_IMAGE, 100, GIMP_NORMAL_MODE);
+  gimp_image_add_layer (image, layer, icon_num);
+  drawable = gimp_drawable_get (layer);
+  gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
+                       drawable->width, drawable->height, TRUE, FALSE);
+  gimp_pixel_rgn_set_rect (&pixel_rgn, (const guchar*) buffer,
+                           0, 0, drawable->width,
+                           drawable->height);
   gimp_drawable_detach (drawable);
 
   return layer;
 }
 
+
 gint32
 ico_load_image (const gchar *filename)
 {
-  MsIcon  ico;
-  gint32  image;
-  gint32  layer;
-  gint    width  = 0;
-  gint    height = 0;
-  gint    i;
+  FILE        *fp;
+  IcoLoadInfo *info;
+  gint         max_width, max_height;
+  gint         i;
+  gint32       image;
+  gint32       layer;
+  guchar      *buffer;
+  guint        icon_count;
+  gint         maxsize;
 
   gimp_progress_init_printf (_("Opening '%s'"),
                              gimp_filename_to_utf8 (filename));
 
-  if (! ico_init (filename, &ico))
-    return -1;
-
-  ico.cp += ico_read_int16 (ico.fp, &ico.icon_count, 1);
-  ico.icon_dir  = g_new0 (MsIconEntry, ico.icon_count);
-  ico.icon_data = g_new0 (MsIconData, ico.icon_count);
-
-  D(("*** %s: Microsoft icon file, containing %i icon(s)\n",
-         ico.filename, ico.icon_count));
-
-  for (i = 0; i < ico.icon_count; i++)
-    ico_read_entry (&ico, &ico.icon_dir[i]);
-
-  /* Do a quick scan of the icons in the file to find the largest icon */
-  for (i = 0; i < ico.icon_count; i++)
+  fp = g_fopen (filename, "rb");
+  if ( !fp )
     {
-      if (ico.icon_dir[i].width > width)
-        width = ico.icon_dir[i].width;
-
-      if (ico.icon_dir[i].height > height)
-        height = ico.icon_dir[i].height;
+      g_message (_("Could not open '%s' for reading: %s"),
+                 gimp_filename_to_utf8 (filename), g_strerror (errno));
+      return -1;
     }
 
-  if (width < 1 || height < 1)
-    return -1;
-
-  image = gimp_image_new (width, height, GIMP_RGB);
-  gimp_image_set_filename (image, ico.filename);
+  icon_count = ico_read_init (fp);
+  if (!icon_count)
+    {
+      fclose (fp);
+      return -1;
+    }
 
-  /* Scan icons again and set up a layer for each icon */
-  for (i = 0; i < ico.icon_count; i++)
-    ico_read_data (&ico, i);
+  info = ico_read_info (fp, icon_count);
+  if (!info)
+    {
+      fclose (fp);
+      return -1;
+    }
 
-  layer = ico_load_layer (image, &ico, 0);
-  for (i = 1; i < ico.icon_count; i++)
-    ico_load_layer (image, &ico, i);
+  /* find width and height of image */
+  max_width = 0;
+  max_height = 0;
+  for (i = 0; i < icon_count; i++)
+    {
+      if ( info[i].width > max_width )
+        max_width = info[i].width;
+      if ( info[i].height > max_height )
+        max_height = info[i].height;
+    }
+  if ( max_width <= 0 || max_height <= 0 )
+    {
+      g_free (info);
+      fclose (fp);
+      return -1;
+    }
+  D(("image size: %ix%i\n", max_width, max_height));
 
-  if (layer != -1)
-    gimp_image_set_active_layer (image, layer);
+  image = gimp_image_new (max_width, max_height, GIMP_RGB);
+  gimp_image_set_filename (image, filename);
 
-  D(("*** icon successfully loaded.\n\n"));
+  maxsize = max_width * max_height * 4;
+  buffer = g_new (guchar, max_width * max_height * 4);
+  for (i = 0; i < icon_count; i++)
+    {
+      layer = ico_load_layer (fp, image, i, buffer, maxsize, info+i);
+    }
+  g_free (buffer);
+  g_free (info);
+  fclose (fp);
 
   gimp_progress_update (1.0);
 
-  ico_cleanup (&ico);
-
   return image;
 }
 
@@ -517,60 +693,74 @@ ico_load_thumbnail_image (const gchar *f
                           gint        *width,
                           gint        *height)
 {
-  MsIcon  ico;
-  gint32  image;
-  gint32  layer;
-  gint    w     = 0;
-  gint    h     = 0;
-  gint    match = 0;
-  gint    i;
+  FILE        *fp;
+  IcoLoadInfo *info;
+  gint32       image;
+  gint32       layer;
+  gint         w     = 0;
+  gint         h     = 0;
+  gint         bpp   = 0;
+  gint         match = 0;
+  gint         i, icon_count;
+  guchar      *buffer;
 
   gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
                              gimp_filename_to_utf8 (filename));
 
-  if (! ico_init (filename, &ico))
-    return -1;
+  fp = g_fopen (filename, "rb");
+  if ( !fp )
+    {
+      g_message (_("Could not open '%s' for reading: %s"),
+                 gimp_filename_to_utf8 (filename), g_strerror (errno));
+      return -1;
+    }
 
-  ico.cp += ico_read_int16 (ico.fp, &ico.icon_count, 1);
-  ico.icon_dir  = g_new0 (MsIconEntry, ico.icon_count);
-  ico.icon_data = g_new0 (MsIconData, ico.icon_count);
+  icon_count = ico_read_init (fp);
+  if ( !icon_count )
+    {
+      fclose (fp);
+      return -1;
+    }
 
   D(("*** %s: Microsoft icon file, containing %i icon(s)\n",
-         ico.filename, ico.icon_count));
+     filename, icon_count));
 
-  for (i = 0; i < ico.icon_count; i++)
-    ico_read_entry (&ico, &ico.icon_dir[i]);
+  info = ico_read_info (fp, icon_count);
+  if ( !info )
+    {
+      fclose (fp);
+      return -1;
+    }
 
   /* Do a quick scan of the icons in the file to find the best match */
-  for (i = 0; i < ico.icon_count; i++)
+  for (i = 0; i < icon_count; i++)
     {
-      if ((ico.icon_dir[i].width  > w && w < *width) ||
-          (ico.icon_dir[i].height > h && h < *height))
+      if ((info[i].width  > w && w < *width) ||
+          (info[i].height > h && h < *height))
         {
-          w = ico.icon_dir[i].width;
-          h = ico.icon_dir[i].height;
+          w = info[i].width;
+          h = info[i].height;
+          bpp = info[i].bpp;
 
           match = i;
         }
+      else if ( w == info[i].width
+                && h == info[i].height
+                && info[i].bpp > bpp )
+        {
+          /* better quality */
+          bpp = info[i].bpp;
+          match = i;
+        }
     }
 
-  if (w < 1 || h < 1)
+  if (w <= 0 || h <= 0)
     return -1;
 
-  ico_read_data (&ico, match);
-
   image = gimp_image_new (w, h, GIMP_RGB);
-  layer = ico_load_layer (image, &ico, match);
-
-  /* Do a quick scan of the icons in the file to find the largest icon */
-  for (i = 0, w = 0, h = 0; i < ico.icon_count; i++)
-    {
-      if (ico.icon_dir[i].width > w)
-        w = ico.icon_dir[i].width;
-
-      if (ico.icon_dir[i].height > h)
-        h = ico.icon_dir[i].height;
-    }
+  buffer = g_new (guchar, w*h*4);
+  layer = ico_load_layer (fp, image, match, buffer, w*h*4, info+match);
+  g_free (buffer);
 
   *width  = w;
   *height = h;
@@ -579,7 +769,8 @@ ico_load_thumbnail_image (const gchar *f
 
   gimp_progress_update (1.0);
 
-  ico_cleanup (&ico);
+  g_free (info);
+  fclose (fp);
 
   return image;
 }
Index: icoload.h
===================================================================
RCS file: /cvs/gnome/gimp/plug-ins/winicon/icoload.h,v
retrieving revision 1.3
diff -u -p -u -p -r1.3 icoload.h
--- icoload.h	6 May 2005 23:26:04 -0000	1.3
+++ icoload.h	3 Oct 2006 07:55:35 -0000
@@ -22,7 +22,6 @@
 #ifndef __ICO_LOAD_H__
 #define __ICO_LOAD_H__
 
-
 gint32 ico_load_image           (const gchar  *filename);
 gint32 ico_load_thumbnail_image (const gchar  *filename,
                                  gint         *width,
@@ -36,7 +35,6 @@ gint   ico_get_nibble_from_data (const g
 gint   ico_get_byte_from_data   (const guint8 *data,
                                  gint          line_width,
                                  gint          byte);
-
 
 
 #endif /* __ICO_LOAD_H__ */
Index: icosave.c
===================================================================
RCS file: /cvs/gnome/gimp/plug-ins/winicon/icosave.c,v
retrieving revision 1.21
diff -u -p -u -p -r1.21 icosave.c
--- icosave.c	24 Aug 2006 15:07:04 -0000	1.21
+++ icosave.c	3 Oct 2006 07:55:36 -0000
@@ -29,6 +29,8 @@
 #include <libgimp/gimp.h>
 #include <libgimp/gimpui.h>
 
+#include <png.h>
+
 /* #define ICO_DBG */
 
 #include "main.h"
@@ -38,27 +40,37 @@
 #include "libgimp/stdplugins-intl.h"
 
 
-static gint     ico_write_int8         (FILE *fp, guint8 *data, gint count);
-static gint     ico_write_int16        (FILE *fp, guint16 *data, gint count);
-static gint     ico_write_int32        (FILE *fp, guint32 *data, gint count);
-
-static gint *   ico_show_icon_dialog   (gint32 image_ID, gint *num_icons);
+static gint     ico_write_int8         (FILE   *fp,
+                                        guint8 *data,
+                                        gint    count);
+static gint     ico_write_int16        (FILE *fp,
+                                        guint16 *data,
+                                        gint count);
+static gint     ico_write_int32        (FILE    *fp,
+                                        guint32 *data,
+                                        gint     count);
 
 /* Helpers to set bits in a *cleared* data chunk */
-static void     ico_set_bit_in_data    (guint8 *data, gint line_width,
-                                        gint bit_num, gint bit_val);
-static void     ico_set_nibble_in_data (guint8 *data, gint line_width,
-                                        gint nibble_num, gint nibble_val);
-static void     ico_set_byte_in_data   (guint8 *data, gint line_width,
-                                        gint byte_num, gint byte_val);
+static void     ico_set_bit_in_data    (guint8 *data,
+                                        gint    line_width,
+                                        gint    bit_num,
+                                        gint    bit_val);
+static void     ico_set_nibble_in_data (guint8 *data,
+                                        gint    line_width,
+                                        gint    nibble_num,
+                                        gint    nibble_val);
+static void     ico_set_byte_in_data   (guint8 *data,
+                                        gint    line_width,
+                                        gint    byte_num,
+                                        gint    byte_val);
 
 static gint     ico_get_layer_num_colors  (gint32    layer,
-					   gboolean *uses_alpha_levels);
+                                           gboolean *uses_alpha_levels);
 static void     ico_image_get_reduced_buf (guint32   layer,
-					   gint      bpp,
-					   gint     *num_colors,
-					   guchar  **cmap_out,
-					   guchar  **buf_out);
+                                           gint      bpp,
+                                           gint     *num_colors,
+                                           guchar  **cmap_out,
+                                           guchar  **buf_out);
 
 
 static gint
@@ -143,142 +155,107 @@ ico_write_int8 (FILE     *fp,
 }
 
 
-static gint*
-ico_show_icon_dialog (gint32  image_ID,
-                      gint   *num_icons)
-{
-  GtkWidget *dialog, *hbox;
-  GtkWidget *icon_menu;
-  gint      *layers, *icon_depths = NULL;
-  gint       num_layers, i, num_colors;
+static void
+ico_save_init (gint32 image_ID, IcoSaveInfo *info)
+{
+  gint	    *layers;
+  gint       i, num_colors;
   gboolean   uses_alpha_values;
-  gchar      key[MAXLEN];
-
-  *num_icons = 0;
-
-  gimp_ui_init ("winicon", TRUE);
-
-  layers = gimp_image_get_layers (image_ID, &num_layers);
-  dialog = ico_specs_dialog_new (num_layers);
-
-  for (i = 0; i < num_layers; i++)
-    {
-      /* if (gimp_layer_get_visible(layers[i])) */
-      ico_specs_dialog_add_icon (dialog, layers[i], i);
-    }
 
-  /* Scale the thing to approximately fit its content, but not too large ... */
-  gtk_window_set_default_size (GTK_WINDOW (dialog),
-                               -1,
-                               120 + (num_layers > 4 ? 500 : num_layers * 120));
-
-  icon_depths = g_object_get_data (G_OBJECT (dialog), "icon_depths");
+  layers = gimp_image_get_layers (image_ID, &info->num_icons);
+  info->layers = layers;
+  info->depths = g_new (gint, info->num_icons);
+  info->default_depths = g_new (gint, info->num_icons);
+  info->compress = g_new (gboolean, info->num_icons);
 
   /* Limit the color depths to values that don't cause any color loss --
      the user should pick these anyway, so we can save her some time.
      If the user wants to lose some colors, the settings can always be changed
      in the dialog: */
-  for (i = 0; i < num_layers; i++)
+  for (i = 0; i < info->num_icons; i++)
     {
       num_colors = ico_get_layer_num_colors (layers[i], &uses_alpha_values);
 
-      g_snprintf (key, MAXLEN, "layer_%i_hbox", layers[i]);
-      hbox = g_object_get_data (G_OBJECT (dialog), key);
-      icon_menu = g_object_get_data (G_OBJECT (hbox), "icon_menu");
-
       if (!uses_alpha_values)
         {
           if (num_colors <= 2)
             {
               /* Let's suggest monochrome */
-              icon_depths[i] = 1;
-              icon_depths[num_layers + i] = 1;
-              ico_specs_dialog_update_icon_preview (dialog, layers[i], 2);
-              gtk_combo_box_set_active (GTK_COMBO_BOX (icon_menu), 0);
+              info->default_depths [i] = 1;
             }
           else if (num_colors <= 16)
             {
               /* Let's suggest 4bpp */
-              icon_depths[i] = 4;
-              icon_depths[num_layers + i] = 4;
-              ico_specs_dialog_update_icon_preview (dialog, layers[i], 4);
-              gtk_combo_box_set_active (GTK_COMBO_BOX (icon_menu), 1);
+              info->default_depths [i] = 4;
             }
           else if (num_colors <= 256)
             {
               /* Let's suggest 8bpp */
-              icon_depths[i] = 8;
-              icon_depths[num_layers + i] = 8;
-              ico_specs_dialog_update_icon_preview (dialog, layers[i], 8);
-              gtk_combo_box_set_active (GTK_COMBO_BOX (icon_menu), 2);
+              info->default_depths [i] = 8;
+            }
+          else
+            {
+              /* Let's suggest 24bpp */
+              info->default_depths [i] = 24;
             }
         }
+      else
+        {
+          /* Otherwise, or if real alpha levels are used, stick with 32bpp */
+          info->default_depths [i] = 32;
+        }
 
-      /* Otherwise, or if real alpha levels are used, stick with 32bpp */
+      // vista icons
+      if (gimp_drawable_width (layers[i]) > 255
+          || gimp_drawable_height (layers[i]) > 255 )
+        {
+          info->compress[i] = TRUE;
+        }
+      else
+        {
+          info->compress[i] = FALSE;
+        }
     }
 
-  g_free (layers);
-
-  gtk_widget_show (dialog);
-
-  if (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK)
-    *num_icons = num_layers;
-  else
-    icon_depths = NULL;
-
-  gtk_widget_destroy (dialog);
-
-  return icon_depths;
+  /* set with default values */
+  memcpy (info->depths, info->default_depths,
+          sizeof (gint) * info->num_icons);
 }
 
 
-static GimpPDBStatusType
-ico_init (const gchar *filename,
-          MsIcon      *ico,
-          gint         num_icons)
-{
-  memset (ico, 0, sizeof (MsIcon));
 
-  if (! (ico->fp = g_fopen (filename, "wb")))
-    {
-      g_message (_("Could not open '%s' for writing: %s"),
-                 gimp_filename_to_utf8 (filename), g_strerror (errno));
-      return GIMP_PDB_EXECUTION_ERROR;
-    }
-
-  ico->filename      = filename;
-  ico->reserved      = 0;
-  ico->resource_type = 1;
-  ico->icon_count    = num_icons;
-  ico->icon_dir      = g_new0 (MsIconEntry, num_icons);
-  ico->icon_data     = g_new0 (MsIconData, num_icons);
-
-  return GIMP_PDB_SUCCESS;
-}
-
-
-static void
-ico_init_direntry (MsIconEntry *entry,
-                   gint32       layer,
-                   gint         bpp)
+static gboolean
+ico_save_dialog (gint32          image_ID,
+                 IcoSaveInfo    *info)
 {
-  /* Was calloc'd, so initialized to 0. */
-  entry->width  = gimp_drawable_width (layer);
-  entry->height = gimp_drawable_height (layer);
+  GtkWidget *dialog;
+  gint       i;
+  gint       response;
 
-  if (bpp < 8)
-    entry->num_colors = (1 << bpp);
+  gimp_ui_init ("winicon", TRUE);
 
-  entry->num_planes = 1;
-  entry->bpp        = bpp;
+  dialog = ico_dialog_new (info);
+  for (i = 0; i < info->num_icons; i++)
+    {
+      /* if (gimp_layer_get_visible(layers[i])) */
+      ico_dialog_add_icon (dialog, info->layers[i], i);
+    }
 
-  D(("Initialized entry to w %i, h %i, bpp %i\n",
-     gimp_drawable_width (layer), entry->width, entry->bpp));
+  /* Scale the thing to approximately fit its content, but not too large ... */
+  gtk_window_set_default_size (GTK_WINDOW (dialog),
+                               -1,
+                               120 + (info->num_icons > 4 ?
+                                      500 : info->num_icons * 120));
 
-  /* We'll set size and offset when writing things out */
+  gtk_widget_show (dialog);
+  response = gimp_dialog_run (GIMP_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+  if (response == GTK_RESPONSE_OK)
+    return TRUE;
+  else
+    return FALSE;
 }
 
-
 static void
 ico_set_bit_in_data (guint8 *data,
                      gint    line_width,
@@ -317,7 +294,8 @@ ico_set_nibble_in_data (guint8 *data,
   offset = nibble_num % line_width;
   nibble_val = nibble_val & 0x0000000F;
 
-  data[line * width8 * 4 + offset/2] |= (nibble_val << (4 * (1 - (offset % 2))));
+  data[line * width8 * 4 + offset/2] |=
+    (nibble_val << (4 * (1 - (offset % 2))));
 }
 
 
@@ -343,7 +321,6 @@ ico_set_byte_in_data (guint8 *data,
 }
 
 
-
 /* Create a colormap from the given buffer data */
 static guint32 *
 ico_create_palette(guchar *cmap,
@@ -455,291 +432,6 @@ ico_get_palette_index (GHashTable *hash,
   return *slot;
 }
 
-static void
-ico_init_data (MsIcon *ico,
-               gint    num_icon,
-               gint32  layer,
-               gint    bpp)
-{
-  MsIconEntry    *entry;
-  MsIconData     *data;
-  gint            and_len, xor_len, palette_index, x, y;
-  gint            num_colors = 0, num_colors_used = 0, black_index = 0;
-  guchar         *buffer = NULL, *pixel;
-  guint32        *buffer32;
-  guchar         *palette;
-  GHashTable     *color_to_slot = NULL;
-
-  D(("Creating data structures for icon %i ------------------------\n", num_icon));
-
-  /* Shortcuts, for convenience */
-  entry = &ico->icon_dir[num_icon];
-  data  = &ico->icon_data[num_icon];
-
-  /* Entries and data were calloc'd, so initialized to 0. */
-
-  data->header_size = 40;
-  data->width       = gimp_drawable_width (layer);
-  data->height      = 2 * gimp_drawable_height (layer);
-  data->planes      = 1;
-  data->bpp         = bpp;
-
-  num_colors = (1L << bpp);
-
-  D(("  header size %i, w %i, h %i, planes %i, bpp %i\n",
-     data->header_size, data->width, data->height, data->planes, data->bpp));
-
-  /* Reduce colors in copy of image */
-  ico_image_get_reduced_buf (layer, bpp, &num_colors_used, &palette, &buffer);
-  buffer32 = (guint32 *) buffer;
-
-  /* Set up colormap and andmap when necessary: */
-  if (bpp <= 8)
-    {
-      /* Create a colormap */
-      data->palette = ico_create_palette (palette,
-                                          num_colors, num_colors_used,
-                                          &black_index);
-      data->palette_len = num_colors * 4;
-
-      color_to_slot = ico_create_color_to_palette_map (data->palette,
-                                                       num_colors_used);
-      D(("  created %i-slot colormap with %i colors, black at slot %i\n",
-         num_colors, num_colors_used, black_index));
-    }
-
-  /* Create and_map. It's padded out to 32 bits per line: */
-  data->and_map = ico_alloc_map (entry->width, entry->height, 1, &and_len);
-  data->and_len = and_len;
-
-  for (y = 0; y < entry->height; y++)
-    for (x = 0; x < entry->width; x++)
-      {
-        pixel = (guint8 *) &buffer32[y * entry->width + x];
-
-        ico_set_bit_in_data (data->and_map, entry->width,
-                             (entry->height-y-1) * entry->width + x,
-                             (pixel[3] == 255 ? 0 : 1));
-      }
-
-  data->xor_map = ico_alloc_map(entry->width, entry->height, bpp, &xor_len);
-  data->xor_len = xor_len;
-
-  /* Now fill in the xor map */
-  switch (bpp)
-    {
-    case 1:
-      for (y = 0; y < entry->height; y++)
-        for (x = 0; x < entry->width; x++)
-          {
-            pixel = (guint8 *) &buffer32[y * entry->width + x];
-            palette_index = ico_get_palette_index (color_to_slot,
-                                                   pixel[0], pixel[1], pixel[2]);
-
-            if (ico_get_bit_from_data (data->and_map, entry->width,
-                                       (entry->height-y-1) * entry->width + x))
-              {
-                ico_set_bit_in_data (data->xor_map, entry->width,
-                                     (entry->height-y-1) * entry->width + x,
-                                     black_index);
-              }
-            else
-              {
-                ico_set_bit_in_data (data->xor_map, entry->width,
-                                     (entry->height-y-1) * entry->width + x,
-                                     palette_index);
-              }
-          }
-      break;
-
-    case 4:
-      for (y = 0; y < entry->height; y++)
-        for (x = 0; x < entry->width; x++)
-          {
-            pixel = (guint8 *) &buffer32[y * entry->width + x];
-            palette_index = ico_get_palette_index(color_to_slot,
-                                                  pixel[0], pixel[1], pixel[2]);
-
-            if (ico_get_bit_from_data (data->and_map, entry->width,
-                                       (entry->height-y-1) * entry->width + x))
-              {
-                ico_set_nibble_in_data (data->xor_map, entry->width,
-                                        (entry->height-y-1) * entry->width + x,
-                                        black_index);
-              }
-            else
-              {
-                ico_set_nibble_in_data (data->xor_map, entry->width,
-                                        (entry->height-y-1) * entry->width + x,
-                                        palette_index);
-              }
-          }
-      break;
-
-    case 8:
-      for (y = 0; y < entry->height; y++)
-        for (x = 0; x < entry->width; x++)
-          {
-            pixel = (guint8 *) &buffer32[y * entry->width + x];
-            palette_index = ico_get_palette_index (color_to_slot,
-                                                   pixel[0],
-                                                   pixel[1],
-                                                   pixel[2]);
-
-            if (ico_get_bit_from_data (data->and_map, entry->width,
-                                       (entry->height-y-1) * entry->width + x))
-              {
-                ico_set_byte_in_data (data->xor_map, entry->width,
-                                      (entry->height-y-1) * entry->width + x,
-                                      black_index);
-              }
-            else
-              {
-                ico_set_byte_in_data (data->xor_map, entry->width,
-                                      (entry->height-y-1) * entry->width + x,
-                                      palette_index);
-              }
-
-          }
-      break;
-
-    default:
-      for (y = 0; y < entry->height; y++)
-        for (x = 0; x < entry->width; x++)
-          {
-            pixel = (guint8 *) &buffer32[y * entry->width + x];
-
-            ((guint32 *) data->xor_map)[(entry->height-y-1) * entry->width + x] =
-              GUINT32_TO_LE ((pixel[0] << 16) |
-                             (pixel[1] << 8)  |
-                             (pixel[2])       |
-                             (pixel[3] << 24));
-          }
-    }
-
-  D(("  filled and_map of length %i, xor_map of length %i\n",
-     data->and_len, data->xor_len));
-
-  if (color_to_slot)
-    {
-      g_hash_table_foreach (color_to_slot, ico_free_hash_item, NULL);
-      g_hash_table_destroy (color_to_slot);
-    }
-
-  g_free(palette);
-  g_free(buffer);
-}
-
-
-static void
-ico_setup (MsIcon *ico,
-           gint32  image,
-           gint   *icon_depths,
-           gint    num_icons)
-{
-  gint *layers;
-  gint  i;
-  gint  offset;
-
-  layers = gimp_image_get_layers (image, &num_icons);
-
-  /* Set up icon entries */
-  for (i = 0; i < num_icons; i++)
-    {
-      ico_init_direntry (&ico->icon_dir[i], layers[i], icon_depths[i]);
-      gimp_progress_update ((gdouble) i / (gdouble) num_icons * 0.3);
-    }
-
-  /* Set up data entries (the actual icons), and calculate each one's size */
-  for (i = 0; i < num_icons; i++)
-    {
-      ico_init_data (ico, i, layers[i], icon_depths[i]);
-
-      ico->icon_dir[i].size =
-        ico->icon_data[i].header_size +
-        ico->icon_data[i].palette_len +
-        ico->icon_data[i].xor_len +
-        ico->icon_data[i].and_len;
-
-      gimp_progress_update (0.3 + (gdouble) i / (gdouble) num_icons * 0.3);
-    }
-
-  /* Finally, calculate offsets for each icon and store them in each entry */
-  offset = 3 * sizeof (guint16) + ico->icon_count * sizeof (MsIconEntry);
-
-  for (i = 0; i < num_icons; i++)
-    {
-      ico->icon_dir[i].offset = offset;
-      offset += ico->icon_dir[i].size;
-
-      gimp_progress_update (0.6 + (gdouble) i / (gdouble) num_icons * 0.3);
-    }
-
-  gimp_progress_update (1.0);
-
-  g_free (layers);
-}
-
-
-static GimpPDBStatusType
-ico_save (MsIcon *ico)
-{
-  MsIconEntry *entry;
-  MsIconData *data;
-  int i;
-
-  ico->cp += ico_write_int16 (ico->fp, &ico->reserved, 3);
-
-  for (i = 0; i < ico->icon_count; i++)
-    {
-      entry = &ico->icon_dir[i];
-
-      ico->cp += ico_write_int8 (ico->fp, (guint8 *) entry, 4);
-      ico->cp += ico_write_int16 (ico->fp, &entry->num_planes, 2);
-      ico->cp += ico_write_int32 (ico->fp, &entry->size, 2);
-    }
-
-  for (i = 0; i < ico->icon_count; i++)
-    {
-      data  = &ico->icon_data[i];
-
-      ico->cp += ico_write_int32 (ico->fp, (guint32 *) data, 3);
-      ico->cp += ico_write_int16 (ico->fp, &data->planes, 2);
-      ico->cp += ico_write_int32 (ico->fp, &data->compression, 6);
-
-      if (data->palette)
-        ico->cp += ico_write_int8 (ico->fp,
-                                   (guint8 *) data->palette, data->palette_len);
-
-      ico->cp += ico_write_int8 (ico->fp, data->xor_map, data->xor_len);
-      ico->cp += ico_write_int8 (ico->fp, data->and_map, data->and_len);
-    }
-
-  return GIMP_PDB_SUCCESS;
-}
-
-static gboolean
-ico_layers_too_big (gint32 image)
-{
-  gint *layers;
-  gint  i, num_layers;
-
-  layers = gimp_image_get_layers (image, &num_layers);
-
-  for (i = 0; i < num_layers; i++)
-    {
-      if ((gimp_drawable_width (layers[i])  > 255) ||
-          (gimp_drawable_height (layers[i]) > 255))
-        {
-          g_free (layers);
-          return TRUE;
-        }
-    }
-
-  g_free (layers);
-  return FALSE;
-}
-
 static gint
 ico_get_layer_num_colors (gint32    layer,
                           gboolean *uses_alpha_levels)
@@ -832,7 +524,7 @@ ico_get_layer_num_colors (gint32    laye
   return num_colors;
 }
 
-static gboolean
+gboolean
 ico_cmap_contains_black (guchar *cmap,
                          gint    num_colors)
 {
@@ -873,7 +565,7 @@ ico_image_get_reduced_buf (guint32   lay
 
   buffer = g_new (guchar, w * h * 4);
 
-  if (bpp <= 8 || drawable->bpp != 4)
+  if (bpp <= 8 || bpp == 24 || drawable->bpp != 4)
     {
       gint32        image = gimp_drawable_get_image (layer);
       GimpDrawable *tmp;
@@ -924,7 +616,20 @@ ico_image_get_reduced_buf (guint32   lay
                * We need to eliminate one more color to make room for black.
                */
 
-              gimp_image_convert_rgb (tmp_image);
+              if (gimp_drawable_is_indexed (layer))
+                {
+                  g_free (cmap);
+                  cmap = gimp_image_get_colormap (image, num_colors);
+                  gimp_image_set_colormap (tmp_image, cmap, *num_colors);
+                }
+              else if (gimp_drawable_is_gray (layer))
+                {
+                  gimp_image_convert_grayscale (tmp_image);
+                }
+              else
+                {
+                  gimp_image_convert_rgb (tmp_image);
+                }
 
               tmp = gimp_drawable_get (tmp_layer);
               gimp_pixel_rgn_init (&dst_pixel_rgn,
@@ -932,15 +637,32 @@ ico_image_get_reduced_buf (guint32   lay
               gimp_pixel_rgn_set_rect (&dst_pixel_rgn, buffer, 0, 0, w, h);
               gimp_drawable_detach (tmp);
 
+              if (!gimp_drawable_is_rgb (layer))
+                gimp_image_convert_rgb (tmp_image);
+
               gimp_image_convert_indexed (tmp_image,
                                           GIMP_FS_DITHER, GIMP_MAKE_PALETTE,
-                                          (1 << bpp) - 1, TRUE, FALSE, "dummy");
+                                          (1<<bpp) - 1, TRUE, FALSE, "dummy");
               g_free (cmap);
               cmap = gimp_image_get_colormap (tmp_image, num_colors);
             }
 
           gimp_image_convert_rgb (tmp_image);
         }
+      else if (bpp == 24)
+        {
+          GimpParam    *return_vals;
+          gint          n_return_vals;
+
+          return_vals =
+            gimp_run_procedure ("plug-in-threshold-alpha", &n_return_vals,
+                                GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+                                GIMP_PDB_IMAGE, tmp_image,
+                                GIMP_PDB_DRAWABLE, tmp_layer,
+                                GIMP_PDB_INT32, ICO_ALPHA_THRESHOLD,
+                                GIMP_PDB_END);
+          gimp_destroy_params (return_vals, n_return_vals);
+        }
 
       gimp_layer_add_alpha (tmp_layer);
 
@@ -963,48 +685,409 @@ ico_image_get_reduced_buf (guint32   lay
   *buf_out = buffer;
 }
 
+static gboolean
+ico_write_png (FILE   *fp,
+               gint32  layer,
+               gint32  depth)
+{
+  png_structp png_ptr;
+  png_infop   info_ptr;
+  png_byte  **row_pointers;
+  gint        i, rowstride;
+  gint        width, height;
+  gint        num_colors_used;
+  guchar     *palette;
+  guchar     *buffer;
+
+  row_pointers = NULL;
+  palette = NULL;
+  buffer = NULL;
+
+  width = gimp_drawable_width (layer);
+  height = gimp_drawable_height (layer);
+
+  png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+  if ( !png_ptr )
+    return FALSE;
+
+  info_ptr = png_create_info_struct (png_ptr);
+  if ( !info_ptr )
+    {
+      png_destroy_write_struct (&png_ptr, NULL);
+      return FALSE;
+    }
+
+  if (setjmp (png_jmpbuf (png_ptr)))
+    {
+      png_destroy_write_struct (&png_ptr, &info_ptr);
+      if ( row_pointers )
+        g_free (row_pointers);
+      if (palette)
+        g_free (palette);
+      if (buffer)
+        g_free (buffer);
+      return FALSE;
+    }
+
+  ico_image_get_reduced_buf (layer, depth, &num_colors_used,
+                             &palette, &buffer);
+
+  png_init_io (png_ptr, fp);
+  png_set_IHDR (png_ptr, info_ptr, width, height,
+                8,
+                PNG_COLOR_TYPE_RGBA,
+                PNG_INTERLACE_NONE,
+                PNG_COMPRESSION_TYPE_DEFAULT,
+                PNG_FILTER_TYPE_DEFAULT);
+  png_write_info (png_ptr, info_ptr);
+
+  rowstride = ico_rowstride (width, 32);
+  row_pointers = g_new (png_byte*, height);
+  for (i = 0; i < height; i++)
+    {
+      row_pointers[i] = buffer + rowstride * i;
+    }
+  png_write_image (png_ptr, row_pointers);
+
+  row_pointers = NULL;
+
+  png_write_end (png_ptr, info_ptr);
+  png_destroy_write_struct (&png_ptr, &info_ptr);
+
+  g_free (row_pointers);
+  g_free (palette);
+  g_free (buffer);
+  return TRUE;
+}
+
+static gboolean
+ico_write_icon (FILE   *fp,
+                gint32  layer,
+                gint32  depth)
+{
+  IcoFileDataHeader  header;
+  gint               and_len, xor_len, palette_index, x, y;
+  gint               num_colors = 0, num_colors_used = 0, black_index = 0;
+  gint               width, height;
+  guchar            *buffer = NULL, *pixel;
+  guint32           *buffer32;
+  guchar            *palette;
+  GHashTable        *color_to_slot = NULL;
+  guchar            *row;
+  guchar            *xor_map, *and_map;
+
+  guint32           *palette32 = NULL;
+  gint               palette_len = 0;
+
+  D(("Creating data structures for icon %i ------------------------\n",
+     num_icon));
+
+  width = gimp_drawable_width (layer);
+  height = gimp_drawable_height (layer);
+
+  header.header_size     = 40;
+  header.width          = width;
+  header.height         = 2 * height;
+  header.planes         = 1;
+  header.bpp            = depth;
+  header.compression    = 0;
+  header.image_size     = 0;
+  header.x_res          = 0;
+  header.y_res          = 0;
+  header.used_clrs      = 0;
+  header.important_clrs = 0;
+
+  num_colors = (1L << header.bpp);
+
+  D(("  header size %i, w %i, h %i, planes %i, bpp %i\n",
+     header.header_size, header.width, header.height, header.planes,
+     header.bpp));
+
+  /* Reduce colors in copy of image */
+  ico_image_get_reduced_buf (layer, header.bpp, &num_colors_used,
+                             &palette, &buffer);
+  buffer32 = (guint32 *) buffer;
+
+  /* Set up colormap and andmap when necessary: */
+  if (header.bpp <= 8)
+    {
+      /* Create a colormap */
+      palette32 = ico_create_palette (palette,
+                                      num_colors, num_colors_used,
+                                      &black_index);
+      palette_len = num_colors * 4;
+
+      color_to_slot = ico_create_color_to_palette_map (palette32,
+                                                       num_colors_used);
+      D(("  created %i-slot colormap with %i colors, black at slot %i\n",
+         num_colors, num_colors_used, black_index));
+    }
+
+  /* Create and_map. It's padded out to 32 bits per line: */
+  and_map = ico_alloc_map (width, height, 1, &and_len);
+  and_len = and_len;
+
+  for (y = 0; y < height; y++)
+    for (x = 0; x < width; x++)
+      {
+        pixel = (guint8 *) &buffer32[y * width + x];
+
+        ico_set_bit_in_data (and_map, width,
+                             (height-y-1) * width + x,
+                             (pixel[3] > ICO_ALPHA_THRESHOLD ? 0 : 1));
+      }
+
+  xor_map = ico_alloc_map (width, height, header.bpp, &xor_len);
+  xor_len = xor_len;
+
+  /* Now fill in the xor map */
+  switch (header.bpp)
+    {
+    case 1:
+      for (y = 0; y < height; y++)
+        for (x = 0; x < width; x++)
+          {
+            pixel = (guint8 *) &buffer32[y * width + x];
+            palette_index = ico_get_palette_index (color_to_slot, pixel[0],
+                                                   pixel[1], pixel[2]);
+
+            if (ico_get_bit_from_data (and_map, width,
+                                       (height-y-1) * width + x))
+              {
+                ico_set_bit_in_data (xor_map, width,
+                                     (height-y-1) * width + x,
+                                     black_index);
+              }
+            else
+              {
+                ico_set_bit_in_data (xor_map, width,
+                                     (height-y-1) * width + x,
+                                     palette_index);
+              }
+          }
+      break;
+
+    case 4:
+      for (y = 0; y < height; y++)
+        for (x = 0; x < width; x++)
+          {
+            pixel = (guint8 *) &buffer32[y * width + x];
+            palette_index = ico_get_palette_index(color_to_slot, pixel[0],
+                                                  pixel[1], pixel[2]);
+
+            if (ico_get_bit_from_data (and_map, width,
+                                       (height-y-1) * width + x))
+              {
+                ico_set_nibble_in_data (xor_map, width,
+                                        (height-y-1) * width + x,
+                                        black_index);
+              }
+            else
+              {
+                ico_set_nibble_in_data (xor_map, width,
+                                        (height-y-1) * width + x,
+                                        palette_index);
+              }
+          }
+      break;
+
+    case 8:
+      for (y = 0; y < height; y++)
+        for (x = 0; x < width; x++)
+          {
+            pixel = (guint8 *) &buffer32[y * width + x];
+            palette_index = ico_get_palette_index (color_to_slot,
+                                                   pixel[0],
+                                                   pixel[1],
+                                                   pixel[2]);
+
+            if (ico_get_bit_from_data (and_map, width,
+                                       (height-y-1) * width + x))
+              {
+                ico_set_byte_in_data (xor_map, width,
+                                      (height-y-1) * width + x,
+                                      black_index);
+              }
+            else
+              {
+                ico_set_byte_in_data (xor_map, width,
+                                      (height-y-1) * width + x,
+                                      palette_index);
+              }
+
+          }
+      break;
+
+    case 24:
+      for (y = 0; y < height; y++)
+        {
+          row = xor_map + (xor_len*(height-y-1) / height);
+          for (x = 0; x < height; x++)
+            {
+              pixel = (guint8 *) &buffer32[y * width + x];
+              row[0] = pixel[2];
+              row[1] = pixel[1];
+              row[2] = pixel[0];
+              row += 3;
+            }
+        }
+      break;
+
+    default:
+      for (y = 0; y < height; y++)
+        for (x = 0; x < width; x++)
+          {
+            pixel = (guint8 *) &buffer32[y * width + x];
+
+            ((guint32 *) xor_map)[(height-y-1) * width + x] =
+              GUINT32_TO_LE ((pixel[0] << 16) |
+                             (pixel[1] << 8)  |
+                             (pixel[2])       |
+                             (pixel[3] << 24));
+          }
+    }
+
+  D(("  filled and_map of length %i, xor_map of length %i\n",
+     and_len, xor_len));
+
+  if (color_to_slot)
+    {
+      g_hash_table_foreach (color_to_slot, ico_free_hash_item, NULL);
+      g_hash_table_destroy (color_to_slot);
+    }
+
+  g_free(palette);
+  g_free(buffer);
+
+  ico_write_int32 (fp, (guint32*) &header, 3);
+  ico_write_int16 (fp, &header.planes, 2);
+  ico_write_int32 (fp, &header.compression, 6);
+
+  if (palette_len)
+    ico_write_int8 (fp, (guint8 *) palette32, palette_len);
+  ico_write_int8 (fp, xor_map, xor_len);
+  ico_write_int8 (fp, and_map, and_len);
+
+  if (palette32)
+    g_free (palette32);
+  g_free (xor_map);
+  g_free (and_map);
+  return TRUE;
+}
+
+static void
+ico_save_info_free (IcoSaveInfo  *info)
+{
+  g_free (info->depths);
+  g_free (info->default_depths);
+  g_free (info->compress);
+  g_free (info->layers);
+  memset (info, 0, sizeof (IcoSaveInfo));
+}
+
 GimpPDBStatusType
-SaveICO (const gchar *filename,
-         gint32       image)
+ico_save_image (const gchar *filename,
+                gint32       image,
+                gint32       run_mode)
 {
-  MsIcon             ico;
-  gint              *icon_depths = NULL;
-  gint               num_icons;
-  GimpPDBStatusType  exit_state;
+  FILE *fp;
+
+  gint i;
+  gint width, height;
+  IcoSaveInfo      info;
+  IcoFileHeader    header;
+  IcoFileEntry    *entries;
+  gboolean          saved;
 
   D(("*** Saving Microsoft icon file %s\n", filename));
 
-  if (ico_layers_too_big (image))
+  ico_save_init (image, &info);
+
+  if (run_mode == GIMP_RUN_INTERACTIVE)
     {
-      g_message (_("Windows icons cannot be higher or wider than 255 pixels."));
-      return GIMP_PDB_EXECUTION_ERROR;
+      /* Allow user to override default values */
+      if ( !ico_save_dialog (image, &info))
+        return GIMP_PDB_CANCEL;
     }
 
-  /* First, set up the icon specs dialog and show it: */
-  if ((icon_depths = ico_show_icon_dialog (image, &num_icons)) == NULL)
-    return GIMP_PDB_CANCEL;
-
   gimp_progress_init_printf (_("Saving '%s'"),
                              gimp_filename_to_utf8 (filename));
 
-  /* Okay, let's actually save the thing with the depths the
-     user specified. */
-
-  if ((exit_state = ico_init (filename, &ico, num_icons) != GIMP_PDB_SUCCESS))
-    return exit_state;
+  if (! (fp = g_fopen (filename, "wb")))
+    {
+      g_message (_("Could not open '%s' for writing: %s"),
+                 gimp_filename_to_utf8 (filename), g_strerror (errno));
+      return GIMP_PDB_EXECUTION_ERROR;
+    }
 
-  D(("icon initialized ...\n"));
+  header.reserved = 0;
+  header.resource_type = 1;
+  header.icon_count = info.num_icons;
+  if ( !ico_write_int16 (fp, &header.reserved, 1)
+       || !ico_write_int16 (fp, &header.resource_type, 1)
+       || !ico_write_int16 (fp, &header.icon_count, 1) )
+    {
+      ico_save_info_free (&info);
+      fclose (fp);
+      return GIMP_PDB_EXECUTION_ERROR;
+    }
 
-  ico_setup (&ico, image, icon_depths, num_icons);
+  entries = g_new0 (IcoFileEntry, info.num_icons);
+  if (fwrite (entries, sizeof (IcoFileEntry), info.num_icons, fp) <= 0)
+    {
+      ico_save_info_free (&info);
+      fclose (fp);
+      return GIMP_PDB_EXECUTION_ERROR;
+    }
 
-  D(("icon data created ...\n"));
+  for (i = 0; i < info.num_icons; i++)
+    {
+      gimp_progress_update ((gdouble)i / (gdouble)info.num_icons);
 
-  exit_state = ico_save(&ico);
+      width = gimp_drawable_width (info.layers[i]);
+      height = gimp_drawable_height (info.layers[i]);
+      if (width <= 255 && height <= 255)
+        {
+          entries[i].width = width;
+          entries[i].height = height;
+        }
+      else
+        {
+          entries[i].width = 0;
+          entries[i].height = 0;
+        }
+      if ( info.depths[i] <= 8 )
+        entries[i].num_colors = 1 << info.depths[i];
+      else
+        entries[i].num_colors = 0;
+      entries[i].reserved = 0;
+      entries[i].planes = 1;
+      entries[i].bpp = info.depths[i];
+      entries[i].offset = ftell (fp);
+      if (info.compress[i])
+        saved = ico_write_png (fp, info.layers[i], info.depths[i]);
+      else
+        saved = ico_write_icon (fp, info.layers[i], info.depths[i]);
+      if (!saved)
+        {
+          ico_save_info_free (&info);
+          fclose (fp);
+          return GIMP_PDB_EXECUTION_ERROR;
+        }
+      entries[i].size = ftell (fp) - entries[i].offset;
+    }
 
-  D(("*** icon saved, exit status %i.\n\n", exit_state));
+  if (fseek (fp, sizeof(IcoFileHeader), SEEK_SET) < 0
+      || fwrite (entries, sizeof (IcoFileEntry), info.num_icons, fp) <= 0)
+    {
+      ico_save_info_free (&info);
+      fclose (fp);
+      return GIMP_PDB_EXECUTION_ERROR;
+    }
 
-  ico_cleanup (&ico);
-  g_free (icon_depths);
+  gimp_progress_update (1.0);
 
-  return exit_state;
+  ico_save_info_free (&info);
+  fclose (fp);
+  return GIMP_PDB_SUCCESS;
 }
Index: icosave.h
===================================================================
RCS file: /cvs/gnome/gimp/plug-ins/winicon/icosave.h,v
retrieving revision 1.2
diff -u -p -u -p -r1.2 icosave.h
--- icosave.h	10 Nov 2004 14:44:58 -0000	1.2
+++ icosave.h	3 Oct 2006 07:55:36 -0000
@@ -23,8 +23,12 @@
 #define __ICO_SAVE_H__
 
 
-GimpPDBStatusType SaveICO (const gchar *file_name,
-                           gint32       image_ID);
+GimpPDBStatusType ico_save_image (const gchar *file_name,
+                                  gint32       image_ID,
+                                  gint32       run_mode);
+
+gboolean ico_cmap_contains_black (guchar *cmap,
+                                  gint    num_colors);
 
 
 #endif /* __ICO_SAVE_H__ */
Index: main.c
===================================================================
RCS file: /cvs/gnome/gimp/plug-ins/winicon/main.c,v
retrieving revision 1.13
diff -u -p -u -p -r1.13 main.c
--- main.c	27 Jun 2006 12:19:57 -0000	1.13
+++ main.c	3 Oct 2006 07:55:36 -0000
@@ -259,7 +259,7 @@ run (const gchar      *name,
 
       if (status == GIMP_PDB_SUCCESS)
         {
-          status = SaveICO (file_name, image_ID);
+          status = ico_save_image (file_name, image_ID, run_mode);
         }
 
       if (export == GIMP_EXPORT_EXPORT)
@@ -275,70 +275,63 @@ run (const gchar      *name,
 }
 
 
-guint8 *
-ico_alloc_map (gint  width,
-               gint  height,
-               gint  bpp,
-               gint *length)
+gint
+ico_rowstride (gint width,
+               gint bpp)
 {
-  gint    len = 0;
-  guint8 *map = NULL;
-
   switch (bpp)
     {
     case 1:
       if ((width % 32) == 0)
-        len = (width * height / 8);
+        return width / 8;
       else
-        len = 4 * ((width/32 + 1) * height);
+        return 4 * (width/32 + 1);
       break;
 
     case 4:
       if ((width % 8) == 0)
-        len = (width * height / 2);
+        return width / 2;
       else
-        len = 4 * ((width/8 + 1) * height);
+        return 4 * (width/8 + 1);
       break;
 
     case 8:
       if ((width % 4) == 0)
-        len = width * height;
+        return width;
       else
-        len = 4 * ((width/4 + 1) * height);
+        return 4 * (width/4 + 1);
       break;
 
-    default:
-      len = width * height * (bpp/8);
-    }
+    case 24:
+      if (((width*3) % 4) == 0)
+        return width * 3;
+      else
+        return 4 * (width*3/4+1);
 
-  *length = len;
-  map = g_new0 (guint8, len);
+    case 32:
+      return width * 4;
 
-  return map;
+    default:
+      g_warning ("invalid bitrate: %d\n", bpp);
+      g_assert_not_reached ();
+      return width * (bpp/8);
+    }
 }
 
-void
-ico_cleanup (MsIcon *ico)
+guint8 *
+ico_alloc_map (gint  width,
+               gint  height,
+               gint  bpp,
+               gint *length)
 {
-  gint i;
-
-  if (!ico)
-    return;
+  gint    len = 0;
+  guint8 *map = NULL;
 
-  if (ico->fp)
-    fclose (ico->fp);
+  len = ico_rowstride (width, bpp) * height;
 
-  if (ico->icon_dir)
-    g_free (ico->icon_dir);
+  *length = len;
+  map = g_new0 (guint8, len);
 
-  if (ico->icon_data)
-    {
-      for (i = 0; i < ico->icon_count; i++)
-        {
-          g_free (ico->icon_data[i].palette);
-          g_free (ico->icon_data[i].xor_map);
-          g_free (ico->icon_data[i].and_map);
-        }
-      g_free (ico->icon_data);
-    }
+  return map;
 }
+
Index: main.h
===================================================================
RCS file: /cvs/gnome/gimp/plug-ins/winicon/main.h,v
retrieving revision 1.5
diff -u -p -u -p -r1.5 main.h
--- main.h	27 Nov 2004 11:19:38 -0000	1.5
+++ main.h	3 Oct 2006 07:55:36 -0000
@@ -33,71 +33,76 @@
 #define D(x)
 #endif
 
+#define ICO_PNG_MAGIC       0x474e5089
+#define ICO_ALPHA_THRESHOLD 127
+#define ICO_MAXBUF          4096
 
-#define MAXLEN 4096
 
-typedef struct _MsIconEntry
+typedef struct _IcoFileHeader
 {
-  guint8        width;        /* Width of icon in pixels */
-  guint8        height;       /* Height of icon in pixels */
-  guint8        num_colors;   /* Maximum number of colors */
-  guint8        reserved;     /* Not used */
-  guint16       num_planes;   /* Not used */
-  guint16       bpp;
-  guint32       size;         /* Length of icon bitmap in bytes */
-  guint32       offset;       /* Offset position of icon bitmap in file */
-} MsIconEntry;
+  guint16   reserved;
+  guint16   resource_type;
+  guint16   icon_count;
+} IcoFileHeader;
+
+typedef struct _IcoFileEntry
+{
+  guint8        width;      /* Width of icon in pixels */
+  guint8        height;    /* Height of icon in pixels */
+  guint8        num_colors; /* Number of colors of paletted image */
+  guint8        reserved;   /* Must be 0 */
+  guint16       planes;     /* Must be 1 */
+  guint16       bpp;        /* 1, 4, 8, 24 or 32 bits per pixel */
+  guint32       size;       /* Size of icon (including data header) */
+  guint32       offset;     /* Absolute offset of data in a file */
+ } IcoFileEntry;
 
-typedef struct _MsIconData
+typedef struct _IcoFileDataHeader
 {
-  /* Bitmap header data */
-  guint32       header_size;  /* = 40 Bytes */
-  guint32       width;
-  guint32       height;
-  guint16       planes;
+  guint32       header_size; /* 40 bytes */
+  guint32       width;       /* Width of image in pixels */
+  guint32       height;      /* Height of image in pixels */
+  guint16       planes;      /* Must be 1 */
   guint16       bpp;
-  guint32       compression;  /* not used for icons */
-  guint32       image_size;   /* size of image */
+  guint32       compression; /* Not used for icons */
+  guint32       image_size;  /* Size of image (without this header) */
   guint32       x_res;
   guint32       y_res;
   guint32       used_clrs;
   guint32       important_clrs;
+} IcoFileDataHeader;
+
 
-  guint32      *palette;      /* Color palette, only if bpp <= 8. */
-  guint8       *xor_map;      /* Icon bitmap */
-  guint8       *and_map;      /* Display bit mask */
-
-  /* Only used when saving: */
-  gint          palette_len;
-  gint          xor_len;
-  gint          and_len;
-} MsIconData;
+typedef struct _IcoLoadInfo
+{
+    guint    width;
+    guint    height;
+    gint     bpp;
+    gint     offset;
+    gint     size;
+} IcoLoadInfo;
 
-typedef struct _MsIcon
+typedef struct _IcoSaveInfo
 {
-  FILE         *fp;
-  guint         cp;
-  const gchar  *filename;
-
-  guint16       reserved;
-  guint16       resource_type;
-  guint16       icon_count;
-  MsIconEntry  *icon_dir;
-  MsIconData   *icon_data;
-} MsIcon;
+    gint        *depths;
+    gint        *default_depths;
+    gboolean    *compress;
+    gint        *layers;
+    gint         num_icons;
+} IcoSaveInfo;
 
 
 /* Miscellaneous helper functions below: */
 
+gint     ico_rowstride (gint width,
+                        gint bpp);
+
 /* Allocates a 32-bit padded bitmap for various color depths.
    Returns the allocated array directly, and the length of the
    array in the len pointer */
 guint8 * ico_alloc_map  (gint     width,
-			 gint     height,
-			 gint     bpp,
-			 gint    *len);
-
-void     ico_cleanup    (MsIcon  *ico);
-
+                         gint     height,
+                         gint     bpp,
+                         gint    *len);
 
 #endif /* __MAIN_H__ */
