mirror of https://github.com/GNOME/gimp.git
plug-ins: Add .ani file import/export
This commit is contained in:
parent
e7faae1dc3
commit
aa51b9e19e
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,49 +1201,122 @@ 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
|
||||
shared_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,
|
||||
GError **error,
|
||||
IcoSaveInfo *info)
|
||||
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;
|
||||
GList *iter;
|
||||
gint width;
|
||||
gint height;
|
||||
IcoFileHeader header;
|
||||
IcoFileEntry *entries;
|
||||
gboolean saved;
|
||||
gint i;
|
||||
GimpParasite *parasite = NULL;
|
||||
gchar *str;
|
||||
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 ? *n_hot_spot_x : 0,
|
||||
hot_spot_x ? *hot_spot_x : NULL,
|
||||
n_hot_spot_y ? *n_hot_spot_y : 0,
|
||||
hot_spot_y ? *hot_spot_y : NULL,
|
||||
error))
|
||||
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)
|
||||
{
|
||||
/* Allow user to override default values */
|
||||
if ( !ico_save_dialog (image, info))
|
||||
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'"),
|
||||
|
@ -1246,6 +1324,212 @@ shared_save_image (GFile *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)
|
||||
{
|
||||
FILE *fp;
|
||||
GList *iter;
|
||||
gint width;
|
||||
gint height;
|
||||
IcoFileHeader header;
|
||||
IcoFileEntry *entries;
|
||||
gboolean saved;
|
||||
gint i;
|
||||
gint num_icons;
|
||||
GimpParasite *parasite = NULL;
|
||||
gchar *str;
|
||||
|
||||
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,
|
||||
hot_spot_y ? *hot_spot_y : NULL,
|
||||
error))
|
||||
{
|
||||
return GIMP_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
|
||||
if (run_mode == GIMP_RUN_INTERACTIVE && ! fp_ani)
|
||||
{
|
||||
/* Allow user to override default values */
|
||||
if ( !ico_save_dialog (image, info,
|
||||
NULL, NULL))
|
||||
return GIMP_PDB_CANCEL;
|
||||
}
|
||||
|
||||
num_icons = (fp_ani) ? 1 : info->num_icons;
|
||||
|
||||
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)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
|
@ -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)
|
||||
{
|
||||
*hot_spot_y = info->hot_spot_y;
|
||||
info->hot_spot_y = NULL;
|
||||
}
|
||||
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);
|
||||
/* If saving .ani file, don't clear until
|
||||
* all icons are saved in ani_save_image ()
|
||||
*/
|
||||
if (! file_offset)
|
||||
{
|
||||
ico_save_info_free (info);
|
||||
fclose (fp);
|
||||
}
|
||||
g_free (entries);
|
||||
|
||||
return GIMP_PDB_SUCCESS;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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: */
|
||||
|
||||
|
|
Loading…
Reference in New Issue