[gimp/alxsa-import-export-ani-files: 75/75] plug-ins: Add .ani file import/export
- From: Alx Sa <sawyeralex src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/alxsa-import-export-ani-files: 75/75] plug-ins: Add .ani file import/export
- Date: Mon, 8 Aug 2022 16:50:21 +0000 (UTC)
commit e4b959fb7811cc65ed09606e99ff61b51c1d9ad2
Author: Alx Sa <cmyk student gmail com>
Date: Wed Jun 22 10:33:49 2022 +0000
plug-ins: Add .ani file import/export
This patch ports a 2.4-based plug-in from James Huang into file-ico.
It largely reads/writes the .ani header, then uses the existing
ico/cur functions to load/export the icon data.
A variable to note current file offsets are added to existing code
for this purpose.
Metadata on the cursor and author's name as well as the animation speed
are also loaded and optionally exported.
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]