2006-12-09 22:12:23 +00:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
|
|
|
* Multiple-image Network Graphics (MNG) plug-in
|
2003-03-30 22:06:46 +00:00
|
|
|
*
|
2008-03-24 22:01:03 +00:00
|
|
|
* Copyright (C) 2002 Mukund Sivaraman <muks@mukund.org>
|
2024-04-13 15:10:25 +00:00
|
|
|
* Portions are copyright of the authors of the file-gif-export, file-png-export
|
|
|
|
* and file-jpeg-export plug-ins' code. The exact ownership of these code
|
2003-03-30 22:06:46 +00:00
|
|
|
* fragments has not been determined.
|
|
|
|
*
|
|
|
|
* This work was sponsored by Xinit Systems Limited, UK.
|
|
|
|
* http://www.xinitsystems.com/
|
|
|
|
*
|
2009-01-17 22:28:01 +00:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
2003-03-30 22:06:46 +00:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
2009-01-17 22:28:01 +00:00
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
2003-03-30 22:06:46 +00:00
|
|
|
* (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
|
2018-07-11 23:27:07 +02:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2003-03-30 22:06:46 +00:00
|
|
|
* --
|
2003-11-06 15:27:05 +00:00
|
|
|
*
|
2024-04-13 15:10:25 +00:00
|
|
|
* For now, this MNG plug-in can only export images. It cannot load images.
|
2003-03-30 22:06:46 +00:00
|
|
|
* Save your working copy as .xcf and use this for "exporting" your images
|
|
|
|
* to MNG. Supports animation the same way as animated GIFs. Supports alpha
|
|
|
|
* transparency. Uses the libmng library (http://www.libmng.com/).
|
|
|
|
* The MIME content-type for MNG images is video/x-mng for now. Make sure
|
|
|
|
* your web-server is configured appropriately if you want to serve MNG
|
|
|
|
* images.
|
|
|
|
*
|
|
|
|
* Since libmng cannot write PNG, JNG and delta PNG chunks at this time
|
|
|
|
* (when this text was written), this plug-in uses libpng and jpeglib to
|
|
|
|
* create the data for the chunks.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2004-01-20 11:24:14 +00:00
|
|
|
#include <errno.h>
|
2003-03-30 22:06:46 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
2005-03-04 15:12:29 +00:00
|
|
|
#include <glib/gstdio.h>
|
|
|
|
|
2003-03-30 22:06:46 +00:00
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/* libpng and jpeglib are currently used in this plug-in. */
|
|
|
|
|
|
|
|
#include <png.h>
|
|
|
|
#include <jpeglib.h>
|
|
|
|
|
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
/* Grrr. The grrr is because the following have to be defined by the
|
|
|
|
* application as well for some reason, although they were enabled
|
|
|
|
* when libmng was compiled. The authors of libmng must look into
|
|
|
|
* this.
|
|
|
|
*/
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
#if !defined(MNG_SUPPORT_FULL)
|
|
|
|
#define MNG_SUPPORT_FULL 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(MNG_SUPPORT_READ)
|
|
|
|
#define MNG_SUPPORT_READ 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(MNG_SUPPORT_WRITE)
|
|
|
|
#define MNG_SUPPORT_WRITE 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(MNG_SUPPORT_DISPLAY)
|
|
|
|
#define MNG_SUPPORT_DISPLAY 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(MNG_ACCESS_CHUNKS)
|
|
|
|
#define MNG_ACCESS_CHUNKS 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <libmng.h>
|
|
|
|
|
|
|
|
#include "libgimp/gimp.h"
|
|
|
|
#include "libgimp/gimpui.h"
|
|
|
|
|
|
|
|
#include "libgimp/stdplugins-intl.h"
|
|
|
|
|
|
|
|
|
2024-04-13 15:10:25 +00:00
|
|
|
#define EXPORT_PROC "file-mng-export"
|
2008-08-11 10:06:13 +00:00
|
|
|
#define PLUG_IN_BINARY "file-mng"
|
2011-04-08 20:31:34 +02:00
|
|
|
#define PLUG_IN_ROLE "gimp-file-mng"
|
2005-08-14 00:41:26 +00:00
|
|
|
#define SCALE_WIDTH 125
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2004-07-15 06:57:46 +00:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
CHUNKS_PNG_D,
|
|
|
|
CHUNKS_JNG_D,
|
|
|
|
CHUNKS_PNG,
|
|
|
|
CHUNKS_JNG
|
|
|
|
};
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2004-07-15 06:57:46 +00:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
DISPOSE_COMBINE,
|
|
|
|
DISPOSE_REPLACE
|
|
|
|
};
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
|
2011-05-06 16:28:25 +05:30
|
|
|
/* These are not saved or restored. */
|
|
|
|
|
|
|
|
struct mng_globals_t
|
|
|
|
{
|
|
|
|
gboolean has_trns;
|
|
|
|
png_bytep trans;
|
|
|
|
int num_trans;
|
|
|
|
gboolean has_plte;
|
|
|
|
png_colorp palette;
|
|
|
|
int num_palette;
|
|
|
|
};
|
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
/* The output FILE pointer which is used by libmng; passed around as
|
|
|
|
* user data.
|
|
|
|
*/
|
2003-11-06 15:27:05 +00:00
|
|
|
struct mnglib_userdata_t
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
};
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
|
2019-08-27 15:22:15 +02:00
|
|
|
typedef struct _Mng Mng;
|
|
|
|
typedef struct _MngClass MngClass;
|
|
|
|
|
|
|
|
struct _Mng
|
|
|
|
{
|
|
|
|
GimpPlugIn parent_instance;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _MngClass
|
|
|
|
{
|
|
|
|
GimpPlugInClass parent_class;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#define MNG_TYPE (mng_get_type ())
|
2023-10-18 18:29:37 +02:00
|
|
|
#define MNG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MNG_TYPE, Mng))
|
2019-08-27 15:22:15 +02:00
|
|
|
|
|
|
|
GType mng_get_type (void) G_GNUC_CONST;
|
|
|
|
|
|
|
|
static GList * mng_query_procedures (GimpPlugIn *plug_in);
|
|
|
|
static GimpProcedure * mng_create_procedure (GimpPlugIn *plug_in,
|
|
|
|
const gchar *name);
|
|
|
|
|
2024-04-13 15:10:25 +00:00
|
|
|
static GimpValueArray * mng_export (GimpProcedure *procedure,
|
2019-08-27 15:22:15 +02:00
|
|
|
GimpRunMode run_mode,
|
|
|
|
GimpImage *image,
|
|
|
|
GFile *file,
|
2024-05-06 18:38:12 +00:00
|
|
|
GimpExportOptions *options,
|
2023-07-20 23:16:27 +02:00
|
|
|
GimpMetadata *metadata,
|
|
|
|
GimpProcedureConfig *config,
|
2019-08-27 15:22:15 +02:00
|
|
|
gpointer run_data);
|
2004-07-15 06:57:46 +00:00
|
|
|
|
2017-06-04 00:09:27 -05:00
|
|
|
static mng_ptr MNG_DECL myalloc (mng_size_t size);
|
|
|
|
static void MNG_DECL myfree (mng_ptr ptr,
|
|
|
|
mng_size_t size);
|
|
|
|
static mng_bool MNG_DECL myopenstream (mng_handle handle);
|
|
|
|
static mng_bool MNG_DECL myclosestream (mng_handle handle);
|
|
|
|
static mng_bool MNG_DECL mywritedata (mng_handle handle,
|
|
|
|
mng_ptr buf,
|
|
|
|
mng_uint32 size,
|
|
|
|
mng_uint32 *written_size);
|
2008-08-18 06:31:03 +00:00
|
|
|
|
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
static gint32 parse_chunks_type_from_layer_name (const gchar *str,
|
|
|
|
gint default_chunks);
|
|
|
|
static gint32 parse_disposal_type_from_layer_name (const gchar *str,
|
|
|
|
gint default_dispose);
|
|
|
|
static gint32 parse_ms_tag_from_layer_name (const gchar *str,
|
|
|
|
gint default_delay);
|
|
|
|
static gint find_unused_ia_color (guchar *pixels,
|
2008-08-18 06:31:03 +00:00
|
|
|
gint numpixels,
|
|
|
|
gint *colors);
|
|
|
|
static gboolean ia_has_transparent_pixels (guchar *pixels,
|
|
|
|
gint numpixels);
|
|
|
|
|
|
|
|
static gboolean respin_cmap (png_structp png_ptr,
|
|
|
|
png_infop png_info_ptr,
|
|
|
|
guchar *remap,
|
2019-08-27 15:22:15 +02:00
|
|
|
GimpImage *image,
|
2012-11-29 01:30:34 +01:00
|
|
|
GeglBuffer *buffer,
|
2011-04-26 15:18:45 +05:30
|
|
|
int *bit_depth);
|
2008-08-18 06:31:03 +00:00
|
|
|
|
2024-04-13 15:10:25 +00:00
|
|
|
static gboolean mng_export_image (GFile *file,
|
|
|
|
GimpImage *image,
|
|
|
|
gint n_drawables,
|
2024-04-30 13:50:24 +00:00
|
|
|
GList *drawables,
|
2024-04-13 15:10:25 +00:00
|
|
|
GObject *config,
|
|
|
|
GError **error);
|
|
|
|
static gboolean mng_save_dialog (GimpImage *image,
|
|
|
|
GimpProcedure *procedure,
|
|
|
|
GObject *config);
|
2019-08-27 15:22:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (Mng, mng, GIMP_TYPE_PLUG_IN)
|
|
|
|
|
|
|
|
GIMP_MAIN (MNG_TYPE)
|
2022-05-26 00:59:36 +02:00
|
|
|
DEFINE_STD_SET_I18N
|
2019-08-27 15:22:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
static struct mng_globals_t mngg;
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
mng_class_init (MngClass *klass)
|
|
|
|
{
|
|
|
|
GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);
|
|
|
|
|
|
|
|
plug_in_class->query_procedures = mng_query_procedures;
|
|
|
|
plug_in_class->create_procedure = mng_create_procedure;
|
2022-05-26 00:59:36 +02:00
|
|
|
plug_in_class->set_i18n = STD_SET_I18N;
|
2019-08-27 15:22:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mng_init (Mng *mng)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static GList *
|
|
|
|
mng_query_procedures (GimpPlugIn *plug_in)
|
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
return g_list_append (NULL, g_strdup (EXPORT_PROC));
|
2019-08-27 15:22:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static GimpProcedure *
|
|
|
|
mng_create_procedure (GimpPlugIn *plug_in,
|
|
|
|
const gchar *name)
|
|
|
|
{
|
|
|
|
GimpProcedure *procedure = NULL;
|
|
|
|
|
2024-04-13 15:10:25 +00:00
|
|
|
if (! strcmp (name, EXPORT_PROC))
|
2019-08-27 15:22:15 +02:00
|
|
|
{
|
2024-04-20 03:08:57 +00:00
|
|
|
procedure = gimp_export_procedure_new (plug_in, name,
|
|
|
|
GIMP_PDB_PROC_TYPE_PLUGIN,
|
|
|
|
FALSE, mng_export, NULL, NULL);
|
2019-08-27 15:22:15 +02:00
|
|
|
|
|
|
|
gimp_procedure_set_image_types (procedure, "*");
|
|
|
|
|
2022-07-04 22:50:53 +02:00
|
|
|
gimp_procedure_set_menu_label (procedure, _("MNG animation"));
|
2023-04-15 23:13:04 +00:00
|
|
|
gimp_file_procedure_set_format_name (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
_("MNG"));
|
2019-08-27 15:22:15 +02:00
|
|
|
|
|
|
|
gimp_procedure_set_documentation (procedure,
|
2023-04-15 23:13:04 +00:00
|
|
|
_("Saves images in the MNG file format"),
|
|
|
|
_("This plug-in saves images in the "
|
|
|
|
"Multiple-image Network Graphics (MNG) "
|
|
|
|
"format which can be used as a "
|
|
|
|
"replacement for animated GIFs, and "
|
|
|
|
"more."),
|
2019-08-27 15:22:15 +02:00
|
|
|
name);
|
|
|
|
gimp_procedure_set_attribution (procedure,
|
|
|
|
"Mukund Sivaraman <muks@mukund.org>",
|
|
|
|
"Mukund Sivaraman <muks@mukund.org>",
|
|
|
|
"November 19, 2002");
|
|
|
|
|
|
|
|
gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
"image/x-mng");
|
|
|
|
gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
"mng");
|
|
|
|
|
2024-05-06 18:38:12 +00:00
|
|
|
gimp_export_procedure_set_capabilities (GIMP_EXPORT_PROCEDURE (procedure),
|
|
|
|
GIMP_EXPORT_CAN_HANDLE_RGB |
|
|
|
|
GIMP_EXPORT_CAN_HANDLE_GRAY |
|
|
|
|
GIMP_EXPORT_CAN_HANDLE_INDEXED |
|
|
|
|
GIMP_EXPORT_CAN_HANDLE_ALPHA |
|
|
|
|
GIMP_EXPORT_CAN_HANDLE_LAYERS,
|
app, libgimp*, pdb, plug-ins: review and enhance MR !1549.
- Fix annotations for gimp_export_options_get_image() to make it
actually introspectable with the GimpImage being both input and
output. Even though the logic doesn't change much (the input image may
be overriden or not), it doesn't matter for introspection because
images are handled centrally by libgimp and therefore must not be
freed. Actually deleting the image from the central list of images
though remains a manual action depending on code logic, not some
automatic action to be handled by binding engines.
- Add G_GNUC_WARN_UNUSED_RESULT to gimp_export_options_get_image()
because ignoring the returned value is rarely a good idea (as you
usually want to delete the image).
- Remove gimp_export_options_new(): we don't need this constructor
because at this point, the best is to tell plug-in developers to just
pass NULL everywhere. This leaves us free to create a more useful
default constructor if needed, in the future. Main description for
GimpExportOptions has also been updated to say this.
- Add a data_destroy callback for the user data passed in
gimp_export_procedure_set_capabilities().
- Fixing annotations of 'export_options' object from pdb/pdb.pl: input
args would actually be (nullable) and would not transfer ownership
(calling code must still free the object). Return value's ownership on
the other hand is fully transfered.
- Add C and Python unit testing for GimpExportOptions and
gimp_export_options_get_image() in particular.
- Fix or improve various details.
Note that I have also considered for a long time changing the signature
of gimp_export_options_get_image() to return a boolean indicating
whether `image` had been replaced (hence needed deletion) or not. This
also meant getting rid of the GimpExportReturn enum. Right now it would
work because there are no third case, but I was considering the future
possibility that for instance we got some impossible conversion for some
future capability. I'm not sure it would ever happen; and for sure, this
is not desirable because it implies an export failure a bit late in the
workflow. But just in case, let's keep the enum return value. It does
not even make the using code that much more complicated (well just a
value comparison instead of a simple boolean test).
2024-08-17 15:06:27 +02:00
|
|
|
NULL, NULL, NULL);
|
2024-05-06 18:38:12 +00:00
|
|
|
|
2024-06-12 16:53:12 +00:00
|
|
|
gimp_procedure_add_boolean_argument (procedure, "interlaced",
|
|
|
|
_("_Interlace"),
|
|
|
|
_("Use interlacing"),
|
|
|
|
FALSE,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
gimp_procedure_add_int_argument (procedure, "png-compression",
|
|
|
|
_("_PNG compression level"),
|
|
|
|
_("PNG compression level, choose a high compression "
|
|
|
|
"level for small file size"),
|
|
|
|
0, 9, 9,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
gimp_procedure_add_double_argument (procedure, "jpeg-quality",
|
|
|
|
_("JPEG compression _quality"),
|
|
|
|
_("JPEG quality factor"),
|
|
|
|
0, 1, 0.75,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
gimp_procedure_add_double_argument (procedure, "jpeg-smoothing",
|
|
|
|
_("_JPEG smoothing factor"),
|
|
|
|
_("JPEG smoothing factor"),
|
|
|
|
0, 1, 0,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
gimp_procedure_add_boolean_argument (procedure, "loop",
|
|
|
|
_("L_oop"),
|
|
|
|
_("(ANIMATED MNG) Loop infinitely"),
|
|
|
|
TRUE,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
gimp_procedure_add_int_argument (procedure, "default-delay",
|
|
|
|
_("Default fra_me delay"),
|
|
|
|
_("(ANIMATED MNG) Default delay between frames in "
|
|
|
|
"milliseconds"),
|
|
|
|
1, G_MAXINT, 100,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
gimp_procedure_add_int_argument (procedure, "default-chunks",
|
|
|
|
_("Default chunks t_ype"),
|
|
|
|
_("(ANIMATED MNG) Default chunks type "
|
|
|
|
"(0 = PNG + Delta PNG; 1 = JNG + Delta PNG; "
|
|
|
|
"2 = All PNG; 3 = All JNG)"),
|
|
|
|
0, 3, CHUNKS_PNG_D,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
gimp_procedure_add_int_argument (procedure, "default-dispose",
|
|
|
|
_("De_fault frame disposal"),
|
|
|
|
_("(ANIMATED MNG) Default dispose type "
|
|
|
|
"(0 = combine; 1 = replace)"),
|
|
|
|
0, 1, DISPOSE_COMBINE,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
gimp_procedure_add_boolean_argument (procedure, "bkgd",
|
|
|
|
_("Save _background color"),
|
|
|
|
_("Write bKGd (background color) chunk"),
|
|
|
|
FALSE,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
gimp_procedure_add_boolean_argument (procedure, "gama",
|
|
|
|
_("Save _gamma"),
|
|
|
|
_("Write gAMA (gamma) chunk"),
|
|
|
|
FALSE,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
gimp_procedure_add_boolean_argument (procedure, "phys",
|
|
|
|
_("Sa_ve resolution"),
|
|
|
|
_("Write pHYs (image resolution) chunk"),
|
|
|
|
TRUE,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
gimp_procedure_add_boolean_argument (procedure, "time",
|
|
|
|
_("Save creation _time"),
|
|
|
|
_("Write tIME (creation time) chunk"),
|
|
|
|
TRUE,
|
|
|
|
G_PARAM_READWRITE);
|
2019-08-27 15:22:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return procedure;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GimpValueArray *
|
2024-04-13 15:10:25 +00:00
|
|
|
mng_export (GimpProcedure *procedure,
|
|
|
|
GimpRunMode run_mode,
|
|
|
|
GimpImage *image,
|
|
|
|
GFile *file,
|
2024-05-06 18:38:12 +00:00
|
|
|
GimpExportOptions *options,
|
2024-04-13 15:10:25 +00:00
|
|
|
GimpMetadata *metadata,
|
|
|
|
GimpProcedureConfig *config,
|
|
|
|
gpointer run_data)
|
2019-08-27 15:22:15 +02:00
|
|
|
{
|
2023-07-20 23:16:27 +02:00
|
|
|
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
|
|
|
|
GimpExportReturn export = GIMP_EXPORT_IGNORE;
|
2024-04-30 13:50:24 +00:00
|
|
|
GList *drawables;
|
|
|
|
gint n_drawables;
|
2023-07-20 23:16:27 +02:00
|
|
|
GError *error = NULL;
|
2019-08-27 15:22:15 +02:00
|
|
|
|
|
|
|
gegl_init (NULL, NULL);
|
|
|
|
|
|
|
|
if (run_mode == GIMP_RUN_INTERACTIVE)
|
|
|
|
{
|
2024-07-14 20:12:57 +00:00
|
|
|
gimp_ui_init (PLUG_IN_BINARY);
|
2024-05-06 18:38:12 +00:00
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
if (! mng_save_dialog (image, procedure, G_OBJECT (config)))
|
|
|
|
status = GIMP_PDB_CANCEL;
|
2019-08-27 15:22:15 +02:00
|
|
|
}
|
|
|
|
|
2024-05-06 18:38:12 +00:00
|
|
|
export = gimp_export_options_get_image (options, &image);
|
2024-07-14 20:12:57 +00:00
|
|
|
drawables = gimp_image_list_layers (image);
|
|
|
|
n_drawables = g_list_length (drawables);
|
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
if (status == GIMP_PDB_SUCCESS)
|
2019-08-27 15:22:15 +02:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
if (! mng_export_image (file, image, n_drawables, drawables,
|
|
|
|
G_OBJECT (config), &error))
|
2019-11-12 01:36:56 +01:00
|
|
|
{
|
|
|
|
status = GIMP_PDB_EXECUTION_ERROR;
|
|
|
|
}
|
2019-08-27 15:22:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (export == GIMP_EXPORT_EXPORT)
|
2024-04-30 13:50:24 +00:00
|
|
|
gimp_image_delete (image);
|
2019-08-27 15:22:15 +02:00
|
|
|
|
2024-04-30 13:50:24 +00:00
|
|
|
g_list_free (drawables);
|
2019-08-27 15:22:15 +02:00
|
|
|
return gimp_procedure_new_return_values (procedure, status, error);
|
|
|
|
}
|
2008-03-25 22:57:06 +00:00
|
|
|
|
2004-07-15 06:57:46 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Callbacks for libmng
|
|
|
|
*/
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2017-06-04 00:09:27 -05:00
|
|
|
static mng_ptr MNG_DECL
|
2003-03-30 22:06:46 +00:00
|
|
|
myalloc (mng_size_t size)
|
|
|
|
{
|
|
|
|
gpointer ptr;
|
2008-03-25 22:25:47 +00:00
|
|
|
|
2008-04-08 09:38:11 +00:00
|
|
|
ptr = g_try_malloc (size);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
if (ptr != NULL)
|
|
|
|
memset (ptr, 0, size);
|
|
|
|
|
|
|
|
return ((mng_ptr) ptr);
|
|
|
|
}
|
|
|
|
|
2017-06-04 00:09:27 -05:00
|
|
|
static void MNG_DECL
|
2004-07-15 06:57:46 +00:00
|
|
|
myfree (mng_ptr ptr,
|
|
|
|
mng_size_t size)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
|
|
|
g_free (ptr);
|
|
|
|
}
|
|
|
|
|
2017-06-04 00:09:27 -05:00
|
|
|
static mng_bool MNG_DECL
|
2003-03-30 22:06:46 +00:00
|
|
|
myopenstream (mng_handle handle)
|
|
|
|
{
|
|
|
|
return MNG_TRUE;
|
|
|
|
}
|
|
|
|
|
2017-06-04 00:09:27 -05:00
|
|
|
static mng_bool MNG_DECL
|
2003-03-30 22:06:46 +00:00
|
|
|
myclosestream (mng_handle handle)
|
|
|
|
{
|
|
|
|
return MNG_TRUE;
|
|
|
|
}
|
|
|
|
|
2017-06-04 00:09:27 -05:00
|
|
|
static mng_bool MNG_DECL
|
2004-07-15 06:57:46 +00:00
|
|
|
mywritedata (mng_handle handle,
|
|
|
|
mng_ptr buf,
|
|
|
|
mng_uint32 size,
|
|
|
|
mng_uint32 *written_size)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
|
|
|
struct mnglib_userdata_t *userdata =
|
|
|
|
(struct mnglib_userdata_t *) mng_get_userdata (handle);
|
|
|
|
|
|
|
|
|
|
|
|
*written_size = (mng_uint32) fwrite ((void *) buf, 1,
|
2004-07-14 10:55:31 +00:00
|
|
|
(size_t) size, userdata->fp);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
return MNG_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
/* Parses which output chunk type to use for this layer from the layer
|
|
|
|
* name.
|
|
|
|
*/
|
2003-03-30 22:06:46 +00:00
|
|
|
static gint32
|
2019-11-12 01:36:56 +01:00
|
|
|
parse_chunks_type_from_layer_name (const gchar *str,
|
|
|
|
gint default_chunks)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
for (i = 0; (i + 5) <= strlen (str); i++)
|
|
|
|
{
|
|
|
|
if (g_ascii_strncasecmp (str + i, "(png)", 5) == 0)
|
2004-07-14 10:55:31 +00:00
|
|
|
return CHUNKS_PNG;
|
2003-03-30 22:06:46 +00:00
|
|
|
else if (g_ascii_strncasecmp (str + i, "(jng)", 5) == 0)
|
2004-07-14 10:55:31 +00:00
|
|
|
return CHUNKS_JNG;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
return default_chunks;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
/* Parses which disposal type to use for this layer from the layer
|
|
|
|
* name.
|
|
|
|
*/
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
static gint32
|
2019-11-12 01:36:56 +01:00
|
|
|
parse_disposal_type_from_layer_name (const gchar *str,
|
|
|
|
gint default_dispose)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
for (i = 0; (i + 9) <= strlen (str); i++)
|
|
|
|
{
|
|
|
|
if (g_ascii_strncasecmp (str + i, "(combine)", 9) == 0)
|
2004-07-14 10:55:31 +00:00
|
|
|
return DISPOSE_COMBINE;
|
2003-03-30 22:06:46 +00:00
|
|
|
else if (g_ascii_strncasecmp (str + i, "(replace)", 9) == 0)
|
2004-07-14 10:55:31 +00:00
|
|
|
return DISPOSE_REPLACE;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
return default_dispose;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Parses the millisecond delay to use for this layer
|
|
|
|
* from the layer name. */
|
|
|
|
|
|
|
|
static gint32
|
2019-11-12 01:36:56 +01:00
|
|
|
parse_ms_tag_from_layer_name (const gchar *str,
|
|
|
|
gint default_delay)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
|
|
|
guint offset = 0;
|
|
|
|
gint32 sum = 0;
|
|
|
|
guint length = strlen (str);
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
while ((offset < length) && (str[offset] != '('))
|
2004-07-14 10:55:31 +00:00
|
|
|
offset++;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
if (offset >= length)
|
2019-11-12 01:36:56 +01:00
|
|
|
return default_delay;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
if (g_ascii_isdigit (str[offset]))
|
2004-07-14 10:55:31 +00:00
|
|
|
break;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
sum *= 10;
|
|
|
|
sum += str[offset] - '0';
|
|
|
|
offset++;
|
|
|
|
}
|
|
|
|
while ((offset < length) && (g_ascii_isdigit (str[offset])));
|
|
|
|
|
|
|
|
if ((length - offset) <= 2)
|
2019-11-12 01:36:56 +01:00
|
|
|
return default_delay;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2005-02-18 22:45:50 +00:00
|
|
|
if ((g_ascii_toupper (str[offset]) != 'M') ||
|
|
|
|
(g_ascii_toupper (str[offset + 1]) != 'S'))
|
2019-11-12 01:36:56 +01:00
|
|
|
return default_delay;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-06 23:26:16 +02:00
|
|
|
/* Try to find a color in the palette which isn't actually
|
2003-11-06 15:27:05 +00:00
|
|
|
* used in the image, so that we can use it as the transparency
|
2003-08-17 14:15:49 +00:00
|
|
|
* index. Taken from png.c */
|
|
|
|
static gint
|
2013-06-06 23:26:16 +02:00
|
|
|
find_unused_ia_color (guchar *pixels,
|
2003-08-17 14:15:49 +00:00
|
|
|
gint numpixels,
|
|
|
|
gint *colors)
|
|
|
|
{
|
|
|
|
gint i;
|
|
|
|
gboolean ix_used[256];
|
|
|
|
gboolean trans_used = FALSE;
|
|
|
|
|
|
|
|
for (i = 0; i < *colors; i++)
|
|
|
|
{
|
|
|
|
ix_used[i] = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < numpixels; i++)
|
|
|
|
{
|
2013-06-06 23:26:16 +02:00
|
|
|
/* If alpha is over a threshold, the color index in the
|
2003-08-17 14:15:49 +00:00
|
|
|
* palette is taken. Otherwise, this pixel is transparent. */
|
|
|
|
if (pixels[i * 2 + 1] > 127)
|
|
|
|
ix_used[pixels[i * 2]] = TRUE;
|
|
|
|
else
|
|
|
|
trans_used = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there is no transparency, ignore alpha. */
|
|
|
|
if (trans_used == FALSE)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (i = 0; i < *colors; i++)
|
|
|
|
{
|
|
|
|
if (ix_used[i] == FALSE)
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-06 23:26:16 +02:00
|
|
|
/* Couldn't find an unused color index within the number of
|
2003-08-17 14:15:49 +00:00
|
|
|
bits per pixel we wanted. Will have to increment the number
|
2013-06-06 23:26:16 +02:00
|
|
|
of colors in the image and assign a transparent pixel there. */
|
2003-08-17 14:15:49 +00:00
|
|
|
if ((*colors) < 256)
|
|
|
|
{
|
|
|
|
(*colors)++;
|
|
|
|
return ((*colors) - 1);
|
|
|
|
}
|
|
|
|
|
2008-03-25 22:25:47 +00:00
|
|
|
return -1;
|
2003-08-17 14:15:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-07-14 22:09:51 +00:00
|
|
|
static gboolean
|
|
|
|
ia_has_transparent_pixels (guchar *pixels,
|
|
|
|
gint numpixels)
|
|
|
|
{
|
|
|
|
while (numpixels --)
|
|
|
|
{
|
|
|
|
if (pixels [1] <= 127)
|
|
|
|
return TRUE;
|
|
|
|
pixels += 2;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2011-04-26 15:18:45 +05:30
|
|
|
static int
|
|
|
|
get_bit_depth_for_palette (int num_palette)
|
|
|
|
{
|
|
|
|
if (num_palette <= 2)
|
|
|
|
return 1;
|
|
|
|
else if (num_palette <= 4)
|
|
|
|
return 2;
|
|
|
|
else if (num_palette <= 16)
|
|
|
|
return 4;
|
|
|
|
else
|
|
|
|
return 8;
|
|
|
|
}
|
2004-07-14 22:09:51 +00:00
|
|
|
|
2003-08-17 14:15:49 +00:00
|
|
|
/* Spins the color map (palette) putting the transparent color at
|
|
|
|
* index 0 if there is space. If there isn't any space, warn the user
|
2004-07-14 12:18:58 +00:00
|
|
|
* and forget about transparency. Returns TRUE if the colormap has
|
|
|
|
* been changed and FALSE otherwise.
|
2003-08-17 14:15:49 +00:00
|
|
|
*/
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2004-07-14 12:18:58 +00:00
|
|
|
static gboolean
|
2011-04-26 15:19:43 +05:30
|
|
|
respin_cmap (png_structp pp,
|
|
|
|
png_infop info,
|
2012-11-29 01:30:34 +01:00
|
|
|
guchar *remap,
|
2019-08-27 15:22:15 +02:00
|
|
|
GimpImage *image,
|
2012-11-29 01:30:34 +01:00
|
|
|
GeglBuffer *buffer,
|
|
|
|
int *bit_depth)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2004-06-15 11:22:05 +00:00
|
|
|
static guchar trans[] = { 0 };
|
|
|
|
guchar *before;
|
|
|
|
guchar *pixels;
|
2004-07-14 22:09:51 +00:00
|
|
|
gint numpixels;
|
2004-06-15 11:22:05 +00:00
|
|
|
gint colors;
|
|
|
|
gint transparent;
|
|
|
|
gint cols, rows;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2023-05-23 23:37:46 +02:00
|
|
|
before = gimp_image_get_colormap (image, NULL, &colors);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2008-03-25 22:25:47 +00:00
|
|
|
/* Make sure there is something in the colormap */
|
2004-06-15 11:22:05 +00:00
|
|
|
if (colors == 0)
|
|
|
|
{
|
2013-01-17 19:23:15 -05:00
|
|
|
before = g_newa (guchar, 3);
|
|
|
|
memset (before, 0, sizeof (guchar) * 3);
|
|
|
|
|
2004-06-15 11:22:05 +00:00
|
|
|
colors = 1;
|
|
|
|
}
|
|
|
|
|
2012-11-29 01:30:34 +01:00
|
|
|
cols = gegl_buffer_get_width (buffer);
|
|
|
|
rows = gegl_buffer_get_height (buffer);
|
2004-07-14 22:09:51 +00:00
|
|
|
numpixels = cols * rows;
|
2003-08-17 14:15:49 +00:00
|
|
|
|
2004-07-14 22:09:51 +00:00
|
|
|
pixels = (guchar *) g_malloc (numpixels * 2);
|
2003-08-17 14:15:49 +00:00
|
|
|
|
2012-11-29 01:30:34 +01:00
|
|
|
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, cols, rows), 1.0,
|
|
|
|
NULL, pixels,
|
|
|
|
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
2003-08-17 14:15:49 +00:00
|
|
|
|
2004-07-14 22:09:51 +00:00
|
|
|
if (ia_has_transparent_pixels (pixels, numpixels))
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2013-06-06 23:26:16 +02:00
|
|
|
transparent = find_unused_ia_color (pixels, numpixels, &colors);
|
2004-07-14 22:09:51 +00:00
|
|
|
|
|
|
|
if (transparent != -1)
|
|
|
|
{
|
2011-05-06 16:28:25 +05:30
|
|
|
static png_color palette[256] = { {0, 0, 0} };
|
2004-07-14 22:09:51 +00:00
|
|
|
gint i;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2011-05-06 16:28:25 +05:30
|
|
|
/* Set tRNS chunk values for writing later. */
|
2019-11-12 01:36:56 +01:00
|
|
|
mngg.has_trns = TRUE;
|
|
|
|
mngg.trans = trans;
|
2011-05-06 16:28:25 +05:30
|
|
|
mngg.num_trans = 1;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2004-07-14 22:09:51 +00:00
|
|
|
/* Transform all pixels with a value = transparent to
|
|
|
|
* 0 and vice versa to compensate for re-ordering in palette
|
2008-03-25 22:25:47 +00:00
|
|
|
* due to png_set_tRNS().
|
|
|
|
*/
|
2003-08-17 14:15:49 +00:00
|
|
|
|
2004-07-14 22:09:51 +00:00
|
|
|
remap[0] = transparent;
|
|
|
|
remap[transparent] = 0;
|
2003-08-17 14:15:49 +00:00
|
|
|
|
2004-07-14 22:09:51 +00:00
|
|
|
/* Copy from index 0 to index transparent - 1 to index 1 to
|
|
|
|
* transparent of after, then from transparent+1 to colors-1
|
2008-03-25 22:25:47 +00:00
|
|
|
* unchanged, and finally from index transparent to index 0.
|
|
|
|
*/
|
2004-07-14 12:18:58 +00:00
|
|
|
|
2009-07-20 22:55:43 +02:00
|
|
|
for (i = 1; i < colors; i++)
|
2004-07-14 22:09:51 +00:00
|
|
|
{
|
|
|
|
palette[i].red = before[3 * remap[i]];
|
|
|
|
palette[i].green = before[3 * remap[i] + 1];
|
|
|
|
palette[i].blue = before[3 * remap[i] + 2];
|
|
|
|
}
|
|
|
|
|
2011-05-06 16:28:25 +05:30
|
|
|
/* Set PLTE chunk values for writing later. */
|
2019-11-12 01:36:56 +01:00
|
|
|
mngg.has_plte = TRUE;
|
|
|
|
mngg.palette = palette;
|
2011-05-06 16:28:25 +05:30
|
|
|
mngg.num_palette = colors;
|
|
|
|
|
2011-04-26 15:18:45 +05:30
|
|
|
*bit_depth = get_bit_depth_for_palette (colors);
|
2004-07-14 22:09:51 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else
|
2008-03-25 22:25:47 +00:00
|
|
|
{
|
|
|
|
g_message (_("Couldn't losslessly save transparency, "
|
|
|
|
"saving opacity instead."));
|
|
|
|
}
|
2003-08-17 14:15:49 +00:00
|
|
|
}
|
2004-07-14 12:18:58 +00:00
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
mngg.has_plte = TRUE;
|
|
|
|
mngg.palette = (png_colorp) before;
|
2011-05-06 16:28:25 +05:30
|
|
|
mngg.num_palette = colors;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2011-04-26 15:18:45 +05:30
|
|
|
*bit_depth = get_bit_depth_for_palette (colors);
|
2004-07-14 22:09:51 +00:00
|
|
|
|
2004-07-14 12:18:58 +00:00
|
|
|
return FALSE;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2009-07-20 22:55:43 +02:00
|
|
|
static mng_retcode
|
|
|
|
mng_putchunk_plte_wrapper (mng_handle handle,
|
|
|
|
gint numcolors,
|
|
|
|
const guchar *colormap)
|
|
|
|
{
|
|
|
|
mng_palette8 palette;
|
|
|
|
|
|
|
|
memset (palette, 0, sizeof palette);
|
|
|
|
if (0 < numcolors)
|
|
|
|
memcpy (palette, colormap, numcolors * sizeof palette[0]);
|
|
|
|
|
|
|
|
return mng_putchunk_plte (handle, numcolors, palette);
|
|
|
|
}
|
|
|
|
|
|
|
|
static mng_retcode
|
|
|
|
mng_putchunk_trns_wrapper (mng_handle handle,
|
|
|
|
gint n_alphas,
|
|
|
|
const guchar *buffer)
|
|
|
|
{
|
|
|
|
const mng_bool mng_global = TRUE;
|
|
|
|
const mng_bool mng_empty = TRUE;
|
|
|
|
mng_uint8arr alphas;
|
|
|
|
|
|
|
|
memset (alphas, 0, sizeof alphas);
|
|
|
|
if (buffer && 0 < n_alphas)
|
|
|
|
memcpy (alphas, buffer, n_alphas * sizeof alphas[0]);
|
|
|
|
|
|
|
|
return mng_putchunk_trns (handle,
|
|
|
|
! mng_empty,
|
|
|
|
! mng_global,
|
|
|
|
MNG_COLORTYPE_INDEXED,
|
|
|
|
n_alphas,
|
|
|
|
alphas,
|
|
|
|
0, 0, 0, 0, 0, alphas);
|
|
|
|
}
|
2003-08-17 14:15:49 +00:00
|
|
|
|
2008-03-25 22:25:47 +00:00
|
|
|
static gboolean
|
2024-04-13 15:10:25 +00:00
|
|
|
mng_export_image (GFile *file,
|
|
|
|
GimpImage *image,
|
|
|
|
gint n_drawables,
|
2024-04-30 13:50:24 +00:00
|
|
|
GList *drawables,
|
2024-04-13 15:10:25 +00:00
|
|
|
GObject *config,
|
|
|
|
GError **error)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2008-03-26 01:44:23 +00:00
|
|
|
gboolean ret = FALSE;
|
2004-07-14 10:55:31 +00:00
|
|
|
gint rows, cols;
|
|
|
|
volatile gint i;
|
|
|
|
time_t t;
|
|
|
|
struct tm *gmt;
|
|
|
|
|
|
|
|
gint num_layers;
|
2019-08-27 15:22:15 +02:00
|
|
|
GimpLayer **layers;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
struct mnglib_userdata_t *userdata;
|
2004-07-14 10:55:31 +00:00
|
|
|
mng_handle handle;
|
|
|
|
guint32 mng_ticks_per_second;
|
|
|
|
guint32 mng_simplicity_profile;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
gboolean config_interlaced;
|
|
|
|
gint config_png_compression;
|
|
|
|
gdouble config_jpeg_quality;
|
|
|
|
gdouble config_jpeg_smoothing;
|
|
|
|
gboolean config_loop;
|
|
|
|
gint config_default_delay;
|
|
|
|
gint config_default_chunks;
|
|
|
|
gint config_default_dispose;
|
|
|
|
gboolean config_bkgd;
|
|
|
|
gboolean config_gama;
|
|
|
|
gboolean config_phys;
|
|
|
|
gboolean config_time;
|
|
|
|
|
|
|
|
g_object_get (config,
|
|
|
|
"interlaced", &config_interlaced,
|
|
|
|
"png-compression", &config_png_compression,
|
|
|
|
"jpeg-quality", &config_jpeg_quality,
|
|
|
|
"jpeg-smoothing", &config_jpeg_smoothing,
|
|
|
|
"loop", &config_loop,
|
2022-08-29 12:14:39 -04:00
|
|
|
"default-delay", &config_default_delay,
|
2019-11-12 01:36:56 +01:00
|
|
|
"default-chunks", &config_default_chunks,
|
|
|
|
"default-dispose", &config_default_dispose,
|
|
|
|
"bkgd", &config_bkgd,
|
|
|
|
"gama", &config_gama,
|
|
|
|
"phys", &config_phys,
|
|
|
|
"time", &config_time,
|
|
|
|
NULL);
|
|
|
|
|
2019-08-27 15:22:15 +02:00
|
|
|
layers = gimp_image_get_layers (image, &num_layers);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
if (num_layers < 1)
|
2008-03-25 22:25:47 +00:00
|
|
|
return FALSE;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
if (num_layers > 1)
|
|
|
|
mng_ticks_per_second = 1000;
|
|
|
|
else
|
|
|
|
mng_ticks_per_second = 0;
|
|
|
|
|
2021-04-06 00:47:07 +02:00
|
|
|
rows = gimp_image_get_height (image);
|
|
|
|
cols = gimp_image_get_width (image);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2008-03-24 22:23:43 +00:00
|
|
|
mng_simplicity_profile = (MNG_SIMPLICITY_VALID |
|
|
|
|
MNG_SIMPLICITY_SIMPLEFEATURES |
|
|
|
|
MNG_SIMPLICITY_COMPLEXFEATURES);
|
|
|
|
|
|
|
|
/* JNG and delta-PNG chunks exist */
|
|
|
|
mng_simplicity_profile |= (MNG_SIMPLICITY_JNG |
|
|
|
|
MNG_SIMPLICITY_DELTAPNG);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
for (i = 0; i < num_layers; i++)
|
2019-08-27 15:22:15 +02:00
|
|
|
{
|
|
|
|
if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layers[i])))
|
|
|
|
{
|
|
|
|
/* internal transparency exists */
|
|
|
|
mng_simplicity_profile |= MNG_SIMPLICITY_TRANSPARENCY;
|
2008-03-24 22:23:43 +00:00
|
|
|
|
2019-08-27 15:22:15 +02:00
|
|
|
/* validity of following flags */
|
|
|
|
mng_simplicity_profile |= 0x00000040;
|
2008-03-24 22:23:43 +00:00
|
|
|
|
2019-08-27 15:22:15 +02:00
|
|
|
/* semi-transparency exists */
|
|
|
|
mng_simplicity_profile |= 0x00000100;
|
2008-03-24 22:23:43 +00:00
|
|
|
|
2019-08-27 15:22:15 +02:00
|
|
|
/* background transparency should happen */
|
|
|
|
mng_simplicity_profile |= 0x00000080;
|
2008-03-24 22:23:43 +00:00
|
|
|
|
2019-08-27 15:22:15 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2004-01-20 11:24:14 +00:00
|
|
|
userdata = g_new0 (struct mnglib_userdata_t, 1);
|
2021-10-01 18:14:14 +02:00
|
|
|
userdata->fp = g_fopen (g_file_peek_path (file), "wb");
|
2019-11-12 01:36:56 +01:00
|
|
|
|
|
|
|
if (! userdata->fp)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2008-08-18 06:31:03 +00:00
|
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
|
|
_("Could not open '%s' for writing: %s"),
|
2019-11-12 01:36:56 +01:00
|
|
|
gimp_file_get_utf8_name (file), g_strerror (errno));
|
2008-03-25 22:25:47 +00:00
|
|
|
goto err;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2008-03-29 02:30:58 +00:00
|
|
|
handle = mng_initialize ((mng_ptr) userdata, myalloc, myfree, NULL);
|
2019-11-12 01:36:56 +01:00
|
|
|
if (! handle)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_initialize() in mng_export_image()");
|
2008-03-25 22:25:47 +00:00
|
|
|
goto err2;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2008-03-25 22:25:47 +00:00
|
|
|
if ((mng_setcb_openstream (handle, myopenstream) != MNG_NOERROR) ||
|
|
|
|
(mng_setcb_closestream (handle, myclosestream) != MNG_NOERROR) ||
|
|
|
|
(mng_setcb_writedata (handle, mywritedata) != MNG_NOERROR))
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to setup callbacks in mng_export_image()");
|
2008-03-25 22:25:47 +00:00
|
|
|
goto err3;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2008-03-25 22:25:47 +00:00
|
|
|
if (mng_create (handle) != MNG_NOERROR)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_create() image in mng_export_image()");
|
2008-03-25 22:25:47 +00:00
|
|
|
goto err3;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2008-03-25 22:25:47 +00:00
|
|
|
if (mng_putchunk_mhdr (handle, cols, rows, mng_ticks_per_second, 1,
|
2019-11-12 01:36:56 +01:00
|
|
|
num_layers, config_default_delay,
|
2008-03-25 22:25:47 +00:00
|
|
|
mng_simplicity_profile) != MNG_NOERROR)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_mhdr() in mng_export_image()");
|
2008-03-25 22:25:47 +00:00
|
|
|
goto err3;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
if ((num_layers > 1) && (config_loop))
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2008-03-25 22:25:47 +00:00
|
|
|
gint32 ms =
|
2019-11-12 01:36:56 +01:00
|
|
|
parse_ms_tag_from_layer_name (gimp_item_get_name (GIMP_ITEM (layers[0])),
|
|
|
|
config_default_delay);
|
2008-03-25 22:25:47 +00:00
|
|
|
|
|
|
|
if (mng_putchunk_term (handle, MNG_TERMACTION_REPEAT,
|
|
|
|
MNG_ITERACTION_LASTFRAME,
|
|
|
|
ms, 0x7fffffff) != MNG_NOERROR)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_term() in mng_export_image()");
|
2008-03-25 22:25:47 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-03-25 22:25:47 +00:00
|
|
|
gint32 ms =
|
2019-11-12 01:36:56 +01:00
|
|
|
parse_ms_tag_from_layer_name (gimp_item_get_name (GIMP_ITEM (layers[0])),
|
|
|
|
config_default_delay);
|
2008-03-25 22:25:47 +00:00
|
|
|
|
|
|
|
if (mng_putchunk_term (handle, MNG_TERMACTION_LASTFRAME,
|
|
|
|
MNG_ITERACTION_LASTFRAME,
|
|
|
|
ms, 0x7fffffff) != MNG_NOERROR)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_term() in mng_export_image()");
|
2008-03-25 22:25:47 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-07-07 13:49:45 +00:00
|
|
|
/* For now, we hardwire a comment */
|
|
|
|
|
2008-03-25 22:57:06 +00:00
|
|
|
if (mng_putchunk_text (handle,
|
|
|
|
strlen (MNG_TEXT_TITLE), MNG_TEXT_TITLE,
|
2009-07-20 22:55:43 +02:00
|
|
|
18, "Created using GIMP") != MNG_NOERROR)
|
2003-07-07 13:49:45 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_text() in mng_export_image()");
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
2003-07-07 13:49:45 +00:00
|
|
|
}
|
|
|
|
|
2004-07-15 06:57:46 +00:00
|
|
|
#if 0
|
2004-07-14 10:55:31 +00:00
|
|
|
|
2008-03-25 22:57:06 +00:00
|
|
|
/* how do we get this to work? */
|
2019-11-12 01:36:56 +01:00
|
|
|
if (config_bkgd)
|
2008-03-25 22:57:06 +00:00
|
|
|
{
|
2024-03-23 04:10:26 +00:00
|
|
|
GeglColor *background;
|
|
|
|
guchar rgb[3];
|
|
|
|
guchar luminance[1];
|
2008-03-25 22:57:06 +00:00
|
|
|
|
2024-03-23 04:10:26 +00:00
|
|
|
background = gimp_context_get_background ();
|
|
|
|
gegl_color_get_pixel (background, babl_format ("R'G'B' u8"), rgb);
|
|
|
|
gegl_color_get_pixel (background, babl_format ("Y' u8"), luminance);
|
|
|
|
g_object_unref (background);
|
2008-03-25 22:57:06 +00:00
|
|
|
|
2024-03-23 04:10:26 +00:00
|
|
|
if (mng_putchunk_back (handle, rgb[0], rgb[1], rgb[2],
|
2008-03-25 22:57:06 +00:00
|
|
|
MNG_BACKGROUNDCOLOR_MANDATORY,
|
|
|
|
0, MNG_BACKGROUNDIMAGE_NOTILE) != MNG_NOERROR)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_back() in mng_export_image()");
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2008-03-25 22:57:06 +00:00
|
|
|
|
|
|
|
if (mng_putchunk_bkgd (handle, MNG_FALSE, 2, 0,
|
2024-03-23 04:10:26 +00:00
|
|
|
luminance,
|
|
|
|
rgb[0], rgb[1], rgb[2])) != MNG_NOERROR)
|
2008-03-25 22:57:06 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_bkgd() in mng_export_image()");
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-07-15 06:57:46 +00:00
|
|
|
#endif
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
if (config_gama)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2008-03-25 22:57:06 +00:00
|
|
|
if (mng_putchunk_gama (handle, MNG_FALSE,
|
2018-06-18 03:06:34 +02:00
|
|
|
(1.0 / 2.2 * 100000)) != MNG_NOERROR)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_gama() in mng_export_image()");
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2004-07-15 06:57:46 +00:00
|
|
|
#if 0
|
2004-07-14 10:55:31 +00:00
|
|
|
|
2008-03-25 22:57:06 +00:00
|
|
|
/* how do we get this to work? */
|
2019-11-12 01:36:56 +01:00
|
|
|
if (config_phys)
|
2008-03-24 22:23:43 +00:00
|
|
|
{
|
2019-11-12 01:36:56 +01:00
|
|
|
gimp_image_get_resolution (image, &xres, &yres);
|
2008-03-25 22:57:06 +00:00
|
|
|
|
|
|
|
if (mng_putchunk_phyg (handle, MNG_FALSE,
|
|
|
|
(mng_uint32) (xres * 39.37),
|
|
|
|
(mng_uint32) (yres * 39.37), 1) != MNG_NOERROR)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_phyg() in mng_export_image()");
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
2008-03-24 22:23:43 +00:00
|
|
|
}
|
2004-07-14 10:55:31 +00:00
|
|
|
|
2008-03-25 22:57:06 +00:00
|
|
|
if (mng_putchunk_phys (handle, MNG_FALSE,
|
|
|
|
(mng_uint32) (xres * 39.37),
|
|
|
|
(mng_uint32) (yres * 39.37), 1) != MNG_NOERROR)
|
2008-03-24 22:23:43 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_phys() in mng_export_image()");
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2008-03-24 22:23:43 +00:00
|
|
|
}
|
2008-03-25 22:57:06 +00:00
|
|
|
|
2004-07-15 06:57:46 +00:00
|
|
|
#endif
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
if (config_time)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
|
|
|
t = time (NULL);
|
|
|
|
gmt = gmtime (&t);
|
|
|
|
|
2008-03-25 22:57:06 +00:00
|
|
|
if (mng_putchunk_time (handle, gmt->tm_year + 1900, gmt->tm_mon + 1,
|
|
|
|
gmt->tm_mday, gmt->tm_hour, gmt->tm_min,
|
|
|
|
gmt->tm_sec) != MNG_NOERROR)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_time() in mng_export_image()");
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2021-04-06 00:47:07 +02:00
|
|
|
if (gimp_image_get_base_type (image) == GIMP_INDEXED)
|
2004-07-14 12:18:58 +00:00
|
|
|
{
|
|
|
|
guchar *palette;
|
|
|
|
gint numcolors;
|
|
|
|
|
2023-05-23 23:37:46 +02:00
|
|
|
palette = gimp_image_get_colormap (image, NULL, &numcolors);
|
2004-07-14 12:18:58 +00:00
|
|
|
|
2008-03-25 22:57:06 +00:00
|
|
|
if ((numcolors != 0) &&
|
2009-07-20 22:55:43 +02:00
|
|
|
(mng_putchunk_plte_wrapper (handle, numcolors,
|
|
|
|
palette) != MNG_NOERROR))
|
2004-07-14 12:18:58 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_plte() in mng_export_image()");
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
2004-07-14 12:18:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-30 22:06:46 +00:00
|
|
|
for (i = (num_layers - 1); i >= 0; i--)
|
|
|
|
{
|
2004-07-15 06:57:46 +00:00
|
|
|
GimpImageType layer_drawable_type;
|
2012-11-29 01:30:34 +01:00
|
|
|
GeglBuffer *layer_buffer;
|
2004-07-15 06:57:46 +00:00
|
|
|
gint layer_offset_x, layer_offset_y;
|
|
|
|
gint layer_rows, layer_cols;
|
|
|
|
gchar *layer_name;
|
|
|
|
gint layer_chunks_type;
|
2012-11-29 01:30:34 +01:00
|
|
|
const Babl *layer_format;
|
2004-07-15 06:57:46 +00:00
|
|
|
volatile gint layer_bpp;
|
|
|
|
|
2016-10-31 01:08:38 +01:00
|
|
|
guint8 __attribute__((unused))layer_mng_colortype;
|
|
|
|
guint8 __attribute__((unused))layer_mng_compression_type;
|
|
|
|
guint8 __attribute__((unused))layer_mng_interlace_type;
|
2004-07-15 06:57:46 +00:00
|
|
|
gboolean layer_has_unique_palette;
|
|
|
|
|
|
|
|
gchar frame_mode;
|
|
|
|
int frame_delay;
|
2019-09-11 22:32:44 +02:00
|
|
|
GFile *temp_file;
|
2011-04-26 15:00:07 +05:30
|
|
|
png_structp pp;
|
|
|
|
png_infop info;
|
2004-07-15 06:57:46 +00:00
|
|
|
FILE *infile, *outfile;
|
|
|
|
int num_passes;
|
|
|
|
int tile_height;
|
|
|
|
guchar **layer_pixels, *layer_pixel;
|
|
|
|
int pass, j, k, begin, end, num;
|
|
|
|
guchar *fixed;
|
|
|
|
guchar layer_remap[256];
|
2011-04-26 15:18:45 +05:30
|
|
|
int color_type;
|
|
|
|
int bit_depth;
|
2004-07-15 06:57:46 +00:00
|
|
|
|
2019-08-27 15:22:15 +02:00
|
|
|
layer_name = gimp_item_get_name (GIMP_ITEM (layers[i]));
|
2019-11-12 01:36:56 +01:00
|
|
|
layer_chunks_type = parse_chunks_type_from_layer_name (layer_name,
|
|
|
|
config_default_chunks);
|
2019-08-27 15:22:15 +02:00
|
|
|
layer_drawable_type = gimp_drawable_type (GIMP_DRAWABLE (layers[i]));
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2019-08-27 15:22:15 +02:00
|
|
|
layer_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layers[i]));
|
2018-07-13 17:55:19 +02:00
|
|
|
layer_cols = gegl_buffer_get_width (layer_buffer);
|
|
|
|
layer_rows = gegl_buffer_get_height (layer_buffer);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2021-04-06 14:28:40 +02:00
|
|
|
gimp_drawable_get_offsets (GIMP_DRAWABLE (layers[i]),
|
2019-08-27 15:22:15 +02:00
|
|
|
&layer_offset_x, &layer_offset_y);
|
2004-07-14 12:18:58 +00:00
|
|
|
layer_has_unique_palette = TRUE;
|
|
|
|
|
2003-08-17 14:15:49 +00:00
|
|
|
for (j = 0; j < 256; j++)
|
|
|
|
layer_remap[j] = j;
|
|
|
|
|
2003-03-30 22:06:46 +00:00
|
|
|
switch (layer_drawable_type)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
|
|
|
case GIMP_RGB_IMAGE:
|
2012-11-29 01:30:34 +01:00
|
|
|
layer_format = babl_format ("R'G'B' u8");
|
2004-07-14 10:55:31 +00:00
|
|
|
layer_mng_colortype = MNG_COLORTYPE_RGB;
|
|
|
|
break;
|
|
|
|
case GIMP_RGBA_IMAGE:
|
2012-11-29 01:30:34 +01:00
|
|
|
layer_format = babl_format ("R'G'B'A u8");
|
2004-07-14 10:55:31 +00:00
|
|
|
layer_mng_colortype = MNG_COLORTYPE_RGBA;
|
|
|
|
break;
|
|
|
|
case GIMP_GRAY_IMAGE:
|
2012-11-29 01:30:34 +01:00
|
|
|
layer_format = babl_format ("Y' u8");
|
2004-07-14 10:55:31 +00:00
|
|
|
layer_mng_colortype = MNG_COLORTYPE_GRAY;
|
|
|
|
break;
|
|
|
|
case GIMP_GRAYA_IMAGE:
|
2012-11-29 01:30:34 +01:00
|
|
|
layer_format = babl_format ("Y'A u8");
|
2004-07-14 10:55:31 +00:00
|
|
|
layer_mng_colortype = MNG_COLORTYPE_GRAYA;
|
|
|
|
break;
|
|
|
|
case GIMP_INDEXED_IMAGE:
|
2012-11-29 01:30:34 +01:00
|
|
|
layer_format = gegl_buffer_get_format (layer_buffer);
|
2004-07-14 10:55:31 +00:00
|
|
|
layer_mng_colortype = MNG_COLORTYPE_INDEXED;
|
|
|
|
break;
|
|
|
|
case GIMP_INDEXEDA_IMAGE:
|
2012-11-29 01:30:34 +01:00
|
|
|
layer_format = gegl_buffer_get_format (layer_buffer);
|
2004-07-14 10:55:31 +00:00
|
|
|
layer_mng_colortype = MNG_COLORTYPE_INDEXED | MNG_COLORTYPE_GRAYA;
|
|
|
|
break;
|
|
|
|
default:
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unsupported GimpImageType in mng_export_image()");
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2012-11-29 01:30:34 +01:00
|
|
|
layer_bpp = babl_format_get_bytes_per_pixel (layer_format);
|
|
|
|
|
2003-03-30 22:06:46 +00:00
|
|
|
/* Delta PNG chunks are not yet supported */
|
|
|
|
|
|
|
|
/* if (i == (num_layers - 1)) */
|
|
|
|
{
|
2004-07-14 10:55:31 +00:00
|
|
|
if (layer_chunks_type == CHUNKS_JNG_D)
|
|
|
|
layer_chunks_type = CHUNKS_JNG;
|
|
|
|
else if (layer_chunks_type == CHUNKS_PNG_D)
|
|
|
|
layer_chunks_type = CHUNKS_PNG;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (layer_chunks_type)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
|
|
|
case CHUNKS_PNG_D:
|
|
|
|
layer_mng_compression_type = MNG_COMPRESSION_DEFLATE;
|
2019-11-12 01:36:56 +01:00
|
|
|
if (config_interlaced)
|
2004-07-14 10:55:31 +00:00
|
|
|
layer_mng_interlace_type = MNG_INTERLACE_ADAM7;
|
|
|
|
else
|
|
|
|
layer_mng_interlace_type = MNG_INTERLACE_NONE;
|
|
|
|
break;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
case CHUNKS_JNG_D:
|
|
|
|
layer_mng_compression_type = MNG_COMPRESSION_DEFLATE;
|
2019-11-12 01:36:56 +01:00
|
|
|
if (config_interlaced)
|
2004-07-14 10:55:31 +00:00
|
|
|
layer_mng_interlace_type = MNG_INTERLACE_ADAM7;
|
|
|
|
else
|
|
|
|
layer_mng_interlace_type = MNG_INTERLACE_NONE;
|
|
|
|
break;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
case CHUNKS_PNG:
|
|
|
|
layer_mng_compression_type = MNG_COMPRESSION_DEFLATE;
|
2019-11-12 01:36:56 +01:00
|
|
|
if (config_interlaced)
|
2004-07-14 10:55:31 +00:00
|
|
|
layer_mng_interlace_type = MNG_INTERLACE_ADAM7;
|
|
|
|
else
|
|
|
|
layer_mng_interlace_type = MNG_INTERLACE_NONE;
|
|
|
|
break;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
case CHUNKS_JNG:
|
|
|
|
layer_mng_compression_type = MNG_COMPRESSION_BASELINEJPEG;
|
2019-11-12 01:36:56 +01:00
|
|
|
if (config_interlaced)
|
2004-07-14 10:55:31 +00:00
|
|
|
layer_mng_interlace_type = MNG_INTERLACE_PROGRESSIVE;
|
|
|
|
else
|
|
|
|
layer_mng_interlace_type = MNG_INTERLACE_SEQUENTIAL;
|
|
|
|
break;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
default:
|
2008-03-25 22:57:06 +00:00
|
|
|
g_warning ("Huh? Programmer stupidity error "
|
|
|
|
"with 'layer_chunks_type'");
|
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2008-03-25 22:57:06 +00:00
|
|
|
if ((i == (num_layers - 1)) ||
|
2019-11-12 01:36:56 +01:00
|
|
|
(parse_disposal_type_from_layer_name (layer_name,
|
|
|
|
config_default_dispose) != DISPOSE_COMBINE))
|
2004-07-14 10:55:31 +00:00
|
|
|
frame_mode = MNG_FRAMINGMODE_3;
|
2003-03-30 22:06:46 +00:00
|
|
|
else
|
2004-07-14 10:55:31 +00:00
|
|
|
frame_mode = MNG_FRAMINGMODE_1;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
frame_delay = parse_ms_tag_from_layer_name (layer_name,
|
|
|
|
config_default_delay);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2008-03-25 22:57:06 +00:00
|
|
|
if (mng_putchunk_fram (handle, MNG_FALSE, frame_mode, 0, NULL,
|
|
|
|
MNG_CHANGEDELAY_DEFAULT,
|
|
|
|
MNG_CHANGETIMOUT_NO,
|
|
|
|
MNG_CHANGECLIPPING_DEFAULT,
|
|
|
|
MNG_CHANGESYNCID_NO,
|
|
|
|
frame_delay, 0, 0,
|
|
|
|
layer_offset_x,
|
|
|
|
layer_offset_x + layer_cols,
|
|
|
|
layer_offset_y,
|
|
|
|
layer_offset_y + layer_rows,
|
2008-03-29 02:30:58 +00:00
|
|
|
0, NULL) != MNG_NOERROR)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_fram() in mng_export_image()");
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2005-02-18 22:45:50 +00:00
|
|
|
if ((layer_offset_x != 0) || (layer_offset_y != 0))
|
2008-03-25 22:57:06 +00:00
|
|
|
{
|
|
|
|
if (mng_putchunk_defi (handle, 0, 0, 1, 1, layer_offset_x,
|
|
|
|
layer_offset_y, 1, layer_offset_x,
|
|
|
|
layer_offset_x + layer_cols, layer_offset_y,
|
|
|
|
layer_offset_y + layer_rows) != MNG_NOERROR)
|
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_defi() in mng_export_image()");
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
|
|
|
}
|
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2019-09-11 22:32:44 +02:00
|
|
|
if ((temp_file = gimp_temp_file ("mng")) == NULL)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("gimp_temp_file() failed in mng_export_image()");
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-11-06 15:27:05 +00:00
|
|
|
|
2021-10-01 18:14:14 +02:00
|
|
|
if ((outfile = g_fopen (g_file_peek_path (temp_file), "wb")) == NULL)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2008-08-18 06:31:03 +00:00
|
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
|
|
_("Could not open '%s' for writing: %s"),
|
2019-09-11 22:32:44 +02:00
|
|
|
gimp_file_get_utf8_name (temp_file),
|
2008-08-18 06:31:03 +00:00
|
|
|
g_strerror (errno));
|
2019-09-11 22:32:44 +02:00
|
|
|
g_file_delete (temp_file, NULL, NULL);
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2011-04-26 15:00:07 +05:30
|
|
|
pp = png_create_write_struct (PNG_LIBPNG_VER_STRING,
|
2012-11-29 01:30:34 +01:00
|
|
|
NULL, NULL, NULL);
|
2019-11-12 01:36:56 +01:00
|
|
|
if (! pp)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to png_create_write_struct() in mng_export_image()");
|
2004-07-14 10:55:31 +00:00
|
|
|
fclose (outfile);
|
2019-09-11 22:32:44 +02:00
|
|
|
g_file_delete (temp_file, NULL, NULL);
|
2008-03-25 22:57:06 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2011-04-26 15:00:07 +05:30
|
|
|
info = png_create_info_struct (pp);
|
2019-11-12 01:36:56 +01:00
|
|
|
if (! info)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
|
|
|
g_warning
|
2024-04-13 15:10:25 +00:00
|
|
|
("Unable to png_create_info_struct() in mng_export_image()");
|
2011-04-26 15:00:07 +05:30
|
|
|
png_destroy_write_struct (&pp, NULL);
|
2004-07-14 10:55:31 +00:00
|
|
|
fclose (outfile);
|
2019-09-11 22:32:44 +02:00
|
|
|
g_file_delete (temp_file, NULL, NULL);
|
2008-03-26 01:44:23 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2011-04-26 15:18:45 +05:30
|
|
|
if (setjmp (png_jmpbuf (pp)) != 0)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("HRM saving PNG in mng_export_image()");
|
2011-04-26 15:00:07 +05:30
|
|
|
png_destroy_write_struct (&pp, &info);
|
2004-07-14 10:55:31 +00:00
|
|
|
fclose (outfile);
|
2019-09-11 22:32:44 +02:00
|
|
|
g_file_delete (temp_file, NULL, NULL);
|
2008-03-26 01:44:23 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2011-04-26 15:00:07 +05:30
|
|
|
png_init_io (pp, outfile);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2011-04-26 15:18:45 +05:30
|
|
|
bit_depth = 8;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
switch (layer_drawable_type)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
|
|
|
case GIMP_RGB_IMAGE:
|
2011-04-26 15:18:45 +05:30
|
|
|
color_type = PNG_COLOR_TYPE_RGB;
|
2004-07-14 10:55:31 +00:00
|
|
|
break;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
case GIMP_RGBA_IMAGE:
|
2011-04-26 15:18:45 +05:30
|
|
|
color_type = PNG_COLOR_TYPE_RGB_ALPHA;
|
2004-07-14 10:55:31 +00:00
|
|
|
break;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
case GIMP_GRAY_IMAGE:
|
2011-04-26 15:18:45 +05:30
|
|
|
color_type = PNG_COLOR_TYPE_GRAY;
|
2004-07-14 10:55:31 +00:00
|
|
|
break;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
case GIMP_GRAYA_IMAGE:
|
2011-04-26 15:18:45 +05:30
|
|
|
color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
|
2004-07-14 10:55:31 +00:00
|
|
|
break;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
case GIMP_INDEXED_IMAGE:
|
2011-04-26 15:18:45 +05:30
|
|
|
color_type = PNG_COLOR_TYPE_PALETTE;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2011-05-06 16:28:25 +05:30
|
|
|
mngg.has_plte = TRUE;
|
2019-11-12 01:36:56 +01:00
|
|
|
mngg.palette = (png_colorp)
|
2023-05-23 23:37:46 +02:00
|
|
|
gimp_image_get_colormap (image, NULL, &mngg.num_palette);
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2011-05-06 16:28:25 +05:30
|
|
|
bit_depth = get_bit_depth_for_palette (mngg.num_palette);
|
2004-07-14 10:55:31 +00:00
|
|
|
break;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
case GIMP_INDEXEDA_IMAGE:
|
2011-04-26 15:18:45 +05:30
|
|
|
color_type = PNG_COLOR_TYPE_PALETTE;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2004-07-14 12:18:58 +00:00
|
|
|
layer_has_unique_palette =
|
2011-04-26 15:00:07 +05:30
|
|
|
respin_cmap (pp, info, layer_remap,
|
2019-08-27 15:22:15 +02:00
|
|
|
image, layer_buffer,
|
2011-04-26 15:18:45 +05:30
|
|
|
&bit_depth);
|
2004-07-14 10:55:31 +00:00
|
|
|
break;
|
2019-11-12 01:36:56 +01:00
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
default:
|
|
|
|
g_warning ("This can't be!\n");
|
2011-04-26 15:00:07 +05:30
|
|
|
png_destroy_write_struct (&pp, &info);
|
2008-03-26 01:44:23 +00:00
|
|
|
fclose (outfile);
|
2019-09-11 22:32:44 +02:00
|
|
|
g_file_delete (temp_file, NULL, NULL);
|
2008-03-26 01:44:23 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2011-05-06 16:28:25 +05:30
|
|
|
/* Note: png_set_IHDR() must be called before any other
|
2019-11-12 01:36:56 +01:00
|
|
|
* png_set_*() functions.
|
|
|
|
*/
|
2011-04-26 15:18:45 +05:30
|
|
|
png_set_IHDR (pp, info, layer_cols, layer_rows,
|
2012-11-29 01:30:34 +01:00
|
|
|
bit_depth,
|
|
|
|
color_type,
|
2019-11-12 01:36:56 +01:00
|
|
|
config_interlaced ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
|
2012-11-29 01:30:34 +01:00
|
|
|
PNG_COMPRESSION_TYPE_BASE,
|
|
|
|
PNG_FILTER_TYPE_BASE);
|
2011-05-06 16:28:25 +05:30
|
|
|
|
|
|
|
if (mngg.has_trns)
|
|
|
|
{
|
|
|
|
png_set_tRNS (pp, info, mngg.trans, mngg.num_trans, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mngg.has_plte)
|
|
|
|
{
|
|
|
|
png_set_PLTE (pp, info, mngg.palette, mngg.num_palette);
|
|
|
|
}
|
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
png_set_compression_level (pp, config_png_compression);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2011-04-26 15:00:07 +05:30
|
|
|
png_write_info (pp, info);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
if (config_interlaced)
|
2011-04-26 15:00:07 +05:30
|
|
|
num_passes = png_set_interlace_handling (pp);
|
2003-03-30 22:06:46 +00:00
|
|
|
else
|
2004-07-14 10:55:31 +00:00
|
|
|
num_passes = 1;
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2011-04-26 15:18:45 +05:30
|
|
|
if ((color_type == PNG_COLOR_TYPE_PALETTE) &&
|
|
|
|
(bit_depth < 8))
|
2011-04-26 15:00:07 +05:30
|
|
|
png_set_packing (pp);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
tile_height = gimp_tile_height ();
|
|
|
|
layer_pixel = g_new (guchar, tile_height * layer_cols * layer_bpp);
|
|
|
|
layer_pixels = g_new (guchar *, tile_height);
|
|
|
|
|
|
|
|
for (j = 0; j < tile_height; j++)
|
2004-07-14 10:55:31 +00:00
|
|
|
layer_pixels[j] = layer_pixel + (layer_cols * layer_bpp * j);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
for (pass = 0; pass < num_passes; pass++)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2008-03-26 01:44:23 +00:00
|
|
|
for (begin = 0, end = tile_height;
|
|
|
|
begin < layer_rows;
|
2004-07-14 10:55:31 +00:00
|
|
|
begin += tile_height, end += tile_height)
|
|
|
|
{
|
|
|
|
if (end > layer_rows)
|
|
|
|
end = layer_rows;
|
|
|
|
|
|
|
|
num = end - begin;
|
2012-11-29 01:30:34 +01:00
|
|
|
|
|
|
|
gegl_buffer_get (layer_buffer,
|
|
|
|
GEGL_RECTANGLE (0, begin, layer_cols, num), 1.0,
|
|
|
|
layer_format, layer_pixel,
|
|
|
|
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
2004-07-14 10:55:31 +00:00
|
|
|
|
2011-04-26 15:18:45 +05:30
|
|
|
if (png_get_valid (pp, info, PNG_INFO_tRNS))
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
|
|
|
for (j = 0; j < num; j++)
|
|
|
|
{
|
|
|
|
fixed = layer_pixels[j];
|
|
|
|
|
|
|
|
for (k = 0; k < layer_cols; k++)
|
2008-03-24 22:23:43 +00:00
|
|
|
fixed[k] = (fixed[k * 2 + 1] > 127) ?
|
|
|
|
layer_remap[fixed[k * 2]] : 0;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
2012-11-29 01:30:34 +01:00
|
|
|
else if (png_get_valid (pp, info, PNG_INFO_PLTE)
|
|
|
|
&& (layer_bpp == 2))
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
|
|
|
for (j = 0; j < num; j++)
|
|
|
|
{
|
|
|
|
fixed = layer_pixels[j];
|
|
|
|
|
|
|
|
for (k = 0; k < layer_cols; k++)
|
|
|
|
fixed[k] = fixed[k * 2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-26 15:00:07 +05:30
|
|
|
png_write_rows (pp, layer_pixels, num);
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2012-11-29 01:30:34 +01:00
|
|
|
g_object_unref (layer_buffer);
|
|
|
|
|
2011-04-26 15:00:07 +05:30
|
|
|
png_write_end (pp, info);
|
|
|
|
png_destroy_write_struct (&pp, &info);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
g_free (layer_pixels);
|
|
|
|
g_free (layer_pixel);
|
|
|
|
|
|
|
|
fclose (outfile);
|
|
|
|
|
2021-10-01 18:14:14 +02:00
|
|
|
infile = g_fopen (g_file_peek_path (temp_file), "rb");
|
2019-09-11 22:32:44 +02:00
|
|
|
|
|
|
|
if (! infile)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2008-08-18 06:31:03 +00:00
|
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
|
|
_("Could not open '%s' for reading: %s"),
|
2019-09-11 22:32:44 +02:00
|
|
|
gimp_file_get_utf8_name (temp_file),
|
2008-08-18 06:31:03 +00:00
|
|
|
g_strerror (errno));
|
2019-09-11 22:32:44 +02:00
|
|
|
g_file_delete (temp_file, NULL, NULL);
|
2008-03-26 01:44:23 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
fseek (infile, 8L, SEEK_SET);
|
|
|
|
|
|
|
|
while (!feof (infile))
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2005-07-19 20:52:12 +00:00
|
|
|
guchar chunksize_chars[4];
|
|
|
|
gulong chunksize;
|
|
|
|
gchar chunkname[5];
|
|
|
|
guchar *chunkbuffer;
|
|
|
|
glong chunkwidth;
|
|
|
|
glong chunkheight;
|
|
|
|
gchar chunkbitdepth;
|
|
|
|
gchar chunkcolortype;
|
|
|
|
gchar chunkcompression;
|
|
|
|
gchar chunkfilter;
|
|
|
|
gchar chunkinterlaced;
|
2004-07-15 06:57:46 +00:00
|
|
|
|
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
if (fread (chunksize_chars, 1, 4, infile) != 4)
|
|
|
|
break;
|
2005-07-19 20:52:12 +00:00
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
if (fread (chunkname, 1, 4, infile) != 4)
|
|
|
|
break;
|
2005-07-19 20:52:12 +00:00
|
|
|
|
2004-07-14 10:55:31 +00:00
|
|
|
chunkname[4] = 0;
|
|
|
|
|
2008-03-26 01:44:23 +00:00
|
|
|
chunksize = ((chunksize_chars[0] << 24) |
|
|
|
|
(chunksize_chars[1] << 16) |
|
|
|
|
(chunksize_chars[2] << 8) |
|
|
|
|
(chunksize_chars[3]));
|
2004-07-14 10:55:31 +00:00
|
|
|
|
|
|
|
chunkbuffer = NULL;
|
|
|
|
|
|
|
|
if (chunksize > 0)
|
|
|
|
{
|
|
|
|
chunkbuffer = g_new (guchar, chunksize);
|
|
|
|
|
|
|
|
if (fread (chunkbuffer, 1, chunksize, infile) != chunksize)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp (chunkname, "IHDR", 4) == 0)
|
|
|
|
{
|
2008-03-26 01:44:23 +00:00
|
|
|
chunkwidth = ((chunkbuffer[0] << 24) |
|
|
|
|
(chunkbuffer[1] << 16) |
|
|
|
|
(chunkbuffer[2] << 8) |
|
|
|
|
(chunkbuffer[3]));
|
2004-07-14 10:55:31 +00:00
|
|
|
|
2008-03-26 01:44:23 +00:00
|
|
|
chunkheight = ((chunkbuffer[4] << 24) |
|
|
|
|
(chunkbuffer[5] << 16) |
|
|
|
|
(chunkbuffer[6] << 8) |
|
|
|
|
(chunkbuffer[7]));
|
2004-07-14 10:55:31 +00:00
|
|
|
|
|
|
|
chunkbitdepth = chunkbuffer[8];
|
|
|
|
chunkcolortype = chunkbuffer[9];
|
|
|
|
chunkcompression = chunkbuffer[10];
|
|
|
|
chunkfilter = chunkbuffer[11];
|
|
|
|
chunkinterlaced = chunkbuffer[12];
|
|
|
|
|
2008-03-26 01:44:23 +00:00
|
|
|
if (mng_putchunk_ihdr (handle, chunkwidth, chunkheight,
|
|
|
|
chunkbitdepth, chunkcolortype,
|
|
|
|
chunkcompression, chunkfilter,
|
|
|
|
chunkinterlaced) != MNG_NOERROR)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2008-03-26 01:44:23 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_ihdr() "
|
2024-04-13 15:10:25 +00:00
|
|
|
"in mng_export_image()");
|
2019-10-19 21:53:25 +00:00
|
|
|
fclose (infile);
|
2008-03-26 01:44:23 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strncmp (chunkname, "IDAT", 4) == 0)
|
|
|
|
{
|
2008-03-26 01:44:23 +00:00
|
|
|
if (mng_putchunk_idat (handle, chunksize,
|
|
|
|
chunkbuffer) != MNG_NOERROR)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2008-03-26 01:44:23 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_idat() "
|
2024-04-13 15:10:25 +00:00
|
|
|
"in mng_export_image()");
|
2019-10-19 21:53:25 +00:00
|
|
|
fclose (infile);
|
2008-03-26 01:44:23 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strncmp (chunkname, "IEND", 4) == 0)
|
|
|
|
{
|
2008-03-26 01:44:23 +00:00
|
|
|
if (mng_putchunk_iend (handle) != MNG_NOERROR)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2008-03-26 01:44:23 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_iend() "
|
2024-04-13 15:10:25 +00:00
|
|
|
"in mng_export_image()");
|
2019-10-19 21:53:25 +00:00
|
|
|
fclose (infile);
|
2008-03-26 01:44:23 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strncmp (chunkname, "PLTE", 4) == 0)
|
|
|
|
{
|
2008-03-26 01:44:23 +00:00
|
|
|
/* If this frame's palette is the same as the global palette,
|
|
|
|
* write a 0-color palette chunk.
|
|
|
|
*/
|
2009-07-20 22:55:43 +02:00
|
|
|
if (mng_putchunk_plte_wrapper (handle,
|
|
|
|
(layer_has_unique_palette ?
|
|
|
|
(chunksize / 3) : 0),
|
|
|
|
chunkbuffer) != MNG_NOERROR)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2008-03-26 01:44:23 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_plte() "
|
2024-04-13 15:10:25 +00:00
|
|
|
"in mng_export_image()");
|
2019-10-19 21:53:25 +00:00
|
|
|
fclose (infile);
|
2008-03-26 01:44:23 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strncmp (chunkname, "tRNS", 4) == 0)
|
|
|
|
{
|
2009-07-20 22:55:43 +02:00
|
|
|
if (mng_putchunk_trns_wrapper (handle,
|
|
|
|
chunksize,
|
|
|
|
chunkbuffer) != MNG_NOERROR)
|
2004-07-14 10:55:31 +00:00
|
|
|
{
|
2008-03-26 01:44:23 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_trns() "
|
2024-04-13 15:10:25 +00:00
|
|
|
"in mng_export_image()");
|
2019-10-19 21:53:25 +00:00
|
|
|
fclose (infile);
|
2008-03-26 01:44:23 +00:00
|
|
|
goto err3;
|
2004-07-14 10:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chunksize > 0)
|
|
|
|
g_free (chunkbuffer);
|
|
|
|
|
|
|
|
/* read 4 bytes after the chunk */
|
|
|
|
|
|
|
|
fread (chunkname, 1, 4, infile);
|
|
|
|
}
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
fclose (infile);
|
2019-09-11 22:32:44 +02:00
|
|
|
g_file_delete (temp_file, NULL, NULL);
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2008-03-26 01:44:23 +00:00
|
|
|
if (mng_putchunk_mend (handle) != MNG_NOERROR)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_putchunk_mend() in mng_export_image()");
|
2008-03-26 01:44:23 +00:00
|
|
|
goto err3;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2008-03-26 01:44:23 +00:00
|
|
|
if (mng_write (handle) != MNG_NOERROR)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2024-04-13 15:10:25 +00:00
|
|
|
g_warning ("Unable to mng_write() the image in mng_export_image()");
|
2008-03-26 01:44:23 +00:00
|
|
|
goto err3;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2008-03-26 01:44:23 +00:00
|
|
|
ret = TRUE;
|
2008-03-25 22:25:47 +00:00
|
|
|
|
|
|
|
err3:
|
2003-03-30 22:06:46 +00:00
|
|
|
mng_cleanup (&handle);
|
2008-03-25 22:25:47 +00:00
|
|
|
err2:
|
2003-03-30 22:06:46 +00:00
|
|
|
fclose (userdata->fp);
|
2008-03-25 22:25:47 +00:00
|
|
|
err:
|
2003-03-30 22:06:46 +00:00
|
|
|
g_free (userdata);
|
|
|
|
|
2008-03-26 01:44:23 +00:00
|
|
|
return ret;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* The interactive dialog. */
|
|
|
|
|
2008-08-18 06:31:03 +00:00
|
|
|
static gboolean
|
2019-11-12 01:36:56 +01:00
|
|
|
mng_save_dialog (GimpImage *image,
|
|
|
|
GimpProcedure *procedure,
|
|
|
|
GObject *config)
|
2003-03-30 22:06:46 +00:00
|
|
|
{
|
2019-11-12 01:36:56 +01:00
|
|
|
GtkWidget *dialog;
|
|
|
|
GtkWidget *frame;
|
|
|
|
GtkWidget *hbox;
|
|
|
|
GtkListStore *store;
|
|
|
|
GtkWidget *combo;
|
|
|
|
GtkWidget *label;
|
|
|
|
GtkWidget *scale;
|
|
|
|
gint num_layers;
|
|
|
|
gboolean run;
|
|
|
|
|
2024-04-20 03:08:57 +00:00
|
|
|
dialog = gimp_export_procedure_dialog_new (GIMP_EXPORT_PROCEDURE (procedure),
|
|
|
|
GIMP_PROCEDURE_CONFIG (config),
|
|
|
|
image);
|
2005-09-09 18:38:00 +00:00
|
|
|
|
2023-04-15 23:13:04 +00:00
|
|
|
gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"options-vbox", "interlaced",
|
|
|
|
"bkgd", "gama", "phys", "time",
|
|
|
|
NULL);
|
|
|
|
gimp_procedure_dialog_get_label (GIMP_PROCEDURE_DIALOG (dialog),
|
2023-07-18 00:06:26 +02:00
|
|
|
"options-label", _("MNG Options"),
|
|
|
|
FALSE, FALSE);
|
2023-04-15 23:13:04 +00:00
|
|
|
gimp_procedure_dialog_fill_frame (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"options-frame", "options-label",
|
|
|
|
FALSE, "options-vbox");
|
2003-11-06 15:27:05 +00:00
|
|
|
|
2019-08-27 15:22:15 +02:00
|
|
|
g_free (gimp_image_get_layers (image, &num_layers));
|
2003-03-30 22:06:46 +00:00
|
|
|
|
|
|
|
if (num_layers == 1)
|
2019-11-12 01:36:56 +01:00
|
|
|
store = gimp_int_store_new (_("PNG"), CHUNKS_PNG_D,
|
|
|
|
_("JNG"), CHUNKS_JNG_D,
|
|
|
|
NULL);
|
2003-03-30 22:06:46 +00:00
|
|
|
else
|
2019-11-12 01:36:56 +01:00
|
|
|
store = gimp_int_store_new (_("PNG + delta PNG"), CHUNKS_PNG_D,
|
|
|
|
_("JNG + delta PNG"), CHUNKS_JNG_D,
|
|
|
|
_("All PNG"), CHUNKS_PNG,
|
|
|
|
_("All JNG"), CHUNKS_JNG,
|
|
|
|
NULL);
|
2003-11-14 18:05:39 +00:00
|
|
|
|
2023-04-15 23:13:04 +00:00
|
|
|
combo = gimp_procedure_dialog_get_int_combo (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"default-chunks",
|
|
|
|
GIMP_INT_STORE (store));
|
|
|
|
gtk_widget_set_margin_bottom (combo, 6);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
store = gimp_int_store_new (_("Combine"), DISPOSE_COMBINE,
|
|
|
|
_("Replace"), DISPOSE_REPLACE,
|
|
|
|
NULL);
|
2023-04-15 23:13:04 +00:00
|
|
|
combo = gimp_procedure_dialog_get_int_combo (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"default-dispose",
|
|
|
|
GIMP_INT_STORE (store));
|
|
|
|
gtk_widget_set_margin_bottom (combo, 6);
|
|
|
|
|
|
|
|
scale = gimp_procedure_dialog_get_scale_entry (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"png-compression", 1.0);
|
|
|
|
gtk_widget_set_margin_bottom (scale, 6);
|
|
|
|
|
|
|
|
scale = gimp_procedure_dialog_get_scale_entry (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"jpeg-quality", 1.0);
|
|
|
|
gtk_widget_set_margin_bottom (scale, 6);
|
|
|
|
|
|
|
|
scale = gimp_procedure_dialog_get_scale_entry (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"jpeg-smoothing", 1.0);
|
|
|
|
gtk_widget_set_margin_bottom (scale, 6);
|
|
|
|
|
|
|
|
/* MNG Animation Options */
|
|
|
|
label = gimp_procedure_dialog_get_label (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"milliseconds-label",
|
2023-07-18 00:06:26 +02:00
|
|
|
_("milliseconds"), FALSE, FALSE);
|
2023-04-15 23:13:04 +00:00
|
|
|
gtk_widget_set_margin_start (label, 6);
|
|
|
|
hbox = gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"animation-box", "default-delay",
|
|
|
|
"milliseconds-label", NULL);
|
|
|
|
gtk_orientable_set_orientation (GTK_ORIENTABLE (hbox),
|
|
|
|
GTK_ORIENTATION_HORIZONTAL);
|
|
|
|
|
|
|
|
gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"mng-animation-box", "loop",
|
|
|
|
"animation-box", NULL);
|
|
|
|
gimp_procedure_dialog_get_label (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"mng-animation-label",
|
2023-07-18 00:06:26 +02:00
|
|
|
_("Animated MNG Options"), FALSE, FALSE);
|
2023-04-15 23:13:04 +00:00
|
|
|
frame = gimp_procedure_dialog_fill_frame (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"mng-animation-frame",
|
|
|
|
"mng-animation-label", FALSE,
|
|
|
|
"mng-animation-box");
|
2003-11-06 15:27:05 +00:00
|
|
|
|
2010-02-16 08:45:57 +01:00
|
|
|
if (num_layers <= 1)
|
|
|
|
{
|
|
|
|
gtk_widget_set_sensitive (frame, FALSE);
|
|
|
|
gimp_help_set_help_data (frame,
|
|
|
|
_("These options are only available when "
|
|
|
|
"the exported image has more than one "
|
|
|
|
"layer. The image you are exporting only has "
|
|
|
|
"one layer."),
|
|
|
|
NULL);
|
|
|
|
}
|
2003-11-06 15:27:05 +00:00
|
|
|
|
2023-04-15 23:13:04 +00:00
|
|
|
gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"options-frame", "default-chunks",
|
|
|
|
"default-dispose", "png-compression",
|
|
|
|
"jpeg-quality", "jpeg-smoothing",
|
|
|
|
"mng-animation-frame", NULL);
|
|
|
|
|
2005-09-09 18:38:00 +00:00
|
|
|
gtk_widget_show (dialog);
|
2003-03-30 22:06:46 +00:00
|
|
|
|
2019-11-12 01:36:56 +01:00
|
|
|
run = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog));
|
2003-11-06 15:27:05 +00:00
|
|
|
|
2005-09-09 18:38:00 +00:00
|
|
|
gtk_widget_destroy (dialog);
|
2003-11-06 15:27:05 +00:00
|
|
|
|
|
|
|
return run;
|
2003-03-30 22:06:46 +00:00
|
|
|
}
|