[gimp] plug-ins: Add .ani file import/export
- From: Alx Sa <sawyeralex src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] plug-ins: Add .ani file import/export
- Date: Mon, 8 Aug 2022 18:31:46 +0000 (UTC)
commit aa51b9e19ece8a8c54a513fe33b6d65abcb0fbfb
Author: Alx Sa <cmyk student gmail com>
Date: Mon Aug 8 18:31:45 2022 +0000
plug-ins: Add .ani file import/export
plug-ins/file-ico/ico-dialog.c | 118 ++++++++++++-
plug-ins/file-ico/ico-dialog.h | 10 +-
plug-ins/file-ico/ico-load.c | 195 +++++++++++++++++++--
plug-ins/file-ico/ico-load.h | 7 +
plug-ins/file-ico/ico-save.c | 380 ++++++++++++++++++++++++++++++++++++-----
plug-ins/file-ico/ico-save.h | 11 ++
plug-ins/file-ico/ico.c | 303 +++++++++++++++++++++++++++++++-
plug-ins/file-ico/ico.h | 16 ++
8 files changed, 979 insertions(+), 61 deletions(-)
---
diff --git a/plug-ins/file-ico/ico-dialog.c b/plug-ins/file-ico/ico-dialog.c
index fe48e736e4..2cb893561f 100644
--- a/plug-ins/file-ico/ico-dialog.c
+++ b/plug-ins/file-ico/ico-dialog.c
@@ -38,10 +38,16 @@ static void ico_dialog_toggle_compress (GtkWidget *checkbox,
GObject *hbox);
static void ico_dialog_check_compat (GtkWidget *dialog,
IcoSaveInfo *info);
+static void ico_dialog_ani_update_inam (GtkEntry *entry,
+ gpointer data);
+static void ico_dialog_ani_update_iart (GtkEntry *entry,
+ gpointer data);
GtkWidget *
-ico_dialog_new (IcoSaveInfo *info)
+ico_dialog_new (IcoSaveInfo *info,
+ AniFileHeader *ani_header,
+ AniSaveInfo *ani_info)
{
GtkWidget *dialog;
GtkWidget *main_vbox;
@@ -51,7 +57,8 @@ ico_dialog_new (IcoSaveInfo *info)
GtkWidget *viewport;
GtkWidget *warning;
- dialog = gimp_export_dialog_new (info->is_cursor ?
+ dialog = gimp_export_dialog_new (ani_header ?
+ _("Windows Animated Cursor") : info->is_cursor ?
_("Windows Cursor") : _("Windows Icon"),
PLUG_IN_BINARY,
"plug-in-winicon");
@@ -65,6 +72,11 @@ ico_dialog_new (IcoSaveInfo *info)
*/
g_object_set_data (G_OBJECT (dialog), "save_info", info);
+ if (ani_header)
+ {
+ g_object_set_data (G_OBJECT (dialog), "save_ani_header", ani_header);
+ g_object_set_data (G_OBJECT (dialog), "save_ani_info", ani_info);
+ }
main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
@@ -72,6 +84,82 @@ ico_dialog_new (IcoSaveInfo *info)
main_vbox, TRUE, TRUE, 0);
gtk_widget_show (main_vbox);
+ /*Animated Cursor */
+ if (ani_header)
+ {
+ GtkWidget *grid;
+ GtkAdjustment *adjustment;
+ GtkWidget *spin;
+ GtkWidget *label;
+ GtkWidget *hbox;
+ GtkWidget *entry;
+
+ frame = gimp_frame_new (_("Animated Cursor Settings"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ grid = gtk_grid_new ();
+ gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
+ gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
+ gtk_container_add (GTK_CONTAINER (frame), grid);
+ gtk_widget_show (grid);
+
+ /* Cursor Name */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gimp_grid_attach_aligned (GTK_GRID (grid), 0, 1,
+ _("_Cursor Name (Optional)"),
+ 0.0, 0.5,
+ hbox, 1);
+
+ entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (entry),
+ ani_info->inam ? ani_info->inam : "");
+ gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
+ gtk_widget_show (entry);
+
+ g_signal_connect (GTK_ENTRY (entry), "focus-out-event",
+ G_CALLBACK (ico_dialog_ani_update_inam),
+ NULL);
+
+ /* Author Name */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gimp_grid_attach_aligned (GTK_GRID (grid), 0, 3,
+ _("_Author Name (Optional)"),
+ 0.0, 0.5,
+ hbox, 1);
+
+ entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (entry),
+ ani_info->iart ? ani_info->iart : "");
+ gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
+ gtk_widget_show (entry);
+
+ g_signal_connect (GTK_ENTRY (entry), "focus-out-event",
+ G_CALLBACK (ico_dialog_ani_update_iart),
+ NULL);
+
+ /* Default delay spin */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gimp_grid_attach_aligned (GTK_GRID (grid), 0, 5,
+ _("_Delay between frames:"),
+ 0.0, 0.5,
+ hbox, 1);
+
+ adjustment = gtk_adjustment_new (ani_header->jif_rate, 1, G_MAXINT,
+ 1, 10, 0);
+ spin = gimp_spin_button_new (adjustment, 1, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), spin, FALSE, FALSE, 0);
+ gtk_widget_show (spin);
+
+ label = gtk_label_new (_(" jiffies (16.66 ms)"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &ani_header->jif_rate);
+ }
+
/* Cursor */
frame = gimp_frame_new (_("Icon Details"));
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 4);
@@ -572,3 +660,29 @@ ico_dialog_check_compat (GtkWidget *dialog,
gtk_widget_set_visible (warning, warn);
}
+
+static void
+ico_dialog_ani_update_inam (GtkEntry *entry,
+ gpointer data)
+{
+ AniSaveInfo *ani_info;
+ GtkWidget *dialog;
+
+ dialog = gtk_widget_get_toplevel (GTK_WIDGET (entry));
+
+ ani_info = g_object_get_data (G_OBJECT (dialog), "save_ani_info");
+ ani_info->inam = g_strdup_printf ("%s", gtk_entry_get_text (entry));
+}
+
+static void
+ico_dialog_ani_update_iart (GtkEntry *entry,
+ gpointer data)
+{
+ AniSaveInfo *ani_info;
+ GtkWidget *dialog;
+
+ dialog = gtk_widget_get_toplevel (GTK_WIDGET (entry));
+
+ ani_info = g_object_get_data (G_OBJECT (dialog), "save_ani_info");
+ ani_info->iart = g_strdup_printf ("%s", gtk_entry_get_text (entry));
+}
diff --git a/plug-ins/file-ico/ico-dialog.h b/plug-ins/file-ico/ico-dialog.h
index f39315d80c..ff071af5fb 100644
--- a/plug-ins/file-ico/ico-dialog.h
+++ b/plug-ins/file-ico/ico-dialog.h
@@ -22,9 +22,11 @@
#define __ICO_DIALOG_H__
-GtkWidget * ico_dialog_new (IcoSaveInfo *info);
-void ico_dialog_add_icon (GtkWidget *dialog,
- GimpDrawable *layer,
- gint layer_num);
+GtkWidget * ico_dialog_new (IcoSaveInfo *info,
+ AniFileHeader *ani_header,
+ AniSaveInfo *ani_info);
+void ico_dialog_add_icon (GtkWidget *dialog,
+ GimpDrawable *layer,
+ gint layer_num);
#endif /* __ICO_DIALOG_H__ */
diff --git a/plug-ins/file-ico/ico-load.c b/plug-ins/file-ico/ico-load.c
index 36c3b402bb..b29dab85a8 100644
--- a/plug-ins/file-ico/ico-load.c
+++ b/plug-ins/file-ico/ico-load.c
@@ -142,6 +142,7 @@ ico_read_init (FILE *fp)
static gboolean
ico_read_size (FILE *fp,
+ gint32 file_offset,
IcoLoadInfo *info)
{
png_structp png_ptr;
@@ -151,7 +152,7 @@ ico_read_size (FILE *fp,
gint32 color_type;
guint32 magic;
- if (fseek (fp, info->offset, SEEK_SET) < 0)
+ if (fseek (fp, info->offset + file_offset, SEEK_SET) < 0)
return FALSE;
ico_read_int32 (fp, &magic, 1);
@@ -208,6 +209,7 @@ ico_read_size (FILE *fp,
static IcoLoadInfo*
ico_read_info (FILE *fp,
gint icon_count,
+ gint32 file_offset,
GError **error)
{
gint i;
@@ -237,7 +239,7 @@ ico_read_info (FILE *fp,
if (info[i].width == 0 || info[i].height == 0)
{
- ico_read_size (fp, info + i);
+ ico_read_size (fp, file_offset, info + i);
}
D(("ico_read_info: %ix%i (%i bits, size: %i, offset: %i)\n",
@@ -605,6 +607,7 @@ ico_load_layer (FILE *fp,
gint32 icon_num,
guchar *buf,
gint maxsize,
+ gint32 file_offset,
IcoLoadInfo *info)
{
gint width, height;
@@ -613,7 +616,7 @@ ico_load_layer (FILE *fp,
GeglBuffer *buffer;
gchar name[ICO_MAXBUF];
- if (fseek (fp, info->offset, SEEK_SET) < 0 ||
+ if (fseek (fp, info->offset + file_offset, SEEK_SET) < 0 ||
! ico_read_int32 (fp, &first_bytes, 1))
return NULL;
@@ -653,6 +656,7 @@ ico_load_layer (FILE *fp,
GimpImage *
ico_load_image (GFile *file,
+ gint32 *file_offset,
GError **error)
{
FILE *fp;
@@ -666,8 +670,9 @@ ico_load_image (GFile *file,
gint maxsize;
gchar *str;
- gimp_progress_init_printf (_("Opening '%s'"),
- gimp_file_get_utf8_name (file));
+ if (! file_offset)
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_file_get_utf8_name (file));
fp = g_fopen (g_file_peek_path (file), "rb");
@@ -679,6 +684,9 @@ ico_load_image (GFile *file,
return NULL;
}
+ if (file_offset)
+ fseek (fp, *file_offset, SEEK_SET);
+
header = ico_read_init (fp);
icon_count = header.icon_count;
if (!icon_count)
@@ -687,7 +695,7 @@ ico_load_image (GFile *file,
return NULL;
}
- info = ico_read_info (fp, icon_count, error);
+ info = ico_read_info (fp, icon_count, file_offset ? *file_offset : 0, error);
if (! info)
{
fclose (fp);
@@ -713,7 +721,8 @@ ico_load_image (GFile *file,
D(("image size: %ix%i\n", max_width, max_height));
image = gimp_image_new (max_width, max_height, GIMP_RGB);
- gimp_image_set_file (image, file);
+ if (! file_offset)
+ gimp_image_set_file (image, file);
maxsize = max_width * max_height * 4;
buf = g_new (guchar, max_width * max_height * 4);
@@ -721,7 +730,7 @@ ico_load_image (GFile *file,
{
GimpLayer *layer;
- layer = ico_load_layer (fp, image, i, buf, maxsize, info+i);
+ layer = ico_load_layer (fp, image, i, buf, maxsize, file_offset ? *file_offset : 0, info+i);
/* Save CUR hot spot information */
if (header.resource_type == 2)
@@ -737,10 +746,172 @@ ico_load_image (GFile *file,
gimp_parasite_free (parasite);
}
}
+
+ if (file_offset)
+ *file_offset = ftell (fp);
+
g_free (buf);
g_free (info);
fclose (fp);
+ /* Don't update progress here if .ani file */
+ if (! file_offset)
+ gimp_progress_update (1.0);
+
+ return image;
+}
+
+/* Ported from James Huang's ani.c code, under the GPL license, version 3
+ * or any later version of the license */
+GimpImage *
+ani_load_image (GFile *file,
+ gboolean load_thumb,
+ gint *width,
+ gint *height,
+ GError **error)
+{
+ FILE *fp;
+ GimpImage *image = NULL;
+ GimpParasite *parasite;
+ gchar id[4];
+ guint32 size;
+ gint32 file_offset;
+ gint frame = 1;
+ AniFileHeader header;
+ gchar inam[G_MAXSHORT] = {0};
+ gchar iart[G_MAXSHORT] = {0};
+ gchar *str;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ fp = g_fopen (g_file_peek_path (file), "rb");
+
+ if (! fp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_file_get_utf8_name (file), g_strerror (errno));
+ return NULL;
+ }
+
+ while (fread (id, 1, 4, fp) == 4)
+ {
+ if (memcmp (id, "RIFF", 4) == 0 )
+ {
+ fread (&size, sizeof (size), 1, fp);
+ }
+ else if (memcmp (id, "anih", 4) == 0)
+ {
+ fread (&size, sizeof (size), 1, fp);
+ fread (&header, sizeof (header), 1, fp);
+ }
+ else if (memcmp (id, "rate", 4) == 0)
+ {
+ fread (&size, sizeof (size), 1, fp);
+ fseek (fp, size, SEEK_CUR);
+ }
+ else if (memcmp (id, "seq ", 4) == 0)
+ {
+ fread (&size, sizeof (size), 1, fp);
+ fseek (fp, size, SEEK_CUR);
+ }
+ else if (memcmp (id, "LIST", 4) == 0)
+ {
+ fread (&size, sizeof (size), 1, fp);
+ }
+ else if (memcmp (id, "INAM", 4) == 0)
+ {
+ fread (&size, sizeof (size), 1, fp);
+ fread (&inam, sizeof (char), size, fp);
+ inam[size] = '\0';
+ }
+ else if (memcmp (id, "IART", 4) == 0)
+ {
+ fread (&size, sizeof (size), 1, fp);
+ fread (&iart, sizeof (char), size, fp);
+ iart[size] = '\0';
+ }
+ else if (memcmp (id, "icon", 4) == 0)
+ {
+ fread (&size, sizeof (size), 1, fp);
+ file_offset = ftell (fp);
+ if (load_thumb)
+ {
+ image = ico_load_thumbnail_image (file, width, height, file_offset, error);
+ break;
+ }
+ else
+ {
+ if (! image)
+ {
+ image = ico_load_image (file, &file_offset, error);
+ }
+ else
+ {
+ GimpImage *temp_image = NULL;
+ GimpLayer **layers;
+ GimpLayer *new_layer;
+ gint nlayers;
+
+ temp_image = ico_load_image (file, &file_offset, error);
+ layers = gimp_image_get_layers (temp_image, &nlayers);
+ if (layers)
+ {
+ for (gint i = 0; i < nlayers; i++)
+ {
+ new_layer = gimp_layer_new_from_drawable (GIMP_DRAWABLE (layers[i]),
+ image);
+ gimp_image_insert_layer (image, new_layer, NULL, frame);
+ frame++;
+ }
+ }
+ gimp_image_delete (temp_image);
+ }
+
+ /* Update position after reading icon data */
+ fseek (fp, file_offset, SEEK_SET);
+ if (header.frames > 0)
+ gimp_progress_update ((gdouble) frame /
+ (gdouble) header.frames);
+ }
+ }
+ }
+ fclose (fp);
+
+ /* Saving header metadata */
+ str = g_strdup_printf ("%d", header.jif_rate);
+ parasite = gimp_parasite_new ("ani-header",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (str) + 1, (gpointer) str);
+ g_free (str);
+ gimp_image_attach_parasite (image, parasite);
+ gimp_parasite_free (parasite);
+
+ /* Saving INFO block */
+ if (strlen (inam) > 0)
+ {
+ str = g_strdup_printf ("%s", inam);
+ parasite = gimp_parasite_new ("ani-info-inam",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (str) + 1, (gpointer) str);
+ g_free (str);
+ gimp_image_attach_parasite (image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (strlen (iart) > 0)
+ {
+ str = g_strdup_printf ("%s", iart);
+ parasite = gimp_parasite_new ("ani-info-iart",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (str) + 1, (gpointer) str);
+ g_free (str);
+ gimp_image_attach_parasite (image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ gimp_image_set_file (image, file);
gimp_progress_update (1.0);
return image;
@@ -750,6 +921,7 @@ GimpImage *
ico_load_thumbnail_image (GFile *file,
gint *width,
gint *height,
+ gint32 file_offset,
GError **error)
{
FILE *fp;
@@ -776,6 +948,9 @@ ico_load_thumbnail_image (GFile *file,
return NULL;
}
+ if (file_offset > 0)
+ fseek (fp, file_offset, SEEK_SET);
+
header = ico_read_init (fp);
icon_count = header.icon_count;
if (! icon_count)
@@ -787,7 +962,7 @@ ico_load_thumbnail_image (GFile *file,
D(("*** %s: Microsoft icon file, containing %i icon(s)\n",
gimp_file_get_utf8_name (file), icon_count));
- info = ico_read_info (fp, icon_count, error);
+ info = ico_read_info (fp, icon_count, file_offset, error);
if (! info)
{
fclose (fp);
@@ -821,7 +996,7 @@ ico_load_thumbnail_image (GFile *file,
image = gimp_image_new (w, h, GIMP_RGB);
buf = g_new (guchar, w*h*4);
- ico_load_layer (fp, image, match, buf, w*h*4, info+match);
+ ico_load_layer (fp, image, match, buf, w*h*4, file_offset, info+match);
g_free (buf);
*width = w;
diff --git a/plug-ins/file-ico/ico-load.h b/plug-ins/file-ico/ico-load.h
index 5c97cfdf67..88f9f68d61 100644
--- a/plug-ins/file-ico/ico-load.h
+++ b/plug-ins/file-ico/ico-load.h
@@ -23,10 +23,17 @@
GimpImage * ico_load_image (GFile *file,
+ gint32 *file_offset,
+ GError **error);
+GimpImage * ani_load_image (GFile *file,
+ gboolean load_thumb,
+ gint *width,
+ gint *height,
GError **error);
GimpImage * ico_load_thumbnail_image (GFile *file,
gint *width,
gint *height,
+ gint32 file_offset,
GError **error);
gint ico_get_bit_from_data (const guint8 *data,
diff --git a/plug-ins/file-ico/ico-save.c b/plug-ins/file-ico/ico-save.c
index 4993166732..915c61b373 100644
--- a/plug-ins/file-ico/ico-save.c
+++ b/plug-ins/file-ico/ico-save.c
@@ -82,12 +82,15 @@ static gboolean ico_save_init (GimpImage *image,
GError **error);
static GimpPDBStatusType
shared_save_image (GFile *file,
+ FILE *fp_ani,
GimpImage *image,
gint32 run_mode,
gint *n_hot_spot_x,
gint32 **hot_spot_x,
gint *n_hot_spot_y,
gint32 **hot_spot_y,
+ gint32 file_offset,
+ gint icon_index,
GError **error,
IcoSaveInfo *info);
@@ -302,8 +305,10 @@ ico_save_init (GimpImage *image,
static gboolean
-ico_save_dialog (GimpImage *image,
- IcoSaveInfo *info)
+ico_save_dialog (GimpImage *image,
+ IcoSaveInfo *info,
+ AniFileHeader *ani_header,
+ AniSaveInfo *ani_info)
{
GtkWidget *dialog;
GList *iter;
@@ -312,7 +317,7 @@ ico_save_dialog (GimpImage *image,
gimp_ui_init (PLUG_IN_BINARY);
- dialog = ico_dialog_new (info);
+ dialog = ico_dialog_new (info, ani_header, ani_info);
for (iter = info->layers, i = 0;
iter;
iter = g_list_next (iter), i++)
@@ -1174,9 +1179,9 @@ ico_save_image (GFile *file,
info.is_cursor = FALSE;
- return shared_save_image (file, image, run_mode,
+ return shared_save_image (file, NULL, image, run_mode,
0, NULL, 0, NULL,
- error, &info);
+ 0, 0, error, &info);
}
GimpPDBStatusType
@@ -1196,20 +1201,285 @@ cur_save_image (GFile *file,
info.is_cursor = TRUE;
- return shared_save_image (file, image, run_mode,
+ return shared_save_image (file, NULL, image, run_mode,
n_hot_spot_x, hot_spot_x,
n_hot_spot_y, hot_spot_y,
- error, &info);
+ 0, 0, error, &info);
+}
+
+/* Ported from James Huang's ani.c code, under the GPL v3 license */
+GimpPDBStatusType
+ani_save_image (GFile *file,
+ GimpImage *image,
+ gint32 run_mode,
+ gint *n_hot_spot_x,
+ gint32 **hot_spot_x,
+ gint *n_hot_spot_y,
+ gint32 **hot_spot_y,
+ AniFileHeader *header,
+ AniSaveInfo *ani_info,
+ GError **error)
+{
+ FILE *fp;
+ gint32 i;
+ gchar *str;
+ GimpParasite *parasite = NULL;
+ gchar id[5];
+ guint32 size;
+ gint32 offset, ofs_size_riff, ofs_size_list, ofs_size_icon;
+ gint32 ofs_size_info = 0;
+ IcoSaveInfo info;
+
+ if (! ico_save_init (image, run_mode, &info,
+ *n_hot_spot_x, *hot_spot_x,
+ *n_hot_spot_y, *hot_spot_y,
+ error))
+ {
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* Save individual frames as .cur so we can retain
+ * the hotspot information
+ */
+ info.is_cursor = TRUE;
+
+ /* Default header values */
+ header->bSizeOf = sizeof (*header);
+ header->frames = info.num_icons;
+ header->steps = info.num_icons;
+ header->x = 0;
+ header->y = 0;
+ if (info.depths[0] == 24)
+ {
+ header->bpp = 4;
+ header->planes = 1;
+ }
+ else
+ {
+ header->bpp = 0;
+ header->planes = 0;
+ }
+ header->flags = 1;
+
+ /* Load metadata from parasite */
+ parasite = gimp_image_get_parasite (image, "ani-header");
+ if (parasite)
+ {
+ gchar *parasite_data;
+ guint32 parasite_size;
+ gint jif_rate;
+
+ parasite_data = (gchar *) gimp_parasite_get_data (parasite, ¶site_size);
+ parasite_data = g_strndup (parasite_data, parasite_size);
+
+ if (sscanf (parasite_data, "%i", &jif_rate) == 1)
+ {
+ header->jif_rate = jif_rate;
+ }
+
+ gimp_parasite_free (parasite);
+ g_free (parasite_data);
+ }
+
+ parasite = gimp_image_get_parasite (image, "ani-info-inam");
+ if (parasite)
+ {
+ guint32 parasite_size;
+ gchar *inam = NULL;
+
+ inam = (gchar *) gimp_parasite_get_data (parasite, ¶site_size);
+ ani_info->inam = g_strndup (inam, parasite_size);
+
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_image_get_parasite (image, "ani-info-iart");
+ if (parasite)
+ {
+ guint32 parasite_size;
+ gchar *iart = NULL;
+
+ iart = (gchar *) gimp_parasite_get_data (parasite, ¶site_size);
+ ani_info->iart = g_strndup (iart, parasite_size);
+
+ gimp_parasite_free (parasite);
+ }
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ if (! ico_save_dialog (image, &info,
+ header, ani_info))
+ return GIMP_PDB_CANCEL;
+
+ for (i = 1; i < info.num_icons; i++)
+ {
+ info.depths[i] = info.depths[0];
+ info.default_depths[i] = info.default_depths[0];
+ info.compress[i] = info.compress[0];
+ }
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ fp = g_fopen (g_file_peek_path (file), "wb");
+
+ if (! fp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_file_get_utf8_name (file), g_strerror (errno));
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* Writing the .ani header data */
+ strcpy (id, "RIFF");
+ size = 0;
+ fwrite (id, 4, 1, fp);
+ ofs_size_riff = ftell (fp);
+ fwrite (&size, sizeof (size), 1, fp);
+
+ strcpy (id, "ACON");
+ fwrite (id, 4, 1, fp);
+
+ if ((ani_info->inam && strlen (ani_info->inam) > 0) ||
+ (ani_info->iart && strlen (ani_info->iart) > 0))
+ {
+ gint32 string_size;
+
+ strcpy (id, "LIST");
+ fwrite (id, 4, 1, fp);
+ ofs_size_info = ftell (fp);
+ fwrite (&size, sizeof (size), 1, fp);
+
+ strcpy (id, "INFO");
+ fwrite (id, 4, 1, fp);
+ if (ani_info->inam && strlen (ani_info->inam) > 0) /* Cursor name */
+ {
+ strcpy (id, "INAM");
+ fwrite (id, 4, 1, fp);
+ string_size = strlen (ani_info->inam) + 1;
+ fwrite (&string_size, 4, 1, fp);
+ fwrite (ani_info->inam, string_size, 1, fp);
+ }
+ if (ani_info->iart && strlen (ani_info->iart) > 0) /* Author name */
+ {
+ strcpy (id, "IART");
+ fwrite (id, 4, 1, fp);
+ string_size = strlen (ani_info->iart) + 1;
+ fwrite (&string_size, 4, 1, fp);
+ fwrite (ani_info->iart, string_size, 1, fp);
+ }
+
+ /* Go back and update info list size */
+ fseek (fp, 0L, SEEK_END);
+ size = ftell (fp) - ofs_size_info - 4;
+ fseek (fp, ofs_size_info, SEEK_SET);
+ fwrite (&size, sizeof (size), 1, fp);
+ fseek (fp, 0L, SEEK_END);
+ }
+
+ strcpy (id, "anih");
+ size = sizeof (*header);
+ fwrite (id, 4, 1, fp);
+ fwrite (&size, sizeof (size), 1, fp);
+ fwrite (header, sizeof (*header), 1, fp);
+
+ strcpy (id, "LIST");
+ fwrite (id, 4, 1, fp);
+ ofs_size_list = ftell (fp);
+ fwrite (&size, sizeof (size), 1, fp);
+
+ strcpy (id, "fram");
+ fwrite (id, 4, 1, fp);
+
+ strcpy (id, "icon");
+ for (i = 0; i < info.num_icons; i++ )
+ {
+ GimpPDBStatusType status;
+ fwrite (id, 4, 1, fp);
+ ofs_size_icon = ftell (fp);
+ fwrite (&size, sizeof (size), 1, fp);
+ offset = ftell (fp);
+ status = shared_save_image (file, fp, image, run_mode,
+ n_hot_spot_x, hot_spot_x,
+ n_hot_spot_y, hot_spot_y,
+ offset, i, error, &info);
+
+ if (status != GIMP_PDB_SUCCESS)
+ {
+ ico_save_info_free (&info);
+ g_free (ani_info->inam);
+ g_free (ani_info->iart);
+ fclose (fp);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ fseek (fp, 0L, SEEK_END);
+ size = ftell (fp) - offset;
+ fseek (fp, ofs_size_icon, SEEK_SET);
+ fwrite (&size, sizeof (size), 1, fp);
+ fseek (fp, 0L, SEEK_END);
+
+ gimp_progress_update ((gdouble) i / (gdouble) info.num_icons);
+ }
+ ico_save_info_free (&info);
+
+ fseek (fp, 0L, SEEK_END);
+ size = ftell (fp);
+ fseek (fp, ofs_size_riff, SEEK_SET);
+ fwrite (&size, sizeof (size), 1, fp);
+
+ size -= ofs_size_list;
+ fseek (fp, ofs_size_list, SEEK_SET);
+ fwrite (&size, sizeof (size), 1, fp);
+ fclose (fp);
+
+ /* Update metadata if needed */
+ str = g_strdup_printf ("%d", header->jif_rate);
+ parasite = gimp_parasite_new ("ani-header",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (str) + 1, (gpointer) str);
+ g_free (str);
+ gimp_image_attach_parasite (image, parasite);
+ gimp_parasite_free (parasite);
+
+ if (ani_info->inam && strlen (ani_info->inam) > 0)
+ {
+ str = g_strdup_printf ("%s", ani_info->inam);
+ parasite = gimp_parasite_new ("ani-info-inam",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (ani_info->inam) + 1, (gpointer) str);
+ g_free (str);
+ gimp_image_attach_parasite (image, parasite);
+ gimp_parasite_free (parasite);
+ }
+ if (ani_info->iart && strlen (ani_info->iart) > 0)
+ {
+ str = g_strdup_printf ("%s", ani_info->iart);
+ parasite = gimp_parasite_new ("ani-info-iart",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (ani_info->iart) + 1, (gpointer) str);
+ g_free (str);
+ gimp_image_attach_parasite (image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ gimp_progress_update (1.0);
+
+ return GIMP_PDB_SUCCESS;
}
GimpPDBStatusType
shared_save_image (GFile *file,
+ FILE *fp_ani,
GimpImage *image,
gint32 run_mode,
gint *n_hot_spot_x,
gint32 **hot_spot_x,
gint *n_hot_spot_y,
gint32 **hot_spot_y,
+ gint32 file_offset,
+ gint icon_index,
GError **error,
IcoSaveInfo *info)
{
@@ -1221,10 +1491,12 @@ shared_save_image (GFile *file,
IcoFileEntry *entries;
gboolean saved;
gint i;
+ gint num_icons;
GimpParasite *parasite = NULL;
gchar *str;
- if (! ico_save_init (image, run_mode, info,
+ if (! fp_ani &&
+ ! ico_save_init (image, run_mode, info,
n_hot_spot_x ? *n_hot_spot_x : 0,
hot_spot_x ? *hot_spot_x : NULL,
n_hot_spot_y ? *n_hot_spot_y : 0,
@@ -1234,17 +1506,29 @@ shared_save_image (GFile *file,
return GIMP_PDB_EXECUTION_ERROR;
}
- if (run_mode == GIMP_RUN_INTERACTIVE)
+ if (run_mode == GIMP_RUN_INTERACTIVE && ! fp_ani)
{
/* Allow user to override default values */
- if ( !ico_save_dialog (image, info))
+ if ( !ico_save_dialog (image, info,
+ NULL, NULL))
return GIMP_PDB_CANCEL;
}
- gimp_progress_init_printf (_("Exporting '%s'"),
- gimp_file_get_utf8_name (file));
+ num_icons = (fp_ani) ? 1 : info->num_icons;
- fp = g_fopen (g_file_peek_path (file), "wb");
+ if (! fp_ani)
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ /* If saving an .ani file, we append the next icon frame. */
+ if (! fp_ani)
+ {
+ fp = g_fopen (g_file_peek_path (file), "wb");
+ }
+ else
+ {
+ fp = fp_ani;
+ }
if (! fp)
{
@@ -1258,7 +1542,7 @@ shared_save_image (GFile *file,
header.resource_type = 1;
if (info->is_cursor)
header.resource_type = 2;
- header.icon_count = info->num_icons;
+ header.icon_count = 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))
@@ -1268,8 +1552,8 @@ shared_save_image (GFile *file,
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, num_icons);
+ if (fwrite (entries, sizeof (IcoFileEntry), num_icons, fp) <= 0)
{
ico_save_info_free (info);
g_free (entries);
@@ -1281,7 +1565,12 @@ shared_save_image (GFile *file,
iter;
iter = g_list_next (iter), i++)
{
- gimp_progress_update ((gdouble)i / (gdouble)info->num_icons);
+ if (! fp_ani)
+ gimp_progress_update ((gdouble)i / (gdouble)info->num_icons);
+
+ /* If saving .ani file, jump to the correct frame */
+ if (fp_ani)
+ iter = g_list_nth (info->layers, icon_index);
width = gimp_drawable_get_width (iter->data);
height = gimp_drawable_get_height (iter->data);
@@ -1305,10 +1594,12 @@ shared_save_image (GFile *file,
/* .cur file reuses these fields for cursor offsets */
if (info->is_cursor)
{
- entries[i].planes = info->hot_spot_x[i];
- entries[i].bpp = info->hot_spot_y[i];
+ gint hot_spot_index = icon_index ? icon_index : i;
+
+ entries[i].planes = info->hot_spot_x[hot_spot_index];
+ entries[i].bpp = info->hot_spot_y[hot_spot_index];
}
- entries[i].offset = ftell (fp);
+ entries[i].offset = ftell (fp) - file_offset;
if (info->compress[i])
saved = ico_write_png (fp, iter->data, info->depths[i]);
@@ -1322,10 +1613,13 @@ shared_save_image (GFile *file,
return GIMP_PDB_EXECUTION_ERROR;
}
- entries[i].size = ftell (fp) - entries[i].offset;
+ entries[i].size = ftell (fp) - file_offset - entries[i].offset;
+
+ if (fp_ani)
+ break;
}
- for (i = 0; i < info->num_icons; i++)
+ for (i = 0; i < num_icons; i++)
{
entries[i].planes = GUINT16_TO_LE (entries[i].planes);
entries[i].bpp = GUINT16_TO_LE (entries[i].bpp);
@@ -1333,15 +1627,16 @@ shared_save_image (GFile *file,
entries[i].offset = GUINT32_TO_LE (entries[i].offset);
}
- if (fseek (fp, sizeof(IcoFileHeader), SEEK_SET) < 0
- || fwrite (entries, sizeof (IcoFileEntry), info->num_icons, fp) <= 0)
+ if (fseek (fp, sizeof (IcoFileHeader) + file_offset, SEEK_SET) < 0 ||
+ fwrite (entries, sizeof (IcoFileEntry), num_icons, fp) <= 0)
{
ico_save_info_free (info);
fclose (fp);
return GIMP_PDB_EXECUTION_ERROR;
}
- gimp_progress_update (1.0);
+ if (! fp_ani)
+ gimp_progress_update (1.0);
/* Updating parasite hot spots if needed */
if (info->is_cursor)
@@ -1360,23 +1655,32 @@ shared_save_image (GFile *file,
}
}
- if (hot_spot_x)
+ if (! fp_ani)
{
- *hot_spot_x = info->hot_spot_x;
- info->hot_spot_x = NULL;
+ if (hot_spot_x)
+ {
+ *hot_spot_x = info->hot_spot_x;
+ info->hot_spot_x = NULL;
+ }
+ if (hot_spot_y)
+ {
+ *hot_spot_y = info->hot_spot_y;
+ info->hot_spot_y = NULL;
+ }
+ if (n_hot_spot_x)
+ *n_hot_spot_x = num_icons;
+ if (n_hot_spot_y)
+ *n_hot_spot_y = num_icons;
}
- if (hot_spot_y)
+
+ /* If saving .ani file, don't clear until
+ * all icons are saved in ani_save_image ()
+ */
+ if (! file_offset)
{
- *hot_spot_y = info->hot_spot_y;
- info->hot_spot_y = NULL;
+ ico_save_info_free (info);
+ fclose (fp);
}
- if (n_hot_spot_x)
- *n_hot_spot_x = info->num_icons;
- if (n_hot_spot_y)
- *n_hot_spot_y = info->num_icons;
-
- ico_save_info_free (info);
- fclose (fp);
g_free (entries);
return GIMP_PDB_SUCCESS;
diff --git a/plug-ins/file-ico/ico-save.h b/plug-ins/file-ico/ico-save.h
index 1e1ed82dc9..ff9b3d7a30 100644
--- a/plug-ins/file-ico/ico-save.h
+++ b/plug-ins/file-ico/ico-save.h
@@ -36,6 +36,17 @@ GimpPDBStatusType cur_save_image (GFile *file,
gint32 **hot_spot_y,
GError **error);
+GimpPDBStatusType ani_save_image (GFile *file,
+ GimpImage *image,
+ gint32 run_mode,
+ gint *n_hot_spot_x,
+ gint32 **hot_spot_x,
+ gint *n_hot_spot_y,
+ gint32 **hot_spot_y,
+ AniFileHeader *header,
+ AniSaveInfo *ani_info,
+ GError **error);
+
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 1ac292760d..0b4efda25d 100644
--- a/plug-ins/file-ico/ico.c
+++ b/plug-ins/file-ico/ico.c
@@ -20,8 +20,11 @@
#include "config.h"
+#include <stdlib.h>
#include <string.h>
+#include <glib/gstdio.h>
+
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
@@ -33,11 +36,14 @@
#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"
+#define LOAD_PROC "file-ico-load"
+#define LOAD_CUR_PROC "file-cur-load"
+#define LOAD_ANI_PROC "file-ani-load"
+#define LOAD_THUMB_PROC "file-ico-load-thumb"
+#define LOAD_ANI_THUMB_PROC "file-ani-load-thumb"
+#define SAVE_PROC "file-ico-save"
+#define SAVE_CUR_PROC "file-cur-save"
+#define SAVE_ANI_PROC "file-ani-save"
typedef struct _Ico Ico;
@@ -68,11 +74,21 @@ static GimpValueArray * ico_load (GimpProcedure *procedure,
GFile *file,
const GimpValueArray *args,
gpointer run_data);
+static GimpValueArray * ani_load (GimpProcedure *procedure,
+ GimpRunMode run_mode,
+ GFile *file,
+ const GimpValueArray *args,
+ gpointer run_data);
static GimpValueArray * ico_load_thumb (GimpProcedure *procedure,
GFile *file,
gint size,
const GimpValueArray *args,
gpointer run_data);
+static GimpValueArray * ani_load_thumb (GimpProcedure *procedure,
+ GFile *file,
+ gint size,
+ const GimpValueArray *args,
+ gpointer run_data);
static GimpValueArray * ico_save (GimpProcedure *procedure,
GimpRunMode run_mode,
GimpImage *image,
@@ -89,6 +105,14 @@ static GimpValueArray * cur_save (GimpProcedure *procedure,
GFile *file,
const GimpValueArray *args,
gpointer run_data);
+static GimpValueArray * ani_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)
@@ -118,10 +142,13 @@ ico_query_procedures (GimpPlugIn *plug_in)
GList *list = NULL;
list = g_list_append (list, g_strdup (LOAD_THUMB_PROC));
+ list = g_list_append (list, g_strdup (LOAD_ANI_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 (LOAD_ANI_PROC));
list = g_list_append (list, g_strdup (SAVE_PROC));
list = g_list_append (list, g_strdup (SAVE_CUR_PROC));
+ list = g_list_append (list, g_strdup (SAVE_ANI_PROC));
return list;
}
@@ -190,6 +217,36 @@ ico_create_procedure (GimpPlugIn *plug_in,
gimp_load_procedure_set_thumbnail_loader (GIMP_LOAD_PROCEDURE (procedure),
LOAD_THUMB_PROC);
}
+ else if (! strcmp (name, LOAD_ANI_PROC))
+ {
+ procedure = gimp_load_procedure_new (plug_in, name,
+ GIMP_PDB_PROC_TYPE_PLUGIN,
+ ani_load, NULL, NULL);
+
+ gimp_procedure_set_menu_label (procedure, _("Microsoft Windows animated cursor"));
+ gimp_procedure_set_icon_name (procedure, GIMP_ICON_BRUSH);
+
+ gimp_procedure_set_documentation (procedure,
+ _("Loads files of Windows ANI file format"),
+ "Loads files of Windows ANI file format",
+ name);
+ gimp_procedure_set_attribution (procedure,
+ "Christian Kreibich <christian whoop org>, "
+ "James Huang, Alex S.",
+ "Christian Kreibich <christian whoop org>, "
+ "James Huang, Alex S.",
+ "2007-2022");
+
+ gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
+ "application/x-navi-animation");
+ gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
+ "ani");
+ gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure),
+ "0,string,RIFF");
+
+ gimp_load_procedure_set_thumbnail_loader (GIMP_LOAD_PROCEDURE (procedure),
+ LOAD_ANI_THUMB_PROC);
+ }
else if (! strcmp (name, LOAD_THUMB_PROC))
{
procedure = gimp_thumbnail_procedure_new (plug_in, name,
@@ -205,6 +262,24 @@ ico_create_procedure (GimpPlugIn *plug_in,
"Sven Neumann <sven gimp org>",
"2005");
}
+ else if (! strcmp (name, LOAD_ANI_THUMB_PROC))
+ {
+ procedure = gimp_thumbnail_procedure_new (plug_in, name,
+ GIMP_PDB_PROC_TYPE_PLUGIN,
+ ani_load_thumb, NULL, NULL);
+
+ gimp_procedure_set_documentation (procedure,
+ _("Loads a preview from a Windows ANI files"),
+ "",
+ name);
+ gimp_procedure_set_attribution (procedure,
+ "Dom Lachowicz, Sven Neumann, James Huang, "
+ "Alex S.",
+ "Dom Lachowicz, "
+ "Sven Neumann <sven gimp org>, "
+ "James Huang, Alex S.",
+ "2007-2022");
+ }
else if (! strcmp (name, SAVE_PROC))
{
procedure = gimp_save_procedure_new (plug_in, name,
@@ -277,6 +352,72 @@ ico_create_procedure (GimpPlugIn *plug_in,
"Y coordinates of hot spot (one per layer)",
G_PARAM_READWRITE);
}
+ else if (! strcmp (name, SAVE_ANI_PROC))
+ {
+ procedure = gimp_save_procedure_new (plug_in, name,
+ GIMP_PDB_PROC_TYPE_PLUGIN,
+ ani_save, NULL, NULL);
+
+ gimp_procedure_set_image_types (procedure, "*");
+
+ gimp_procedure_set_menu_label (procedure, _("Microsoft Windows animated cursor"));
+ gimp_procedure_set_icon_name (procedure, GIMP_ICON_BRUSH);
+
+ gimp_procedure_set_documentation (procedure,
+ _("Saves files in Windows ANI file format"),
+ _("Saves files in Windows ANI file format"),
+ name);
+ gimp_procedure_set_attribution (procedure,
+ "Christian Kreibich <christian whoop org>, "
+ "James Huang, Alex S.",
+ "Christian Kreibich <christian whoop org>, "
+ "James Huang, Alex S.",
+ "2007-2022");
+
+ gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
+ "application/x-navi-animation");
+ gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
+ "ani");
+
+ GIMP_PROC_ARG_STRING (procedure, "cursor-name",
+ "Cursor Name",
+ _("Cursor Name (Optional)"),
+ NULL,
+ G_PARAM_READWRITE);
+
+ GIMP_PROC_ARG_STRING (procedure, "author-name",
+ "Cursor Author",
+ _("Cursor Author (Optional)"),
+ NULL,
+ G_PARAM_READWRITE);
+
+ GIMP_PROC_ARG_INT (procedure, "default-delay",
+ "Default delay",
+ "Default delay between frames "
+ "in jiffies (1/60 of a second)",
+ 0, G_MAXINT, 8,
+ G_PARAM_READWRITE);
+
+ GIMP_PROC_ARG_INT (procedure, "n-hot-spot-x",
+ "Number of hot spot's X coordinates",
+ "Number of hot spot's X coordinates",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE);
+ GIMP_PROC_ARG_INT32_ARRAY (procedure, "hot-spot-x",
+ "Hot spot X",
+ "X coordinates of hot spot (one per layer)",
+ G_PARAM_READWRITE);
+
+ GIMP_PROC_ARG_INT (procedure, "n-hot-spot-y",
+ "Number of hot spot's Y coordinates",
+ "Number of hot spot's Y coordinates",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE);
+ GIMP_PROC_ARG_INT32_ARRAY (procedure, "hot-spot-y",
+ "Hot spot Y",
+ "Y coordinates of hot spot (one per layer)",
+ G_PARAM_READWRITE);
+ }
return procedure;
}
@@ -294,7 +435,37 @@ ico_load (GimpProcedure *procedure,
gegl_init (NULL, NULL);
- image = ico_load_image (file, &error);
+ image = ico_load_image (file, NULL, &error);
+
+ if (! image)
+ return gimp_procedure_new_return_values (procedure,
+ GIMP_PDB_EXECUTION_ERROR,
+ error);
+
+ return_vals = gimp_procedure_new_return_values (procedure,
+ GIMP_PDB_SUCCESS,
+ NULL);
+
+ GIMP_VALUES_SET_IMAGE (return_vals, 1, image);
+
+ return return_vals;
+}
+
+static GimpValueArray *
+ani_load (GimpProcedure *procedure,
+ GimpRunMode run_mode,
+ GFile *file,
+ const GimpValueArray *args,
+ gpointer run_data)
+{
+ GimpValueArray *return_vals;
+ GimpImage *image;
+ GError *error = NULL;
+
+ gegl_init (NULL, NULL);
+
+ image = ani_load_image (file, FALSE,
+ NULL, NULL, &error);
if (! image)
return gimp_procedure_new_return_values (procedure,
@@ -329,7 +500,46 @@ ico_load_thumb (GimpProcedure *procedure,
height = size;
image = ico_load_thumbnail_image (file,
- &width, &height, &error);
+ &width, &height, 0, &error);
+
+ if (image)
+ return gimp_procedure_new_return_values (procedure,
+ GIMP_PDB_EXECUTION_ERROR,
+ error);
+
+ return_vals = gimp_procedure_new_return_values (procedure,
+ GIMP_PDB_SUCCESS,
+ NULL);
+
+ GIMP_VALUES_SET_IMAGE (return_vals, 1, image);
+ GIMP_VALUES_SET_INT (return_vals, 2, width);
+ GIMP_VALUES_SET_INT (return_vals, 3, height);
+
+ gimp_value_array_truncate (return_vals, 4);
+
+ return return_vals;
+}
+
+static GimpValueArray *
+ani_load_thumb (GimpProcedure *procedure,
+ GFile *file,
+ gint size,
+ const GimpValueArray *args,
+ gpointer run_data)
+{
+ GimpValueArray *return_vals;
+ gint width;
+ gint height;
+ GimpImage *image;
+ GError *error = NULL;
+
+ gegl_init (NULL, NULL);
+
+ width = size;
+ height = size;
+
+ image = ani_load_image (file, TRUE,
+ &width, &height, &error);
if (image)
return gimp_procedure_new_return_values (procedure,
@@ -426,6 +636,85 @@ cur_save (GimpProcedure *procedure,
return gimp_procedure_new_return_values (procedure, status, error);
}
+static GimpValueArray *
+ani_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;
+ gchar *inam = NULL;
+ gchar *iart = NULL;
+ gint jif_rate = 0;
+ gint32 *hot_spot_x = NULL;
+ gint32 *hot_spot_y = NULL;
+ gint n_hot_spot_x = 0;
+ gint n_hot_spot_y = 0;
+ AniFileHeader header;
+ AniSaveInfo ani_info;
+
+ gegl_init (NULL, NULL);
+
+ config = gimp_procedure_create_config (procedure);
+ gimp_procedure_config_begin_run (config, image, run_mode, args);
+
+ g_object_get (config,
+ "cursor-name", &inam,
+ "author-name", &iart,
+ "default-delay", &jif_rate,
+ "n-hot-spot-x", &n_hot_spot_x,
+ "n-hot-spot-y", &n_hot_spot_y,
+ "hot-spot-x", &hot_spot_x,
+ "hot-spot-y", &hot_spot_y,
+ NULL);
+
+ /* Jiffies (1/60th of a second) used if rate chunk not present. */
+ header.jif_rate = jif_rate;
+ ani_info.inam = inam;
+ ani_info.iart = iart;
+
+ status = ani_save_image (file, image, run_mode,
+ &n_hot_spot_x, &hot_spot_x,
+ &n_hot_spot_y, &hot_spot_y,
+ &header, &ani_info, &error);
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* XXX: seems libgimpconfig is not able to serialize
+ * GimpInt32Array args yet anyway. Still leave this here for now,
+ * as reminder of missing feature when we see the warnings.
+ */
+ g_object_set (config,
+ "cursor-name", NULL,
+ "author-name", NULL,
+ "default-delay", header.jif_rate,
+ "n-hot-spot-x", n_hot_spot_x,
+ "n-hot-spot-y", n_hot_spot_y,
+ /*"hot-spot-x", hot_spot_x,*/
+ /*"hot-spot-y", hot_spot_y,*/
+ NULL);
+ g_free (hot_spot_x);
+ g_free (hot_spot_y);
+
+ g_free (inam);
+ g_free (iart);
+ g_free (ani_info.inam);
+ g_free (ani_info.iart);
+ memset (&ani_info, 0, sizeof (AniSaveInfo));
+ }
+
+ 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 07465d853b..dd8a40d400 100644
--- a/plug-ins/file-ico/ico.h
+++ b/plug-ins/file-ico/ico.h
@@ -97,6 +97,22 @@ typedef struct _IcoSaveInfo
gint *hot_spot_y;
} IcoSaveInfo;
+typedef struct _AniFileHeader
+{
+ guint32 bSizeOf; /* Number of bytes in AniFileHeader (36 bytes) */
+ guint32 frames; /* Number of unique icons in this cursor */
+ guint32 steps; /* Number of Blits before the animation cycles */
+ guint32 x, y; /* Reserved, must be zero. */
+ guint32 bpp, planes; /* Reserved, must be zero. */
+ guint32 jif_rate; /* Default Jiffies (1/60th of a second) if rate chunk's not present. */
+ guint32 flags; /* Animation Flag */
+} AniFileHeader;
+
+typedef struct _AniSaveInfo
+{
+ gchar *inam; /* Cursor name metadata */
+ gchar *iart; /* Author name metadata */
+} AniSaveInfo;
/* Miscellaneous helper functions below: */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]