[gimp] Issue #526 - Add Import/Export for Windows .CUR files



commit a22fd4f43aa56fde190239bd0d56221e80d13691
Author: Nikc <nikcdc gmail com>
Date:   Thu Feb 10 12:09:40 2022 +0000

    Issue #526 - Add Import/Export for Windows .CUR files

 plug-ins/file-ico/ico-dialog.c |  66 ++++++++++++++++----
 plug-ins/file-ico/ico-load.c   |  66 +++++++++++++-------
 plug-ins/file-ico/ico-save.c   | 134 ++++++++++++++++++++++++++++++++---------
 plug-ins/file-ico/ico-save.h   |  13 ++++
 plug-ins/file-ico/ico.c        | 118 +++++++++++++++++++++++++++++++++++-
 plug-ins/file-ico/ico.h        |   4 ++
 6 files changed, 338 insertions(+), 63 deletions(-)
---
diff --git a/plug-ins/file-ico/ico-dialog.c b/plug-ins/file-ico/ico-dialog.c
index ba65f9d11f..6172149aa7 100644
--- a/plug-ins/file-ico/ico-dialog.c
+++ b/plug-ins/file-ico/ico-dialog.c
@@ -43,13 +43,16 @@ static void   ico_dialog_check_compat    (GtkWidget   *dialog,
 GtkWidget *
 ico_dialog_new (IcoSaveInfo *info)
 {
-  GtkWidget *dialog;
-  GtkWidget *main_vbox;
-  GtkWidget *vbox;
-  GtkWidget *frame;
-  GtkWidget *scrolled_window;
-  GtkWidget *viewport;
-  GtkWidget *warning;
+  GtkWidget     *dialog;
+  GtkWidget     *main_vbox;
+  GtkWidget     *vbox;
+  GtkWidget     *frame;
+  GtkWidget     *scrolled_window;
+  GtkWidget     *viewport;
+  GtkWidget     *grid;
+  GtkAdjustment *adj;
+  GtkWidget     *spinbutton;
+  GtkWidget     *warning;
 
   dialog = gimp_export_dialog_new (_("Windows Icon"),
                                    PLUG_IN_BINARY,
@@ -65,14 +68,55 @@ ico_dialog_new (IcoSaveInfo *info)
 
   g_object_set_data (G_OBJECT (dialog), "save_info", info);
 
-  main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
-  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+  main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
   gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
                       main_vbox, TRUE, TRUE, 0);
   gtk_widget_show (main_vbox);
 
+  /* Cursor */
+  if (info->is_cursor)
+    {
+      frame = gimp_frame_new (_("Cursor Hot spot"));
+      gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 4);
+      gtk_widget_show (frame);
+
+      grid = gtk_grid_new ();
+      gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
+      gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
+      gtk_container_add (GTK_CONTAINER (frame), grid);
+      gtk_widget_show (grid);
+
+      adj = (GtkAdjustment *)
+             gtk_adjustment_new (info->hot_spot_x, 0,
+                                 G_MAXUINT16, 1, 10, 0);
+      spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+      gtk_spin_button_set_range (GTK_SPIN_BUTTON (spinbutton),
+                                 0, G_MAXUINT16);
+      gimp_grid_attach_aligned (GTK_GRID (grid), 0, 0,
+                                _("Hot spot _X:"), 0.0, 0.5,
+                                spinbutton, 1);
+      g_signal_connect (adj, "value-changed",
+                        G_CALLBACK (gimp_int_adjustment_update),
+                        &info->hot_spot_x);
+
+      adj = (GtkAdjustment *)
+             gtk_adjustment_new (info->hot_spot_y, 0,
+                                 G_MAXUINT16, 1, 10, 0);
+      spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+      gtk_spin_button_set_range (GTK_SPIN_BUTTON (spinbutton),
+                                 0, G_MAXUINT16);
+      gimp_grid_attach_aligned (GTK_GRID (grid), 0, 1,
+                                _("Hot spot _Y:"), 0.0, 0.5,
+                                spinbutton, 1);
+      g_signal_connect (adj, "value-changed",
+                        G_CALLBACK (gimp_int_adjustment_update),
+                        &info->hot_spot_y);
+    }
+
+  /* Cursor */
   frame = gimp_frame_new (_("Icon Details"));
-  gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 4);
   gtk_widget_show (frame);
 
   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
