summaryrefslogtreecommitdiffstats
path: root/plug-ins/file-fli/fli-gimp.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
commite42129241681dde7adae7d20697e7b421682fbb4 (patch)
treeaf1fe815a5e639e68e59fabd8395ec69458b3e5e /plug-ins/file-fli/fli-gimp.c
parentInitial commit. (diff)
downloadgimp-e42129241681dde7adae7d20697e7b421682fbb4.tar.xz
gimp-e42129241681dde7adae7d20697e7b421682fbb4.zip
Adding upstream version 2.10.22.upstream/2.10.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--plug-ins/file-fli/fli-gimp.c984
1 files changed, 984 insertions, 0 deletions
diff --git a/plug-ins/file-fli/fli-gimp.c b/plug-ins/file-fli/fli-gimp.c
new file mode 100644
index 0000000..fb14649
--- /dev/null
+++ b/plug-ins/file-fli/fli-gimp.c
@@ -0,0 +1,984 @@
+/*
+ * GFLI 1.3
+ *
+ * A gimp plug-in to read and write FLI and FLC movies.
+ *
+ * Copyright (C) 1998 Jens Ch. Restemeier <jchrr@hrz.uni-bielefeld.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This is a first loader for FLI and FLC movies. It uses as the same method as
+ * the gif plug-in to store the animation (i.e. 1 layer/frame).
+ *
+ * Current disadvantages:
+ * - Generates A LOT OF warnings.
+ * - Consumes a lot of memory (See wish-list: use the original data or
+ * compression).
+ * - doesn't support palette changes between two frames.
+ *
+ * Wish-List:
+ * - I'd like to have a different format for storing animations, so I can use
+ * Layers and Alpha-Channels for effects. An older version of
+ * this plug-in created one image per frame, and went real soon out of
+ * memory.
+ * - I'd like a method that requests unmodified frames from the original
+ * image, and stores modified without destroying the original file.
+ * - I'd like a way to store additional information about a image to it, for
+ * example copyright stuff or a timecode.
+ * - I've thought about a small utility to mix MIDI events as custom chunks
+ * between the FLI chunks. Anyone interested in implementing this ?
+ */
+
+/*
+ * History:
+ * 1.0 first release
+ * 1.1 first support for FLI saving (BRUN and LC chunks)
+ * 1.2 support for load/save ranges, fixed SGI & SUN problems (I hope...), fixed FLC
+ * 1.3 made saving actually work, alpha channel is silently ignored;
+ loading was broken too, fixed it --Sven
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "fli.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-fli-load"
+#define SAVE_PROC "file-fli-save"
+#define INFO_PROC "file-fli-info"
+#define PLUG_IN_BINARY "file-fli"
+#define PLUG_IN_ROLE "gimp-file-fli"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+/* return the image-ID of the new image, or -1 in case of an error */
+static gint32 load_image (const gchar *filename,
+ gint32 from_frame,
+ gint32 to_frame,
+ GError **error);
+static gboolean load_dialog (const gchar *filename);
+
+static gboolean save_image (const gchar *filename,
+ gint32 image_id,
+ gint32 from_frame,
+ gint32 to_frame,
+ GError **error);
+static gboolean save_dialog (gint32 image_id);
+
+static gboolean get_info (const gchar *filename,
+ gint32 *width,
+ gint32 *height,
+ gint32 *frames,
+ GError **error);
+
+/*
+ * GIMP interface
+ */
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static const GimpParamDef load_args[] =
+{
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ { GIMP_PDB_INT32, "from-frame", "Load beginning from this frame" },
+ { GIMP_PDB_INT32, "to-frame", "End loading with this frame" }
+};
+
+static const GimpParamDef load_return_vals[] =
+{
+ { GIMP_PDB_IMAGE, "image", "Output image" },
+};
+
+static const GimpParamDef save_args[] =
+{
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ { GIMP_PDB_INT32, "from-frame", "Export beginning from this frame" },
+ { GIMP_PDB_INT32, "to-frame", "End exporting with this frame" },
+};
+
+static const GimpParamDef info_args[] =
+{
+ { GIMP_PDB_STRING, "filename", "The name of the file to get info" },
+};
+static const GimpParamDef info_return_vals[] =
+{
+ { GIMP_PDB_INT32, "width", "Width of one frame" },
+ { GIMP_PDB_INT32, "height", "Height of one frame" },
+ { GIMP_PDB_INT32, "frames", "Number of Frames" },
+};
+
+
+static gint32 from_frame;
+static gint32 to_frame;
+
+MAIN ()
+
+static void
+query (void)
+{
+ /*
+ * Load/export procedures
+ */
+ gimp_install_procedure (LOAD_PROC,
+ "load FLI-movies",
+ "This is an experimantal plug-in to handle FLI movies",
+ "Jens Ch. Restemeier",
+ "Jens Ch. Restemeier",
+ "1997",
+ N_("AutoDesk FLIC animation"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args) - 2,
+ G_N_ELEMENTS (load_return_vals),
+ load_args,
+ load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-flic");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "fli,flc",
+ "",
+ "");
+
+ gimp_install_procedure (SAVE_PROC,
+ "export FLI-movies",
+ "This is an experimantal plug-in to handle FLI movies",
+ "Jens Ch. Restemeier",
+ "Jens Ch. Restemeier",
+ "1997",
+ N_("AutoDesk FLIC animation"),
+ "INDEXED,GRAY",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-flic");
+ gimp_register_save_handler (SAVE_PROC,
+ "fli,flc",
+ "");
+
+ /*
+ * Utility functions:
+ */
+ gimp_install_procedure (INFO_PROC,
+ "Get information about a Fli movie",
+ "This is a experimantal plug-in to handle FLI movies",
+ "Jens Ch. Restemeier",
+ "Jens Ch. Restemeier",
+ "1997",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (info_args),
+ G_N_ELEMENTS (info_return_vals),
+ info_args,
+ info_return_vals);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[5];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+ gint32 pc;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ gint32 orig_image_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_NONINTERACTIVE:
+ /*
+ * check for valid parameters:
+ * (Or can I trust GIMP ?)
+ */
+ if ((nparams < G_N_ELEMENTS (load_args) - 2) ||
+ (G_N_ELEMENTS (load_args) < nparams))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ for (pc = 0; pc < G_N_ELEMENTS (load_args) - 2; pc++)
+ {
+ if (load_args[pc].type != param[pc].type)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ }
+ for (pc = G_N_ELEMENTS (load_args) - 2; pc < nparams; pc++)
+ {
+ if (load_args[pc].type != param[pc].type)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ }
+
+ to_frame = ((nparams < G_N_ELEMENTS (load_args) - 1) ?
+ 1 : param[3].data.d_int32);
+ from_frame = ((nparams < G_N_ELEMENTS (load_args)) ?
+ -1 : param[4].data.d_int32);
+
+ image_ID = load_image (param[1].data.d_string,
+ from_frame, to_frame, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ if (load_dialog (param[1].data.d_string))
+ {
+ image_ID = load_image (param[1].data.d_string,
+ from_frame, to_frame, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = orig_image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != G_N_ELEMENTS (save_args))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ for (pc = 0; pc < G_N_ELEMENTS (save_args); pc++)
+ {
+ if (save_args[pc].type!=param[pc].type)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ }
+ if (! save_image (param[3].data.d_string, image_ID,
+ param[5].data.d_int32,
+ param[6].data.d_int32, &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "FLI",
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA |
+ GIMP_EXPORT_CAN_HANDLE_LAYERS);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+
+ if (save_dialog (param[1].data.d_image))
+ {
+ if (! save_image (param[3].data.d_string,
+ image_ID, from_frame, to_frame, &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+ break;
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else if (strcmp (name, INFO_PROC) == 0)
+ {
+ gint32 width, height, frames;
+
+ /*
+ * check for valid parameters;
+ */
+ if (nparams != G_N_ELEMENTS (info_args))
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ for (pc = 0; pc < G_N_ELEMENTS (save_args); pc++)
+ {
+ if (info_args[pc].type != param[pc].type)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (get_info (param[0].data.d_string,
+ &width, &height, &frames, &error))
+ {
+ *nreturn_vals = 4;
+ values[1].type = GIMP_PDB_INT32;
+ values[1].data.d_int32 = width;
+ values[2].type = GIMP_PDB_INT32;
+ values[2].data.d_int32 = height;
+ values[3].type = GIMP_PDB_INT32;
+ values[3].data.d_int32 = frames;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+/*
+ * Open FLI animation and return header-info
+ */
+static gboolean
+get_info (const gchar *filename,
+ gint32 *width,
+ gint32 *height,
+ gint32 *frames,
+ GError **error)
+{
+ FILE *file;
+ s_fli_header fli_header;
+
+ *width = 0; *height = 0; *frames = 0;
+
+ file = g_fopen (filename ,"rb");
+
+ if (!file)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+
+ fli_read_header (file, &fli_header);
+ fclose (file);
+
+ *width = fli_header.width;
+ *height = fli_header.height;
+ *frames = fli_header.frames;
+
+ return TRUE;
+}
+
+/*
+ * load fli animation and store as framestack
+ */
+static gint32
+load_image (const gchar *filename,
+ gint32 from_frame,
+ gint32 to_frame,
+ GError **error)
+{
+ FILE *file;
+ GeglBuffer *buffer;
+ gint32 image_id, layer_ID;
+ guchar *fb, *ofb, *fb_x;
+ guchar cm[768], ocm[768];
+ s_fli_header fli_header;
+ gint cnt;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ file = g_fopen (filename ,"rb");
+ if (!file)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ fli_read_header (file, &fli_header);
+ if (fli_header.magic == NO_HEADER)
+ {
+ fclose (file);
+ return -1;
+ }
+ else
+ {
+ fseek (file, 128, SEEK_SET);
+ }
+
+ /*
+ * Fix parameters
+ */
+ if ((from_frame==-1) && (to_frame==-1))
+ {
+ /* to make scripting easier: */
+ from_frame=1; to_frame=fli_header.frames;
+ }
+ if (to_frame<from_frame)
+ {
+ to_frame = fli_header.frames;
+ }
+ if (from_frame < 1)
+ {
+ from_frame = 1;
+ }
+ if (to_frame < 1)
+ {
+ /* nothing to do ... */
+ fclose (file);
+ return -1;
+ }
+ if (from_frame >= fli_header.frames)
+ {
+ /* nothing to do ... */
+ fclose (file);
+ return -1;
+ }
+ if (to_frame>fli_header.frames)
+ {
+ to_frame = fli_header.frames;
+ }
+
+ image_id = gimp_image_new (fli_header.width, fli_header.height, GIMP_INDEXED);
+ gimp_image_set_filename (image_id, filename);
+
+ fb = g_malloc (fli_header.width * fli_header.height);
+ ofb = g_malloc (fli_header.width * fli_header.height);
+
+ /*
+ * Skip to the beginning of requested frames:
+ */
+ for (cnt = 1; cnt < from_frame; cnt++)
+ {
+ fli_read_frame (file, &fli_header, ofb, ocm, fb, cm);
+ memcpy (ocm, cm, 768);
+ fb_x = fb; fb = ofb; ofb = fb_x;
+ }
+ /*
+ * Load range
+ */
+ for (cnt = from_frame; cnt <= to_frame; cnt++)
+ {
+ gchar *name_buf = g_strdup_printf (_("Frame (%i)"), cnt);
+
+ layer_ID = gimp_layer_new (image_id, name_buf,
+ fli_header.width, fli_header.height,
+ GIMP_INDEXED_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_id));
+ g_free (name_buf);
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ fli_read_frame (file, &fli_header, ofb, ocm, fb, cm);
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0,
+ fli_header.width,
+ fli_header.height), 0,
+ NULL, fb, GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (buffer);
+
+ if (cnt > 0)
+ gimp_layer_add_alpha (layer_ID);
+
+ gimp_image_insert_layer (image_id, layer_ID, -1, 0);
+
+ if (cnt < to_frame)
+ {
+ memcpy (ocm, cm, 768);
+ fb_x = fb; fb = ofb; ofb = fb_x;
+ }
+
+ gimp_progress_update ((double) cnt + 1 / (double)(to_frame - from_frame));
+ }
+
+ gimp_image_set_colormap (image_id, cm, 256);
+
+ fclose (file);
+
+ g_free (fb);
+ g_free (ofb);
+
+ gimp_progress_update (1.0);
+
+ return image_id;
+}
+
+
+#define MAXDIFF 195075 /* 3 * SQR (255) + 1 */
+
+/*
+ * get framestack and store as fli animation
+ * (some code was taken from the GIF plugin.)
+ */
+static gboolean
+save_image (const gchar *filename,
+ gint32 image_id,
+ gint32 from_frame,
+ gint32 to_frame,
+ GError **error)
+{
+ FILE *file;
+ gint32 *framelist;
+ gint nframes;
+ gint colors, i;
+ guchar *cmap;
+ guchar bg;
+ guchar red, green, blue;
+ gint diff, sum, max;
+ gint offset_x, offset_y, xc, yc, xx, yy;
+ guint rows, cols, bytes;
+ guchar *src_row;
+ guchar *fb, *ofb;
+ guchar cm[768];
+ GimpRGB background;
+ s_fli_header fli_header;
+ gint cnt;
+
+ framelist = gimp_image_get_layers (image_id, &nframes);
+
+ if ((from_frame == -1) && (to_frame == -1))
+ {
+ /* to make scripting easier: */
+ from_frame = 0; to_frame = nframes;
+ }
+ if (to_frame < from_frame)
+ {
+ to_frame = nframes;
+ }
+ if (from_frame < 1)
+ {
+ from_frame = 1;
+ }
+ if (to_frame < 1)
+ {
+ /* nothing to do ... */
+ return FALSE;
+ }
+ if (from_frame > nframes)
+ {
+ /* nothing to do ... */
+ return FALSE;
+ }
+ if (to_frame > nframes)
+ {
+ to_frame = nframes;
+ }
+
+ gimp_context_get_background (&background);
+ gimp_rgb_get_uchar (&background, &red, &green, &blue);
+
+ switch (gimp_image_base_type (image_id))
+ {
+ case GIMP_GRAY:
+ /* build grayscale palette */
+ for (i = 0; i < 256; i++)
+ {
+ cm[i*3+0] = cm[i*3+1] = cm[i*3+2] = i;
+ }
+ bg = GIMP_RGB_LUMINANCE (red, green, blue) + 0.5;
+ break;
+
+ case GIMP_INDEXED:
+ max = MAXDIFF;
+ bg = 0;
+ cmap = gimp_image_get_colormap (image_id, &colors);
+ for (i = 0; i < MIN (colors, 256); i++)
+ {
+ cm[i*3+0] = cmap[i*3+0];
+ cm[i*3+1] = cmap[i*3+1];
+ cm[i*3+2] = cmap[i*3+2];
+
+ diff = red - cm[i*3+0];
+ sum = SQR (diff);
+ diff = green - cm[i*3+1];
+ sum += SQR (diff);
+ diff = blue - cm[i*3+2];
+ sum += SQR (diff);
+
+ if (sum < max)
+ {
+ bg = i;
+ max = sum;
+ }
+ }
+ for (i = colors; i < 256; i++)
+ {
+ cm[i*3+0] = cm[i*3+1] = cm[i*3+2] = i;
+ }
+ break;
+
+ default:
+ g_message (_("Sorry, I can export only INDEXED and GRAY images."));
+ return FALSE;
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /*
+ * First build the fli header.
+ */
+ fli_header.filesize = 0; /* will be fixed when writing the header */
+ fli_header.frames = 0; /* will be fixed during the write */
+ fli_header.width = gimp_image_width (image_id);
+ fli_header.height = gimp_image_height (image_id);
+
+ if ((fli_header.width == 320) && (fli_header.height == 200))
+ {
+ fli_header.magic = HEADER_FLI;
+ }
+ else
+ {
+ fli_header.magic = HEADER_FLC;
+ }
+ fli_header.depth = 8; /* I've never seen a depth != 8 */
+ fli_header.flags = 3;
+ fli_header.speed = 1000 / 25;
+ fli_header.created = 0; /* program ID. not necessary... */
+ fli_header.updated = 0; /* date in MS-DOS format. ignore...*/
+ fli_header.aspect_x = 1; /* aspect ratio. Will be added as soon.. */
+ fli_header.aspect_y = 1; /* ... as GIMP supports it. */
+ fli_header.oframe1 = fli_header.oframe2 = 0; /* will be fixed during the write */
+
+ file = g_fopen (filename ,"wb");
+ if (!file)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+ fseek (file, 128, SEEK_SET);
+
+ fb = g_malloc (fli_header.width * fli_header.height);
+ ofb = g_malloc (fli_header.width * fli_header.height);
+
+ /* initialize with bg color */
+ memset (fb, bg, fli_header.width * fli_header.height);
+
+ /*
+ * Now write all frames
+ */
+ for (cnt = from_frame; cnt <= to_frame; cnt++)
+ {
+ GeglBuffer *buffer;
+ const Babl *format = NULL;
+
+ buffer = gimp_drawable_get_buffer (framelist[nframes-cnt]);
+
+ if (gimp_drawable_is_gray (framelist[nframes-cnt]))
+ {
+ if (gimp_drawable_has_alpha (framelist[nframes-cnt]))
+ format = babl_format ("Y' u8");
+ else
+ format = babl_format ("Y'A u8");
+ }
+ else
+ {
+ format = gegl_buffer_get_format (buffer);
+ }
+
+ cols = gegl_buffer_get_width (buffer);
+ rows = gegl_buffer_get_height (buffer);
+
+ gimp_drawable_offsets (framelist[nframes-cnt], &offset_x, &offset_y);
+
+ bytes = babl_format_get_bytes_per_pixel (format);
+
+ src_row = g_malloc (cols * bytes);
+
+ /* now paste it into the framebuffer, with the necessary offset */
+ for (yc = 0, yy = offset_y; yc < rows; yc++, yy++)
+ {
+ if (yy >= 0 && yy < fli_header.height)
+ {
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, yc, cols, 1), 1.0,
+ format, src_row,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (xc = 0, xx = offset_x; xc < cols; xc++, xx++)
+ {
+ if (xx >= 0 && xx < fli_header.width)
+ fb[yy * fli_header.width + xx] = src_row[xc * bytes];
+ }
+ }
+ }
+
+ g_free (src_row);
+ g_object_unref (buffer);
+
+ /* save the frame */
+ if (cnt > from_frame)
+ {
+ /* save frame, allow all codecs */
+ fli_write_frame (file, &fli_header, ofb, cm, fb, cm, W_ALL);
+ }
+ else
+ {
+ /* save first frame, no delta information, allow all codecs */
+ fli_write_frame (file, &fli_header, NULL, NULL, fb, cm, W_ALL);
+ }
+
+ if (cnt < to_frame)
+ memcpy (ofb, fb, fli_header.width * fli_header.height);
+
+ gimp_progress_update ((double) cnt + 1 / (double)(to_frame - from_frame));
+ }
+
+ /*
+ * finish fli
+ */
+ fli_write_header (file, &fli_header);
+ fclose (file);
+
+ g_free (fb);
+ g_free (ofb);
+ g_free (framelist);
+
+ gimp_progress_update (1.0);
+
+ return TRUE;
+}
+
+/*
+ * Dialogs for interactive usage
+ */
+static gboolean
+load_dialog (const gchar *filename)
+{
+ GtkWidget *dialog;
+ GtkWidget *table;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ gint32 width, height, nframes;
+ gboolean run;
+
+ get_info (filename, &width, &height, &nframes, NULL);
+
+ from_frame = 1;
+ to_frame = nframes;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("GFLI 1.3 - Load framestack"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, LOAD_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /*
+ * Maybe I add on-the-fly RGB conversion, to keep palettechanges...
+ * But for now you can set a start- and a end-frame:
+ */
+ adj = (GtkAdjustment *) gtk_adjustment_new (from_frame, 1, nframes, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ C_("frame-range", "_From:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &from_frame);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (to_frame, 1, nframes, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ C_("frame-range", "_To:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &to_frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static gboolean
+save_dialog (gint32 image_id)
+{
+ GtkWidget *dialog;
+ GtkWidget *table;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ gint nframes;
+ gboolean run;
+
+ g_free (gimp_image_get_layers (image_id, &nframes));
+
+ from_frame = 1;
+ to_frame = nframes;
+
+ dialog = gimp_export_dialog_new (_("GFLI 1.3"), PLUG_IN_BINARY, SAVE_PROC);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /*
+ * Maybe I add on-the-fly RGB conversion, to keep palettechanges...
+ * But for now you can set a start- and a end-frame:
+ */
+ adj = (GtkAdjustment *) gtk_adjustment_new (from_frame, 1, nframes, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ C_("frame-range", "_From:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &from_frame);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (to_frame, 1, nframes, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ C_("frame-range", "_To:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &to_frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}