@@ -98,7 +142,7 @@ ico_dialog_new (IcoSaveInfo *info)
                             "by all programs. Older applications may not "
                             "open this file correctly."),
                           NULL);
-  gtk_box_pack_end (GTK_BOX (main_vbox), warning, FALSE, FALSE, 0);
+  gtk_box_pack_end (GTK_BOX (main_vbox), warning, FALSE, FALSE, 12);
   /* don't show the warning here */
 
   g_object_set_data (G_OBJECT (dialog), "warning", warning);
diff --git a/plug-ins/file-ico/ico-load.c b/plug-ins/file-ico/ico-load.c
index 62d8fdce20..abe4f2a915 100644
--- a/plug-ins/file-ico/ico-load.c
+++ b/plug-ins/file-ico/ico-load.c
@@ -120,7 +120,7 @@ ico_read_int8 (FILE   *fp,
 }
 
 
-static guint32
+static IcoFileHeader
 ico_read_init (FILE *fp)
 {
   IcoFileHeader header;
@@ -130,12 +130,13 @@ ico_read_init (FILE *fp)
       ! ico_read_int16 (fp, &header.resource_type, 1) ||
       ! ico_read_int16 (fp, &header.icon_count, 1)    ||
       header.reserved != 0 ||
-      header.resource_type != 1)
+      (header.resource_type != 1 && header.resource_type != 2))
     {
-      return 0;
+      header.icon_count = 0;
+      return header;
     }
 
-  return header.icon_count;
+  return header;
 }
 
 
@@ -229,6 +230,7 @@ ico_read_info (FILE    *fp,
     {
       info[i].width  = entries[i].width;
       info[i].height = entries[i].height;
+      info[i].planes = entries[i].planes;
       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);
@@ -653,14 +655,17 @@ GimpImage *
 ico_load_image (GFile        *file,
                 GError      **error)
 {
-  FILE        *fp;
-  IcoLoadInfo *info;
-  gint         max_width, max_height;
-  gint         i;
-  GimpImage   *image;
-  guchar      *buf;
-  guint        icon_count;
-  gint         maxsize;
+  FILE          *fp;
+  IcoFileHeader  header;
+  IcoLoadInfo   *info;
+  gint           max_width, max_height;
+  gint           i;
+  GimpImage     *image;
+  guchar        *buf;
+  guint          icon_count;
+  gint           maxsize;
+  GimpParasite  *parasite;
+  gchar         *str;
 
   gimp_progress_init_printf (_("Opening '%s'"),
                              gimp_file_get_utf8_name (file));
@@ -675,7 +680,8 @@ ico_load_image (GFile        *file,
       return NULL;
     }
 
-  icon_count = ico_read_init (fp);
+  header = ico_read_init (fp);
+  icon_count = header.icon_count;
   if (!icon_count)
     {
       fclose (fp);
@@ -710,6 +716,18 @@ ico_load_image (GFile        *file,
   image = gimp_image_new (max_width, max_height, GIMP_RGB);
   gimp_image_set_file (image, file);
 
+  /* Save CUR hot spot information */
+  if (header.resource_type == 2)
+    {
+      str = g_strdup_printf ("%d %d", info[0].planes, info[0].bpp);
+      parasite = gimp_parasite_new ("cur-hot-spot",
+                                    GIMP_PARASITE_PERSISTENT,
+                                    strlen (str) + 1, (gpointer) str);
+      g_free (str);
+      gimp_image_attach_parasite (image, parasite);
+      gimp_parasite_free (parasite);
+    }
+
   maxsize = max_width * max_height * 4;
   buf = g_new (guchar, max_width * max_height * 4);
   for (i = 0; i < icon_count; i++)
@@ -731,15 +749,16 @@ ico_load_thumbnail_image (GFile   *file,
                           gint    *height,
                           GError **error)
 {
-  FILE        *fp;
-  IcoLoadInfo *info;
-  GimpImage   *image;
-  gint         w     = 0;
-  gint         h     = 0;
-  gint         bpp   = 0;
-  gint         match = 0;
-  gint         i, icon_count;
-  guchar      *buf;
+  FILE          *fp;
+  IcoLoadInfo   *info;
+  IcoFileHeader  header;
+  GimpImage     *image;
+  gint           w     = 0;
+  gint           h     = 0;
+  gint           bpp   = 0;
+  gint           match = 0;
+  gint           i, icon_count;
+  guchar        *buf;
 
   gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
                              gimp_file_get_utf8_name (file));
@@ -754,7 +773,8 @@ ico_load_thumbnail_image (GFile   *file,
       return NULL;
     }
 
-  icon_count = ico_read_init (fp);
+  header = ico_read_init (fp);
+  icon_count = header.icon_count;
   if (! icon_count)
     {
       fclose (fp);
diff --git a/plug-ins/file-ico/ico-save.c b/plug-ins/file-ico/ico-save.c
index 4f8bf1c927..3f366e0145 100644
--- a/plug-ins/file-ico/ico-save.c
+++ b/plug-ins/file-ico/ico-save.c
@@ -233,13 +233,36 @@ static gboolean
 ico_save_dialog (GimpImage      *image,
                  IcoSaveInfo    *info)
 {
-  GtkWidget *dialog;
-  GList     *iter;
-  gint       i;
-  gint       response;
+  GtkWidget     *dialog;
+  GList         *iter;
+  gint           i;
+  gint           response;
+  GimpParasite  *parasite = NULL;
 
   gimp_ui_init (PLUG_IN_BINARY);
 
+  /* Loading hot spots for cursors if applicable */
+  parasite = gimp_image_get_parasite (image, "cur-hot-spot");
+
+  if (parasite)
+    {
+      gchar   *parasite_data;
+      guint32  parasite_size;
+      gint x, y;
+
+      parasite_data = (gchar *) gimp_parasite_get_data (parasite, &parasite_size);
+      parasite_data = g_strndup (parasite_data, parasite_size);
+
+      if (sscanf (parasite_data, "%i %i", &x, &y) == 2)
+        {
+          info->hot_spot_x = x;
+          info->hot_spot_y = y;
+        }
+
+      gimp_parasite_free (parasite);
+      g_free (parasite_data);
+    }
+
   dialog = ico_dialog_new (info);
   for (iter = info->layers, i = 0;
        iter;
@@ -1065,26 +1088,61 @@ ico_save_image (GFile      *file,
                 GimpImage  *image,
                 gint32      run_mode,
                 GError    **error)
+{
+  IcoSaveInfo    info;
+
+  D(("*** Exporting Microsoft icon file %s\n",
+     gimp_file_get_utf8_name (file)));
+
+  info.is_cursor = FALSE;
+
+  return shared_save_image (file, image, run_mode, error, &info);
+}
+
+GimpPDBStatusType
+cur_save_image (GFile      *file,
+                GimpImage  *image,
+                gint32      run_mode,
+                gint32      hot_spot_x,
+                gint32      hot_spot_y,
+                GError    **error)
+{
+  IcoSaveInfo  info;
+
+  D(("*** Exporting Microsoft cursor file %s\n",
+     gimp_file_get_utf8_name (file)));
+
+  info.is_cursor = TRUE;
+  info.hot_spot_x = hot_spot_x;
+  info.hot_spot_y = hot_spot_y;
+
+  return shared_save_image (file, image, run_mode, error, &info);
+}
+
+GimpPDBStatusType
+shared_save_image (GFile *file,
+                   GimpImage     *image,
+                   gint32         run_mode,
+                   GError       **error,
+                   IcoSaveInfo   *info)
 {
   FILE          *fp;
   GList         *iter;
   gint           width;
   gint           height;
-  IcoSaveInfo    info;
   IcoFileHeader  header;
   IcoFileEntry  *entries;
   gboolean       saved;
   gint           i;
+  GimpParasite  *parasite = NULL;
+  gchar         *str;
 
-  D(("*** Exporting Microsoft icon file %s\n",
-     gimp_file_get_utf8_name (file)));
-
-  ico_save_init (image, &info);
+  ico_save_init (image, info);
 
   if (run_mode == GIMP_RUN_INTERACTIVE)
     {
       /* Allow user to override default values */
-      if ( !ico_save_dialog (image, &info))
+      if ( !ico_save_dialog (image, info))
         return GIMP_PDB_CANCEL;
     }
 
@@ -1103,30 +1161,32 @@ ico_save_image (GFile      *file,
 
   header.reserved = 0;
   header.resource_type = 1;
-  header.icon_count = info.num_icons;
+  if (info->is_cursor)
+    header.resource_type = 2;
+  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);
+      ico_save_info_free (info);
       fclose (fp);
       return GIMP_PDB_EXECUTION_ERROR;
     }
 
-  entries = g_new0 (IcoFileEntry, info.num_icons);
-  if (fwrite (entries, sizeof (IcoFileEntry), info.num_icons, fp) <= 0)
+  entries = g_new0 (IcoFileEntry, info->num_icons);
+  if (fwrite (entries, sizeof (IcoFileEntry), info->num_icons, fp) <= 0)
     {
-      ico_save_info_free (&info);
+      ico_save_info_free (info);
       g_free (entries);
       fclose (fp);
       return GIMP_PDB_EXECUTION_ERROR;
     }
 
-  for (iter = info.layers, i = 0;
+  for (iter = info->layers, i = 0;
        iter;
        iter = g_list_next (iter), i++)
     {
-      gimp_progress_update ((gdouble)i / (gdouble)info.num_icons);
+      gimp_progress_update ((gdouble)i / (gdouble)info->num_icons);
 
       width = gimp_drawable_get_width (iter->data);
       height = gimp_drawable_get_height (iter->data);
@@ -1140,23 +1200,29 @@ ico_save_image (GFile      *file,
           entries[i].width = 0;
           entries[i].height = 0;
         }
-      if ( info.depths[i] <= 8 )
-        entries[i].num_colors = 1 << info.depths[i];
+      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].bpp = info->depths[i];
+      /* .cur file reuses these fields for cursor offsets */
+      if (info->is_cursor)
+        {
+          entries[i].planes = info->hot_spot_x;
+          entries[i].bpp = info->hot_spot_y;
+        }
       entries[i].offset = ftell (fp);
 
-      if (info.compress[i])
-        saved = ico_write_png (fp, iter->data, info.depths[i]);
+      if (info->compress[i])
+        saved = ico_write_png (fp, iter->data, info->depths[i]);
       else
-        saved = ico_write_icon (fp, iter->data, info.depths[i]);
+        saved = ico_write_icon (fp, iter->data, info->depths[i]);
 
       if (!saved)
         {
-          ico_save_info_free (&info);
+          ico_save_info_free (info);
           fclose (fp);
           return GIMP_PDB_EXECUTION_ERROR;
         }
@@ -1164,7 +1230,7 @@ ico_save_image (GFile      *file,
       entries[i].size = ftell (fp) - entries[i].offset;
     }
 
-  for (i = 0; i < info.num_icons; i++)
+  for (i = 0; i < info->num_icons; i++)
     {
       entries[i].planes = GUINT16_TO_LE (entries[i].planes);
       entries[i].bpp    = GUINT16_TO_LE (entries[i].bpp);
@@ -1173,16 +1239,28 @@ ico_save_image (GFile      *file,
     }
 
   if (fseek (fp, sizeof(IcoFileHeader), SEEK_SET) < 0
-      || fwrite (entries, sizeof (IcoFileEntry), info.num_icons, fp) <= 0)
+      || fwrite (entries, sizeof (IcoFileEntry), info->num_icons, fp) <= 0)
     {
-      ico_save_info_free (&info);
+      ico_save_info_free (info);
       fclose (fp);
       return GIMP_PDB_EXECUTION_ERROR;
     }
 
   gimp_progress_update (1.0);
 
-  ico_save_info_free (&info);
+  /* Updating parasite hot spots if needed */
+  if (info->is_cursor)
+    {
+      str = g_strdup_printf ("%d %d", info->hot_spot_x, info->hot_spot_y);
+      parasite = gimp_parasite_new ("cur-hot-spot",
+                                    GIMP_PARASITE_PERSISTENT,
+                                    strlen (str) + 1, (gpointer) str);
+      g_free (str);
+      gimp_image_attach_parasite (image, parasite);
+      gimp_parasite_free (parasite);
+    }
+
+  ico_save_info_free (info);
   fclose (fp);
   g_free (entries);
 
diff --git a/plug-ins/file-ico/ico-save.h b/plug-ins/file-ico/ico-save.h
index 8e78ea969f..920feb05d5 100644
--- a/plug-ins/file-ico/ico-save.h
+++ b/plug-ins/file-ico/ico-save.h
@@ -27,6 +27,19 @@ GimpPDBStatusType ico_save_image          (GFile         *file,
                                            gint32         run_mode,
                                            GError       **error);
 
+GimpPDBStatusType cur_save_image          (GFile         *file,
+                                           GimpImage     *image,
+                                           gint32         run_mode,
+                                           gint32         hot_spot_x,
+                                           gint32         hot_spot_y,
+                                           GError       **error);
+
+GimpPDBStatusType shared_save_image       (GFile         *file,
+                                           GimpImage     *image,
+                                           gint32         run_mode,
+                                           GError       **error,
+                                           IcoSaveInfo   *info);
+
 gboolean          ico_cmap_contains_black (const guchar  *cmap,
                                            gint           num_colors);
 
diff --git a/plug-ins/file-ico/ico.c b/plug-ins/file-ico/ico.c
index cf1926c7f2..25f0f63999 100644
--- a/plug-ins/file-ico/ico.c
+++ b/plug-ins/file-ico/ico.c
@@ -34,8 +34,10 @@
 #include "libgimp/stdplugins-intl.h"
 
 #define LOAD_PROC        "file-ico-load"
+#define LOAD_CUR_PROC    "file-cur-load"
 #define LOAD_THUMB_PROC  "file-ico-load-thumb"
 #define SAVE_PROC        "file-ico-save"
+#define SAVE_CUR_PROC    "file-cur-save"
 
 
 typedef struct _Ico      Ico;
@@ -79,6 +81,14 @@ static GimpValueArray * ico_save             (GimpProcedure        *procedure,
                                               GFile                *file,
                                               const GimpValueArray *args,
                                               gpointer              run_data);
+static GimpValueArray * cur_save             (GimpProcedure        *procedure,
+                                              GimpRunMode           run_mode,
+                                              GimpImage            *image,
+                                              gint                  n_drawables,
+                                              GimpDrawable        **drawables,
+                                              GFile                *file,
+                                              const GimpValueArray *args,
+                                              gpointer              run_data);
 
 
 G_DEFINE_TYPE (Ico, ico, GIMP_TYPE_PLUG_IN)
@@ -107,7 +117,9 @@ ico_query_procedures (GimpPlugIn *plug_in)
 
   list = g_list_append (list, g_strdup (LOAD_THUMB_PROC));
   list = g_list_append (list, g_strdup (LOAD_PROC));
+  list = g_list_append (list, g_strdup (LOAD_CUR_PROC));
   list = g_list_append (list, g_strdup (SAVE_PROC));
+  list = g_list_append (list, g_strdup (SAVE_CUR_PROC));
 
   return list;
 }
@@ -143,6 +155,36 @@ ico_create_procedure (GimpPlugIn  *plug_in,
       gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure),
                                       "0,string,\\000\\001\\000\\000,0,string,\\000\\002\\000\\000");
 
+      gimp_load_procedure_set_thumbnail_loader (GIMP_LOAD_PROCEDURE (procedure),
+                                                LOAD_THUMB_PROC);
+    }
+  else if (! strcmp (name, LOAD_CUR_PROC))
+    {
+      procedure = gimp_load_procedure_new (plug_in, name,
+                                           GIMP_PDB_PROC_TYPE_PLUGIN,
+                                           ico_load, NULL, NULL);
+
+      gimp_procedure_set_menu_label (procedure, N_("Microsoft Windows cursor"));
+      gimp_procedure_set_icon_name (procedure, GIMP_ICON_BRUSH);
+
+      gimp_procedure_set_documentation (procedure,
+                                        "Loads files of Windows CUR file format",
+                                        "Loads files of Windows CUR file format",
+                                        name);
+      gimp_procedure_set_attribution (procedure,
+                                      "Christian Kreibich <christian whoop org>, "
+                                      "Nikc M.",
+                                      "Christian Kreibich <christian whoop org>, "
+                                      "Nikc M.",
+                                      "2002-2022");
+
+      gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
+                                          "image/vnd.microsoft.icon");
+      gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
+                                          "cur");
+      gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure),
+                                      "0,string,\\000\\001\\000\\000,0,string,\\000\\002\\000\\000");
+
       gimp_load_procedure_set_thumbnail_loader (GIMP_LOAD_PROCEDURE (procedure),
                                                 LOAD_THUMB_PROC);
     }
@@ -153,7 +195,7 @@ ico_create_procedure (GimpPlugIn  *plug_in,
                                                 ico_load_thumb, NULL, NULL);
 
       gimp_procedure_set_documentation (procedure,
-                                        "Loads a preview from an Windows ICO file",
+                                        "Loads a preview from a Windows ICO or CUR files",
                                         "",
                                         name);
       gimp_procedure_set_attribution (procedure,
@@ -186,6 +228,45 @@ ico_create_procedure (GimpPlugIn  *plug_in,
       gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
                                           "ico");
     }
+  else if (! strcmp (name, SAVE_CUR_PROC))
+    {
+      procedure = gimp_save_procedure_new (plug_in, name,
+                                           GIMP_PDB_PROC_TYPE_PLUGIN,
+                                           cur_save, NULL, NULL);
+
+      gimp_procedure_set_image_types (procedure, "*");
+
+      gimp_procedure_set_menu_label (procedure, N_("Microsoft Windows cursor"));
+      gimp_procedure_set_icon_name (procedure, GIMP_ICON_BRUSH);
+
+      gimp_procedure_set_documentation (procedure,
+                                        "Saves files in Windows CUR file format",
+                                        "Saves files in Windows CUR file format",
+                                        name);
+      gimp_procedure_set_attribution (procedure,
+                                      "Christian Kreibich <christian whoop org>, "
+                                      "Nikc M.",
+                                      "Christian Kreibich <christian whoop org>, "
+                                      "Nikc M.",
+                                      "2002-2022");
+
+      gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
+                                          "image/vnd.microsoft.icon");
+      gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
+                                          "cur");
+
+      GIMP_PROC_ARG_INT (procedure, "hot-spot-x",
+                         "Hot spot X",
+                         "X coordinate of hot spot",
+                         0, G_MAXUINT16, 0,
+                         G_PARAM_READWRITE);
+
+      GIMP_PROC_ARG_INT (procedure, "hot-spot-y",
+                         "Hot spot Y",
+                         "Y coordinate of hot spot",
+                         0, G_MAXUINT16, 0,
+                         G_PARAM_READWRITE);
+    }
 
   return procedure;
 }
@@ -281,6 +362,41 @@ ico_save (GimpProcedure        *procedure,
   return gimp_procedure_new_return_values (procedure, status, error);
 }
 
+static GimpValueArray *
+cur_save (GimpProcedure        *procedure,
+          GimpRunMode           run_mode,
+          GimpImage            *image,
+          gint                  n_drawables,
+          GimpDrawable        **drawables,
+          GFile                *file,
+          const GimpValueArray *args,
+          gpointer              run_data)
+{
+  GimpProcedureConfig *config;
+  GimpPDBStatusType    status;
+  GError              *error      = NULL;
+  gint                 hot_spot_x = 0;
+  gint                 hot_spot_y = 0;
+
+  INIT_I18N ();
+  gegl_init (NULL, NULL);
+
+  config = gimp_procedure_create_config (procedure);
+  gimp_procedure_config_begin_run (config, image, run_mode, args);
+
+  g_object_get (config,
+                "hot-spot-x",  &hot_spot_x,
+                "hot-spot-y",  &hot_spot_y,
+                NULL);
+
+  status = cur_save_image (file, image, run_mode, hot_spot_x, hot_spot_y, &error);
+
+  gimp_procedure_config_end_run (config, status);
+  g_object_unref (config);
+
+  return gimp_procedure_new_return_values (procedure, status, error);
+}
+
 gint
 ico_rowstride (gint width,
                gint bpp)
diff --git a/plug-ins/file-ico/ico.h b/plug-ins/file-ico/ico.h
index 39374a059c..972dd19ceb 100644
--- a/plug-ins/file-ico/ico.h
+++ b/plug-ins/file-ico/ico.h
@@ -80,6 +80,7 @@ typedef struct _IcoLoadInfo
     guint    width;
     guint    height;
     gint     bpp;
+    gint     planes;
     gint     offset;
     gint     size;
 } IcoLoadInfo;
@@ -91,6 +92,9 @@ typedef struct _IcoSaveInfo
     gboolean    *compress;
     GList       *layers;
     gint         num_icons;
+    gboolean     is_cursor;
+    gint         hot_spot_x;
+    gint         hot_spot_y;
 } IcoSaveInfo;
 
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]