2006-12-09 22:12:23 +00:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
2005-05-31 19:10:46 +00:00
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
2006-12-09 22:12:23 +00:00
|
|
|
* cel.c -- KISS CEL file format plug-in
|
|
|
|
* (copyright) 1997,1998 Nick Lamb (njl195@zepler.org.uk)
|
|
|
|
*
|
2009-01-17 22:28:01 +00:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
2005-05-31 19:10: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
|
2005-05-31 19:10:46 +00:00
|
|
|
* (at your option) any later version.
|
1998-04-26 09:35:56 +00:00
|
|
|
*
|
2005-05-31 19:10:46 +00:00
|
|
|
* 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/>.
|
1997-11-24 22:05:25 +00:00
|
|
|
*/
|
2005-03-04 15:12:29 +00:00
|
|
|
|
1999-05-29 01:28:24 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2003-06-13 14:37:00 +00:00
|
|
|
#include <errno.h>
|
1997-11-24 22:05:25 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
2005-03-04 15:12:29 +00:00
|
|
|
#include <glib/gstdio.h>
|
2000-01-06 16:40:17 +00:00
|
|
|
|
1997-11-24 22:05:25 +00:00
|
|
|
#include <libgimp/gimp.h>
|
2000-01-06 16:40:17 +00:00
|
|
|
#include <libgimp/gimpui.h>
|
2000-01-17 17:02:26 +00:00
|
|
|
|
1999-12-17 21:24:24 +00:00
|
|
|
#include "libgimp/stdplugins-intl.h"
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2004-05-14 16:54:33 +00:00
|
|
|
|
2005-08-13 18:29:14 +00:00
|
|
|
#define LOAD_PROC "file-cel-load"
|
2024-04-13 15:10:25 +00:00
|
|
|
#define EXPORT_PROC "file-cel-export"
|
2008-08-11 10:06:13 +00:00
|
|
|
#define PLUG_IN_BINARY "file-cel"
|
2011-04-08 20:31:34 +02:00
|
|
|
#define PLUG_IN_ROLE "gimp-file-cel"
|
2005-08-13 18:29:14 +00:00
|
|
|
|
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
typedef struct _Cel Cel;
|
|
|
|
typedef struct _CelClass CelClass;
|
|
|
|
|
|
|
|
struct _Cel
|
|
|
|
{
|
|
|
|
GimpPlugIn parent_instance;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _CelClass
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
2019-08-24 17:04:38 +02:00
|
|
|
GimpPlugInClass parent_class;
|
1997-11-24 22:05:25 +00:00
|
|
|
};
|
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
|
|
|
|
#define CEL_TYPE (cel_get_type ())
|
2023-10-18 18:29:37 +02:00
|
|
|
#define CEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CEL_TYPE, Cel))
|
2019-08-24 17:04:38 +02:00
|
|
|
|
|
|
|
GType cel_get_type (void) G_GNUC_CONST;
|
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
static GList * cel_query_procedures (GimpPlugIn *plug_in);
|
|
|
|
static GimpProcedure * cel_create_procedure (GimpPlugIn *plug_in,
|
|
|
|
const gchar *name);
|
|
|
|
|
|
|
|
static GimpValueArray * cel_load (GimpProcedure *procedure,
|
|
|
|
GimpRunMode run_mode,
|
|
|
|
GFile *file,
|
|
|
|
GimpMetadata *metadata,
|
|
|
|
GimpMetadataLoadFlags *flags,
|
|
|
|
GimpProcedureConfig *config,
|
|
|
|
gpointer run_data);
|
2024-04-13 15:10:25 +00:00
|
|
|
static GimpValueArray * cel_export (GimpProcedure *procedure,
|
2023-08-06 02:33:00 +02:00
|
|
|
GimpRunMode run_mode,
|
|
|
|
GimpImage *image,
|
|
|
|
GFile *file,
|
2024-05-06 18:38:12 +00:00
|
|
|
GimpExportOptions *options,
|
2023-08-06 02:33:00 +02:00
|
|
|
GimpMetadata *metadata,
|
|
|
|
GimpProcedureConfig *config,
|
|
|
|
gpointer run_data);
|
|
|
|
|
|
|
|
static gint load_palette (GFile *file,
|
|
|
|
guchar palette[],
|
|
|
|
GError **error);
|
|
|
|
static GimpImage * load_image (GFile *file,
|
|
|
|
GFile *palette_file,
|
|
|
|
GError **error);
|
2024-04-13 15:10:25 +00:00
|
|
|
static gboolean export_image (GFile *file,
|
2023-08-06 02:33:00 +02:00
|
|
|
GimpImage *image,
|
|
|
|
GimpDrawable *drawable,
|
|
|
|
GError **error);
|
|
|
|
static gboolean palette_dialog (const gchar *title,
|
|
|
|
GimpProcedure *procedure,
|
|
|
|
GimpProcedureConfig *config);
|
|
|
|
static gboolean need_palette (GFile *file,
|
|
|
|
GError **error);
|
2019-08-24 17:04:38 +02:00
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (Cel, cel, GIMP_TYPE_PLUG_IN)
|
|
|
|
|
|
|
|
GIMP_MAIN (CEL_TYPE)
|
2022-05-26 00:59:36 +02:00
|
|
|
DEFINE_STD_SET_I18N
|
2019-08-24 17:04:38 +02:00
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
cel_class_init (CelClass *klass)
|
|
|
|
{
|
|
|
|
GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
plug_in_class->query_procedures = cel_query_procedures;
|
|
|
|
plug_in_class->create_procedure = cel_create_procedure;
|
2022-05-26 00:59:36 +02:00
|
|
|
plug_in_class->set_i18n = STD_SET_I18N;
|
2019-08-24 17:04:38 +02:00
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
static void
|
2019-08-24 17:04:38 +02:00
|
|
|
cel_init (Cel *cel)
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
static GList *
|
|
|
|
cel_query_procedures (GimpPlugIn *plug_in)
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
2019-08-24 17:04:38 +02:00
|
|
|
GList *list = NULL;
|
1998-04-26 09:35:56 +00:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
list = g_list_append (list, g_strdup (LOAD_PROC));
|
2024-04-13 15:10:25 +00:00
|
|
|
list = g_list_append (list, g_strdup (EXPORT_PROC));
|
2012-11-19 18:43:35 +01:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
return list;
|
|
|
|
}
|
2003-03-25 16:38:19 +00:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
static GimpProcedure *
|
|
|
|
cel_create_procedure (GimpPlugIn *plug_in,
|
|
|
|
const gchar *name)
|
|
|
|
{
|
|
|
|
GimpProcedure *procedure = NULL;
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
if (! strcmp (name, LOAD_PROC))
|
|
|
|
{
|
2023-08-06 03:21:27 +02:00
|
|
|
procedure = gimp_load_procedure_new (plug_in, name,
|
|
|
|
GIMP_PDB_PROC_TYPE_PLUGIN,
|
|
|
|
cel_load, NULL, NULL);
|
2019-08-24 17:04:38 +02:00
|
|
|
|
2022-07-04 22:50:53 +02:00
|
|
|
gimp_procedure_set_menu_label (procedure, _("KISS CEL"));
|
2019-08-24 17:04:38 +02:00
|
|
|
|
|
|
|
gimp_procedure_set_documentation (procedure,
|
|
|
|
"Loads files in KISS CEL file format",
|
|
|
|
"This plug-in loads individual KISS "
|
|
|
|
"cell files.",
|
|
|
|
name);
|
|
|
|
gimp_procedure_set_attribution (procedure,
|
|
|
|
"Nick Lamb",
|
|
|
|
"Nick Lamb <njl195@zepler.org.uk>",
|
|
|
|
"May 1998");
|
|
|
|
|
|
|
|
gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
"cel");
|
|
|
|
gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
"0,string,KiSS\\040");
|
|
|
|
|
2024-06-12 16:53:12 +00:00
|
|
|
gimp_procedure_add_file_argument (procedure, "palette-file",
|
|
|
|
_("_Palette file"),
|
|
|
|
_("KCF file to load palette from"),
|
|
|
|
G_PARAM_READWRITE);
|
2019-08-24 17:04:38 +02:00
|
|
|
}
|
2024-04-13 15:10:25 +00:00
|
|
|
else if (! strcmp (name, EXPORT_PROC))
|
2019-08-24 17:04:38 +02:00
|
|
|
{
|
2024-04-20 03:08:57 +00:00
|
|
|
procedure = gimp_export_procedure_new (plug_in, name,
|
|
|
|
GIMP_PDB_PROC_TYPE_PLUGIN,
|
|
|
|
FALSE, cel_export, NULL, NULL);
|
2019-08-24 17:04:38 +02:00
|
|
|
|
|
|
|
gimp_procedure_set_image_types (procedure, "RGB*, INDEXED*");
|
|
|
|
|
2022-07-04 22:50:53 +02:00
|
|
|
gimp_procedure_set_menu_label (procedure, _("KISS CEL"));
|
2019-08-24 17:04:38 +02:00
|
|
|
|
|
|
|
gimp_procedure_set_documentation (procedure,
|
|
|
|
"Exports files in KISS CEL file format",
|
|
|
|
"This plug-in exports individual KISS "
|
|
|
|
"cell files.",
|
|
|
|
name);
|
|
|
|
gimp_procedure_set_attribution (procedure,
|
|
|
|
"Nick Lamb",
|
|
|
|
"Nick Lamb <njl195@zepler.org.uk>",
|
|
|
|
"May 1998");
|
|
|
|
|
|
|
|
gimp_file_procedure_set_handles_remote (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
TRUE);
|
|
|
|
gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
"cel");
|
|
|
|
|
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_ALPHA |
|
|
|
|
GIMP_EXPORT_CAN_HANDLE_INDEXED,
|
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_file_argument (procedure, "palette-file",
|
|
|
|
_("_Palette file"),
|
|
|
|
_("File to save palette to"),
|
|
|
|
G_PARAM_READWRITE);
|
2019-08-24 17:04:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return procedure;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GimpValueArray *
|
2023-08-06 02:33:00 +02:00
|
|
|
cel_load (GimpProcedure *procedure,
|
|
|
|
GimpRunMode run_mode,
|
|
|
|
GFile *file,
|
|
|
|
GimpMetadata *metadata,
|
|
|
|
GimpMetadataLoadFlags *flags,
|
|
|
|
GimpProcedureConfig *config,
|
|
|
|
gpointer run_data)
|
2019-08-24 17:04:38 +02:00
|
|
|
{
|
|
|
|
GimpValueArray *return_vals;
|
|
|
|
GimpImage *image = NULL;
|
|
|
|
gboolean needs_palette = FALSE;
|
2023-08-06 02:33:00 +02:00
|
|
|
GFile *palette_file = NULL;
|
2019-08-24 17:04:38 +02:00
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
gegl_init (NULL, NULL);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
needs_palette = need_palette (file, &error);
|
|
|
|
if (error != NULL)
|
|
|
|
return gimp_procedure_new_return_values (procedure,
|
|
|
|
GIMP_PDB_EXECUTION_ERROR,
|
|
|
|
error);
|
2000-01-17 17:02:26 +00:00
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
g_object_get (config, "palette-file", &palette_file, NULL);
|
|
|
|
switch (run_mode)
|
2019-08-24 17:04:38 +02:00
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
case GIMP_RUN_INTERACTIVE:
|
|
|
|
if (needs_palette)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
/* Let user choose KCF palette (cancel ignores) */
|
|
|
|
if (! palette_dialog (_("Load KISS Palette"), procedure, config))
|
|
|
|
return gimp_procedure_new_return_values (procedure,
|
|
|
|
GIMP_PDB_CANCEL,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
g_clear_object (&palette_file);
|
|
|
|
g_object_get (config, "palette-file", &palette_file, NULL);
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
2023-08-06 02:33:00 +02:00
|
|
|
break;
|
|
|
|
case GIMP_RUN_WITH_LAST_VALS:
|
|
|
|
if (! needs_palette)
|
|
|
|
/* It is possible to have a palette file from a previous call. Just ignore
|
|
|
|
* it if it's unneeded, yet keep it stored.
|
|
|
|
*/
|
|
|
|
g_clear_object (&palette_file);
|
|
|
|
break;
|
|
|
|
case GIMP_RUN_NONINTERACTIVE:
|
|
|
|
/* Note: we don't forbid setting a palette file when unneeded as a way to
|
|
|
|
* override the embedded palette in scripts.
|
|
|
|
*/
|
|
|
|
break;
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
2019-08-24 17:04:38 +02:00
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
if (needs_palette && palette_file == NULL)
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
g_set_error (&error, G_FILE_ERROR, 0,
|
|
|
|
_("This KISS CEL image requires a palette file."));
|
|
|
|
|
|
|
|
return gimp_procedure_new_return_values (procedure,
|
|
|
|
GIMP_PDB_CALLING_ERROR,
|
|
|
|
error);
|
2019-08-24 17:04:38 +02:00
|
|
|
}
|
2004-01-20 17:10:16 +00:00
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
image = load_image (file, palette_file, &error);
|
|
|
|
g_clear_object (&palette_file);
|
2019-08-24 17:04:38 +02:00
|
|
|
if (! image)
|
|
|
|
return gimp_procedure_new_return_values (procedure,
|
|
|
|
GIMP_PDB_EXECUTION_ERROR,
|
|
|
|
error);
|
2013-11-10 00:18:48 +01:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
return_vals = gimp_procedure_new_return_values (procedure,
|
|
|
|
GIMP_PDB_SUCCESS,
|
|
|
|
NULL);
|
2013-11-10 00:18:48 +01:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
GIMP_VALUES_SET_IMAGE (return_vals, 1, image);
|
2004-01-11 20:10:14 +00:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
return return_vals;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GimpValueArray *
|
2024-04-13 15:10:25 +00:00
|
|
|
cel_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-24 17:04:38 +02:00
|
|
|
{
|
2023-07-21 18:08:00 +02:00
|
|
|
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
|
2024-04-30 04:25:51 +00:00
|
|
|
GimpExportReturn export = GIMP_EXPORT_IGNORE;
|
2024-04-30 13:50:24 +00:00
|
|
|
GList *drawables;
|
|
|
|
GError *error = NULL;
|
2019-08-24 17:04:38 +02:00
|
|
|
|
|
|
|
gegl_init (NULL, NULL);
|
|
|
|
|
2024-05-06 18:38:12 +00:00
|
|
|
export = gimp_export_options_get_image (options, &image);
|
2019-08-24 17:04:38 +02:00
|
|
|
|
2024-04-30 13:50:24 +00:00
|
|
|
drawables = gimp_image_list_layers (image);
|
2019-08-24 17:04:38 +02:00
|
|
|
|
2024-04-30 13:50:24 +00:00
|
|
|
if (! export_image (file, image, drawables->data, &error))
|
2023-08-06 02:33:00 +02:00
|
|
|
status = GIMP_PDB_EXECUTION_ERROR;
|
2000-01-25 17:46:56 +00:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
if (export == GIMP_EXPORT_EXPORT)
|
2024-04-30 13:50:24 +00:00
|
|
|
gimp_image_delete (image);
|
2008-08-20 14:00:44 +00:00
|
|
|
|
2024-04-30 13:50:24 +00:00
|
|
|
g_list_free (drawables);
|
2019-08-24 17:04:38 +02:00
|
|
|
return gimp_procedure_new_return_values (procedure, status, error);
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
|
|
|
|
2004-01-05 17:37:42 +00:00
|
|
|
/* Peek into the file to determine whether we need a palette */
|
|
|
|
static gboolean
|
2019-09-11 21:48:34 +02:00
|
|
|
need_palette (GFile *file,
|
|
|
|
GError **error)
|
2004-01-05 17:37:42 +00:00
|
|
|
{
|
2004-01-05 21:32:25 +00:00
|
|
|
FILE *fp;
|
2004-01-05 19:01:33 +00:00
|
|
|
guchar header[32];
|
2012-07-12 15:50:02 +02:00
|
|
|
size_t n_read;
|
2004-01-05 19:01:33 +00:00
|
|
|
|
2021-10-01 18:56:12 +02:00
|
|
|
fp = g_fopen (g_file_peek_path (file), "rb");
|
2019-09-11 21:48:34 +02:00
|
|
|
|
|
|
|
if (! fp)
|
2012-07-12 15:50:02 +02:00
|
|
|
{
|
|
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
|
|
_("Could not open '%s' for reading: %s"),
|
2019-09-11 21:48:34 +02:00
|
|
|
gimp_file_get_utf8_name (file), g_strerror (errno));
|
2012-07-12 15:50:02 +02:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
n_read = fread (header, 32, 1, fp);
|
2004-01-05 17:37:42 +00:00
|
|
|
|
2004-01-05 18:54:30 +00:00
|
|
|
fclose (fp);
|
2004-01-05 17:37:42 +00:00
|
|
|
|
2012-07-12 15:50:02 +02:00
|
|
|
if (n_read < 1)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("EOF or error while reading image header"));
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2004-01-05 19:01:33 +00:00
|
|
|
return (header[5] < 32);
|
2004-01-05 17:37:42 +00:00
|
|
|
}
|
|
|
|
|
2006-12-09 22:12:23 +00:00
|
|
|
/* Load CEL image into GIMP */
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
static GimpImage *
|
2019-09-11 21:48:34 +02:00
|
|
|
load_image (GFile *file,
|
2023-08-06 02:33:00 +02:00
|
|
|
GFile *palette_file,
|
2019-09-11 21:48:34 +02:00
|
|
|
GError **error)
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
FILE *fp; /* Read file pointer */
|
|
|
|
guchar header[32], /* File header */
|
|
|
|
file_mark, /* KiSS file type */
|
|
|
|
bpp; /* Bits per pixel */
|
|
|
|
gint height, width, /* Dimensions of image */
|
2018-04-18 20:57:03 +02:00
|
|
|
offx, offy, /* Layer offsets */
|
2023-08-06 02:33:00 +02:00
|
|
|
colors; /* Number of colors */
|
2012-11-19 18:43:35 +01:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
GimpImage *image; /* Image */
|
|
|
|
GimpLayer *layer; /* Layer */
|
2012-11-19 18:43:35 +01:00
|
|
|
guchar *buf; /* Temporary buffer */
|
|
|
|
guchar *line; /* Pixel data */
|
|
|
|
GeglBuffer *buffer; /* Buffer for layer */
|
|
|
|
|
|
|
|
gint i, j, k; /* Counters */
|
|
|
|
size_t n_read; /* Number of items read from file */
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2014-07-23 16:39:00 +02:00
|
|
|
gimp_progress_init_printf (_("Opening '%s'"),
|
2019-09-11 21:48:34 +02:00
|
|
|
gimp_file_get_utf8_name (file));
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
/* Open the file for reading */
|
2021-10-01 18:56:12 +02:00
|
|
|
fp = g_fopen (g_file_peek_path (file), "r");
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
if (fp == NULL)
|
|
|
|
{
|
2008-08-20 14:00:44 +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 21:48:34 +02:00
|
|
|
gimp_file_get_utf8_name (file), g_strerror (errno));
|
2019-08-24 17:04:38 +02:00
|
|
|
return NULL;
|
2000-01-17 17:02:26 +00:00
|
|
|
}
|
1998-04-26 09:35:56 +00:00
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
/* Get the image dimensions and create the image... */
|
|
|
|
|
2012-07-12 15:50:02 +02:00
|
|
|
n_read = fread (header, 4, 1, fp);
|
|
|
|
|
|
|
|
if (n_read < 1)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("EOF or error while reading image header"));
|
2018-08-23 10:05:34 +02:00
|
|
|
fclose (fp);
|
2019-08-24 17:04:38 +02:00
|
|
|
return NULL;
|
2012-07-12 15:50:02 +02:00
|
|
|
}
|
2000-01-17 17:02:26 +00:00
|
|
|
|
2005-08-03 01:15:36 +00:00
|
|
|
if (strncmp ((const gchar *) header, "KiSS", 4))
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
2013-06-06 23:26:16 +02:00
|
|
|
colors= 16;
|
2004-01-05 17:37:42 +00:00
|
|
|
bpp = 4;
|
2005-08-03 01:15:36 +00:00
|
|
|
width = header[0] + (256 * header[1]);
|
|
|
|
height = header[2] + (256 * header[3]);
|
2000-01-17 17:02:26 +00:00
|
|
|
offx= 0;
|
|
|
|
offy= 0;
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
2000-01-17 17:02:26 +00:00
|
|
|
else
|
|
|
|
{ /* New-style image file, read full header */
|
2012-07-12 15:50:02 +02:00
|
|
|
n_read = fread (header, 28, 1, fp);
|
|
|
|
|
|
|
|
if (n_read < 1)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("EOF or error while reading image header"));
|
2018-08-23 10:05:34 +02:00
|
|
|
fclose (fp);
|
2019-08-24 17:04:38 +02:00
|
|
|
return NULL;
|
2012-07-12 15:50:02 +02:00
|
|
|
}
|
|
|
|
|
2012-07-13 15:20:06 +02:00
|
|
|
file_mark = header[0];
|
|
|
|
if (file_mark != 0x20 && file_mark != 0x21)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("is not a CEL image file"));
|
2018-08-23 10:05:34 +02:00
|
|
|
fclose (fp);
|
2019-08-24 17:04:38 +02:00
|
|
|
return NULL;
|
2012-07-13 15:20:06 +02:00
|
|
|
}
|
|
|
|
|
2004-01-05 17:37:42 +00:00
|
|
|
bpp = header[1];
|
2012-07-13 15:20:06 +02:00
|
|
|
switch (bpp)
|
|
|
|
{
|
|
|
|
case 4:
|
|
|
|
case 8:
|
|
|
|
case 32:
|
2013-06-06 23:26:16 +02:00
|
|
|
colors = (1 << bpp);
|
2012-07-13 15:20:06 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("illegal bpp value in image: %hhu"), bpp);
|
2018-08-23 10:05:34 +02:00
|
|
|
fclose (fp);
|
2019-08-24 17:04:38 +02:00
|
|
|
return NULL;
|
2012-07-13 15:20:06 +02:00
|
|
|
}
|
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
width = header[4] + (256 * header[5]);
|
|
|
|
height = header[6] + (256 * header[7]);
|
|
|
|
offx = header[8] + (256 * header[9]);
|
|
|
|
offy = header[10] + (256 * header[11]);
|
|
|
|
}
|
|
|
|
|
2012-07-13 15:20:06 +02:00
|
|
|
if ((width == 0) || (height == 0) || (width + offx > GIMP_MAX_IMAGE_SIZE) ||
|
|
|
|
(height + offy > GIMP_MAX_IMAGE_SIZE))
|
|
|
|
{
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("illegal image dimensions: width: %d, horizontal offset: "
|
|
|
|
"%d, height: %d, vertical offset: %d"),
|
|
|
|
width, offx, height, offy);
|
2018-08-23 10:05:34 +02:00
|
|
|
fclose (fp);
|
2019-08-24 17:04:38 +02:00
|
|
|
return NULL;
|
2012-07-13 15:20:06 +02:00
|
|
|
}
|
|
|
|
|
2004-01-05 17:37:42 +00:00
|
|
|
if (bpp == 32)
|
|
|
|
image = gimp_image_new (width + offx, height + offy, GIMP_RGB);
|
|
|
|
else
|
|
|
|
image = gimp_image_new (width + offx, height + offy, GIMP_INDEXED);
|
2000-01-17 17:02:26 +00:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
if (! image)
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
2012-07-13 15:33:27 +02:00
|
|
|
g_set_error (error, 0, 0, _("Can't create a new image"));
|
2010-11-09 16:03:47 -02:00
|
|
|
fclose (fp);
|
2019-08-24 17:04:38 +02:00
|
|
|
return NULL;
|
2000-01-17 17:02:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Create an indexed-alpha layer to hold the image... */
|
2004-01-05 17:37:42 +00:00
|
|
|
if (bpp == 32)
|
|
|
|
layer = gimp_layer_new (image, _("Background"), width, height,
|
2017-01-08 23:00:19 +01:00
|
|
|
GIMP_RGBA_IMAGE,
|
2017-08-21 20:04:25 +02:00
|
|
|
100,
|
|
|
|
gimp_image_get_default_new_layer_mode (image));
|
2004-01-05 17:37:42 +00:00
|
|
|
else
|
|
|
|
layer = gimp_layer_new (image, _("Background"), width, height,
|
2017-01-08 23:00:19 +01:00
|
|
|
GIMP_INDEXEDA_IMAGE,
|
2017-08-21 20:04:25 +02:00
|
|
|
100,
|
|
|
|
gimp_image_get_default_new_layer_mode (image));
|
2019-08-24 17:04:38 +02:00
|
|
|
gimp_image_insert_layer (image, layer, NULL, 0);
|
2000-01-17 17:02:26 +00:00
|
|
|
gimp_layer_set_offsets (layer, offx, offy);
|
|
|
|
|
|
|
|
/* Get the drawable and set the pixel region for our load... */
|
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
|
2000-01-17 17:02:26 +00:00
|
|
|
|
2007-06-06 08:44:52 +00:00
|
|
|
/* Read the image in and give it to GIMP a line at a time */
|
2012-11-19 18:43:35 +01:00
|
|
|
buf = g_new (guchar, width * 4);
|
|
|
|
line = g_new (guchar, (width + 1) * 4);
|
2000-01-17 17:02:26 +00:00
|
|
|
|
|
|
|
for (i = 0; i < height && !feof(fp); ++i)
|
|
|
|
{
|
2004-01-05 17:37:42 +00:00
|
|
|
switch (bpp)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2004-01-05 17:37:42 +00:00
|
|
|
case 4:
|
2012-11-19 18:43:35 +01:00
|
|
|
n_read = fread (buf, (width + 1) / 2, 1, fp);
|
2012-07-12 15:50:02 +02:00
|
|
|
|
|
|
|
if (n_read < 1)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("EOF or error while reading image data"));
|
2018-08-23 10:05:34 +02:00
|
|
|
fclose (fp);
|
2019-08-24 17:04:38 +02:00
|
|
|
return NULL;
|
2012-07-12 15:50:02 +02:00
|
|
|
}
|
|
|
|
|
2012-11-19 18:43:35 +01:00
|
|
|
for (j = 0, k = 0; j < width * 2; j+= 4, ++k)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
if (buf[k] / 16 == 0)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
line[j] = 16;
|
|
|
|
line[j+ 1 ] = 0;
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
line[j] = (buf[k] / 16) - 1;
|
|
|
|
line[j + 1] = 255;
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
2012-11-19 18:43:35 +01:00
|
|
|
|
|
|
|
if (buf[k] % 16 == 0)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
line[j + 2] = 16;
|
|
|
|
line[j + 3] = 0;
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
line[j + 2] = (buf[k] % 16) - 1;
|
|
|
|
line[j + 3] = 255;
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-01-05 17:37:42 +00:00
|
|
|
case 8:
|
2012-11-19 18:43:35 +01:00
|
|
|
n_read = fread (buf, width, 1, fp);
|
2012-07-12 15:50:02 +02:00
|
|
|
|
|
|
|
if (n_read < 1)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("EOF or error while reading image data"));
|
2018-08-23 10:05:34 +02:00
|
|
|
fclose (fp);
|
2019-08-24 17:04:38 +02:00
|
|
|
return NULL;
|
2012-07-12 15:50:02 +02:00
|
|
|
}
|
|
|
|
|
2012-11-19 18:43:35 +01:00
|
|
|
for (j = 0, k = 0; j < width * 2; j+= 2, ++k)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
if (buf[k] == 0)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
line[j] = 255;
|
|
|
|
line[j + 1] = 0;
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
line[j] = buf[k] - 1;
|
|
|
|
line[j + 1] = 255;
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2004-01-05 18:54:30 +00:00
|
|
|
|
2004-01-05 17:37:42 +00:00
|
|
|
case 32:
|
2012-11-19 18:43:35 +01:00
|
|
|
n_read = fread (line, width * 4, 1, fp);
|
2012-07-12 15:50:02 +02:00
|
|
|
|
|
|
|
if (n_read < 1)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("EOF or error while reading image data"));
|
2018-08-23 10:05:34 +02:00
|
|
|
fclose (fp);
|
2019-08-24 17:04:38 +02:00
|
|
|
return NULL;
|
2012-07-12 15:50:02 +02:00
|
|
|
}
|
|
|
|
|
2004-01-05 17:37:42 +00:00
|
|
|
/* The CEL file order is BGR so we need to swap B and R
|
|
|
|
* to get the Gimp RGB order.
|
|
|
|
*/
|
|
|
|
for (j= 0; j < width; j++)
|
|
|
|
{
|
|
|
|
guint8 tmp = line[j*4];
|
|
|
|
line[j*4] = line[j*4+2];
|
|
|
|
line[j*4+2] = tmp;
|
|
|
|
}
|
|
|
|
break;
|
2003-12-23 22:07:06 +00:00
|
|
|
|
|
|
|
default:
|
2012-07-13 15:33:27 +02:00
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("Unsupported bit depth (%d)!"), bpp);
|
2018-08-23 10:05:34 +02:00
|
|
|
fclose (fp);
|
2019-08-24 17:04:38 +02:00
|
|
|
return NULL;
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
2003-11-15 13:53:33 +00:00
|
|
|
|
2012-11-19 18:43:35 +01:00
|
|
|
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i, width, 1), 0,
|
|
|
|
NULL, line, GEGL_AUTO_ROWSTRIDE);
|
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
gimp_progress_update ((float) i / (float) height);
|
1998-04-26 09:35:56 +00:00
|
|
|
}
|
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
/* Close image files, give back allocated memory */
|
1998-04-26 09:35:56 +00:00
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
fclose (fp);
|
2012-11-19 18:43:35 +01:00
|
|
|
g_free (buf);
|
2000-01-17 17:02:26 +00:00
|
|
|
g_free (line);
|
1998-04-26 09:35:56 +00:00
|
|
|
|
2004-01-05 17:37:42 +00:00
|
|
|
if (bpp != 32)
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
2004-01-05 17:37:42 +00:00
|
|
|
/* Use palette from file or otherwise default grey palette */
|
2012-11-19 18:43:35 +01:00
|
|
|
guchar palette[256 * 3];
|
2004-01-05 18:54:30 +00:00
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
if (palette_file != NULL)
|
2004-01-05 17:37:42 +00:00
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
colors = load_palette (palette_file, palette, error);
|
2013-06-06 23:26:16 +02:00
|
|
|
if (colors < 0 || *error)
|
2019-08-24 17:04:38 +02:00
|
|
|
return NULL;
|
2004-01-05 17:37:42 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-06-06 23:26:16 +02:00
|
|
|
for (i= 0; i < colors; ++i)
|
2004-01-05 17:37:42 +00:00
|
|
|
{
|
2013-06-06 23:26:16 +02:00
|
|
|
palette[i * 3] = palette[i * 3 + 1] = palette[i * 3 + 2]= i * 256 / colors;
|
2004-01-05 17:37:42 +00:00
|
|
|
}
|
|
|
|
}
|
2004-01-05 18:54:30 +00:00
|
|
|
|
2013-06-06 23:26:16 +02:00
|
|
|
gimp_image_set_colormap (image, palette + 3, colors - 1);
|
2000-01-17 17:02:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Now get everything redrawn and hand back the finished image */
|
|
|
|
|
2012-11-19 18:43:35 +01:00
|
|
|
g_object_unref (buffer);
|
|
|
|
|
|
|
|
gimp_progress_update (1.0);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2008-08-20 14:00:44 +00:00
|
|
|
return image;
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
static gint
|
2023-08-06 02:33:00 +02:00
|
|
|
load_palette (GFile *file,
|
|
|
|
guchar palette[],
|
|
|
|
GError **error)
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
GFileInputStream *input;
|
|
|
|
guchar header[32]; /* File header */
|
|
|
|
guchar buffer[2];
|
|
|
|
guchar file_mark, bpp;
|
|
|
|
gint i, colors = 0;
|
|
|
|
gssize n_read;
|
2012-07-12 15:50:02 +02:00
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
input = g_file_read (file, NULL, error);
|
|
|
|
if (input == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
n_read = g_input_stream_read (G_INPUT_STREAM (input), header, 4, NULL, error);
|
2012-07-12 15:50:02 +02:00
|
|
|
|
|
|
|
if (n_read < 1)
|
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
g_object_unref (input);
|
2012-07-12 15:50:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
1998-04-26 09:35:56 +00:00
|
|
|
|
2005-08-03 01:15:36 +00:00
|
|
|
if (!strncmp ((const gchar *) header, "KiSS", 4))
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
n_read = g_input_stream_read (G_INPUT_STREAM (input), header + 4, 28, NULL, error);
|
2012-07-12 15:50:02 +02:00
|
|
|
|
|
|
|
if (n_read < 1)
|
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
g_object_unref (input);
|
2012-07-12 15:50:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-13 15:20:06 +02:00
|
|
|
file_mark = header[4];
|
|
|
|
if (file_mark != 0x10)
|
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
g_object_unref (input);
|
2012-07-13 15:20:06 +02:00
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("'%s': is not a KCF palette file"),
|
2019-09-11 21:48:34 +02:00
|
|
|
gimp_file_get_utf8_name (file));
|
2012-07-13 15:20:06 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
bpp = header[5];
|
2012-07-13 15:20:06 +02:00
|
|
|
if (bpp != 12 && bpp != 24)
|
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
g_object_unref (input);
|
2012-07-13 15:20:06 +02:00
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("'%s': illegal bpp value in palette: %hhu"),
|
2019-09-11 21:48:34 +02:00
|
|
|
gimp_file_get_utf8_name (file), bpp);
|
2012-07-13 15:20:06 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-06-06 23:26:16 +02:00
|
|
|
colors = header[8] + header[9] * 256;
|
|
|
|
if (colors != 16 && colors != 256)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
g_object_unref (input);
|
2012-07-13 15:20:06 +02:00
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("'%s': illegal number of colors: %u"),
|
2019-09-11 21:48:34 +02:00
|
|
|
gimp_file_get_utf8_name (file), colors);
|
2012-07-13 15:20:06 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (bpp)
|
|
|
|
{
|
|
|
|
case 12:
|
2013-06-06 23:26:16 +02:00
|
|
|
for (i = 0; i < colors; ++i)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
n_read = g_input_stream_read (G_INPUT_STREAM (input), buffer, 2, NULL, error);
|
2012-07-12 15:50:02 +02:00
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
if (n_read == 1)
|
2012-07-12 15:50:02 +02:00
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
g_object_unref (input);
|
2012-07-12 15:50:02 +02:00
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("'%s': EOF or error while reading "
|
|
|
|
"palette data"),
|
2019-09-11 21:48:34 +02:00
|
|
|
gimp_file_get_utf8_name (file));
|
2012-07-12 15:50:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2023-08-06 02:33:00 +02:00
|
|
|
else if (n_read < 1)
|
|
|
|
{
|
|
|
|
g_object_unref (input);
|
|
|
|
return -1;
|
|
|
|
}
|
2012-07-12 15:50:02 +02:00
|
|
|
|
2003-12-23 22:07:06 +00:00
|
|
|
palette[i*3]= buffer[0] & 0xf0;
|
|
|
|
palette[i*3+1]= (buffer[1] & 0x0f) * 16;
|
|
|
|
palette[i*3+2]= (buffer[0] & 0x0f) * 16;
|
|
|
|
}
|
2012-07-13 15:20:06 +02:00
|
|
|
break;
|
|
|
|
case 24:
|
2023-08-06 02:33:00 +02:00
|
|
|
n_read = g_input_stream_read (G_INPUT_STREAM (input), palette, 3 * colors, NULL, error);
|
2012-07-12 15:50:02 +02:00
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
if (n_read < 1)
|
2012-07-12 15:50:02 +02:00
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
g_object_unref (input);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (n_read < 3 * colors)
|
|
|
|
{
|
|
|
|
g_object_unref (input);
|
2012-07-12 15:50:02 +02:00
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("'%s': EOF or error while reading palette data"),
|
2019-09-11 21:48:34 +02:00
|
|
|
gimp_file_get_utf8_name (file));
|
2012-07-12 15:50:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2012-07-13 15:20:06 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_assert_not_reached ();
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
1998-04-26 09:35:56 +00:00
|
|
|
}
|
2000-01-17 17:02:26 +00:00
|
|
|
else
|
|
|
|
{
|
2013-06-06 23:26:16 +02:00
|
|
|
colors = 16;
|
2023-08-06 02:33:00 +02:00
|
|
|
if (! g_seekable_seek (G_SEEKABLE (input), 0, G_SEEK_SET, NULL, error))
|
|
|
|
{
|
|
|
|
g_object_unref (input);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-06-06 23:26:16 +02:00
|
|
|
for (i= 0; i < colors; ++i)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
n_read = g_input_stream_read (G_INPUT_STREAM (input), buffer, 2, NULL, error);
|
2012-07-12 15:50:02 +02:00
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
if (n_read < 1)
|
2012-07-12 15:50:02 +02:00
|
|
|
{
|
2023-08-06 02:33:00 +02:00
|
|
|
g_object_unref (input);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (n_read == 1)
|
|
|
|
{
|
|
|
|
g_object_unref (input);
|
2012-07-12 15:50:02 +02:00
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
_("'%s': EOF or error while reading palette data"),
|
2019-09-11 21:48:34 +02:00
|
|
|
gimp_file_get_utf8_name (file));
|
2012-07-12 15:50:02 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2003-12-23 22:07:06 +00:00
|
|
|
palette[i*3] = buffer[0] & 0xf0;
|
|
|
|
palette[i*3+1] = (buffer[1] & 0x0f) * 16;
|
|
|
|
palette[i*3+2] = (buffer[0] & 0x0f) * 16;
|
|
|
|
}
|
1998-04-26 09:35:56 +00:00
|
|
|
}
|
|
|
|
|
2013-06-06 23:26:16 +02:00
|
|
|
return colors;
|
1998-04-26 09:35:56 +00:00
|
|
|
}
|
|
|
|
|
2005-05-31 19:10:46 +00:00
|
|
|
static gboolean
|
2024-04-13 15:10:25 +00:00
|
|
|
export_image (GFile *file,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpDrawable *drawable,
|
|
|
|
GError **error)
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
2014-10-27 23:08:41 +01:00
|
|
|
GOutputStream *output;
|
2012-11-19 18:43:35 +01:00
|
|
|
GeglBuffer *buffer;
|
|
|
|
const Babl *format;
|
2018-11-27 12:27:20 +01:00
|
|
|
GCancellable *cancellable;
|
2012-11-19 18:43:35 +01:00
|
|
|
gint width;
|
|
|
|
gint height;
|
2003-06-13 14:37:00 +00:00
|
|
|
guchar header[32]; /* File header */
|
2004-01-05 17:37:42 +00:00
|
|
|
gint bpp; /* Bit per pixel */
|
2014-10-27 23:08:41 +01:00
|
|
|
gint colors; /* Number of colors */
|
|
|
|
gint type; /* type of layer */
|
2003-06-13 14:37:00 +00:00
|
|
|
gint offx, offy; /* Layer offsets */
|
2014-10-27 23:08:41 +01:00
|
|
|
guchar *buf = NULL; /* Temporary buffer */
|
|
|
|
guchar *line = NULL; /* Pixel data */
|
2003-06-13 14:37:00 +00:00
|
|
|
gint i, j, k; /* Counters */
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
/* Check that this is an indexed image, fail otherwise */
|
2019-08-24 17:04:38 +02:00
|
|
|
type = gimp_drawable_type (drawable);
|
2012-11-19 18:43:35 +01:00
|
|
|
|
|
|
|
if (type == GIMP_INDEXEDA_IMAGE)
|
|
|
|
{
|
|
|
|
bpp = 4;
|
|
|
|
format = NULL;
|
|
|
|
}
|
2004-01-05 17:37:42 +00:00
|
|
|
else
|
2012-11-19 18:43:35 +01:00
|
|
|
{
|
|
|
|
bpp = 32;
|
|
|
|
format = babl_format ("R'G'B'A u8");
|
|
|
|
}
|
2004-01-05 18:54:30 +00:00
|
|
|
|
1998-05-30 07:32:37 +00:00
|
|
|
/* Find out how offset this layer was */
|
2021-04-06 14:28:40 +02:00
|
|
|
gimp_drawable_get_offsets (drawable, &offx, &offy);
|
1998-05-30 07:32:37 +00:00
|
|
|
|
2019-08-24 17:04:38 +02:00
|
|
|
buffer = gimp_drawable_get_buffer (drawable);
|
2012-11-19 18:43:35 +01:00
|
|
|
|
|
|
|
width = gegl_buffer_get_width (buffer);
|
|
|
|
height = gegl_buffer_get_height (buffer);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2016-02-16 02:35:43 +01:00
|
|
|
gimp_progress_init_printf (_("Exporting '%s'"),
|
2014-10-27 23:08:41 +01:00
|
|
|
gimp_file_get_utf8_name (file));
|
|
|
|
|
|
|
|
output = G_OUTPUT_STREAM (g_file_replace (file,
|
|
|
|
NULL, FALSE, G_FILE_CREATE_NONE,
|
|
|
|
NULL, error));
|
|
|
|
if (output)
|
|
|
|
{
|
|
|
|
GOutputStream *buffered;
|
2014-07-23 16:39:00 +02:00
|
|
|
|
2014-10-27 23:08:41 +01:00
|
|
|
buffered = g_buffered_output_stream_new (output);
|
|
|
|
g_object_unref (output);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2014-10-27 23:08:41 +01:00
|
|
|
output = buffered;
|
|
|
|
}
|
|
|
|
else
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
2000-01-25 17:46:56 +00:00
|
|
|
return FALSE;
|
2000-01-17 17:02:26 +00:00
|
|
|
}
|
1998-05-30 07:32:37 +00:00
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
/* Headers */
|
|
|
|
memset (header, 0, 32);
|
2005-08-03 01:15:36 +00:00
|
|
|
strcpy ((gchar *) header, "KiSS");
|
1997-11-24 22:05:25 +00:00
|
|
|
header[4]= 0x20;
|
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
/* Work out whether to save as 8bit or 4bit */
|
2004-01-05 17:37:42 +00:00
|
|
|
if (bpp < 32)
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
2023-05-23 23:37:46 +02:00
|
|
|
g_free (gimp_image_get_colormap (image, NULL, &colors));
|
2012-11-19 18:43:35 +01:00
|
|
|
|
2013-06-06 23:26:16 +02:00
|
|
|
if (colors > 15)
|
2004-01-05 17:37:42 +00:00
|
|
|
{
|
|
|
|
header[5] = 8;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
header[5] = 4;
|
|
|
|
}
|
2000-01-17 17:02:26 +00:00
|
|
|
}
|
|
|
|
else
|
2012-11-19 18:43:35 +01:00
|
|
|
{
|
|
|
|
header[5] = 32;
|
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2000-01-17 17:02:26 +00:00
|
|
|
/* Fill in the blanks ... */
|
2012-11-19 18:43:35 +01:00
|
|
|
header[8] = width % 256;
|
|
|
|
header[9] = width / 256;
|
|
|
|
header[10] = height % 256;
|
|
|
|
header[11] = height / 256;
|
2000-01-17 17:02:26 +00:00
|
|
|
header[12] = offx % 256;
|
|
|
|
header[13] = offx / 256;
|
|
|
|
header[14] = offy % 256;
|
|
|
|
header[15] = offy / 256;
|
2014-10-27 23:08:41 +01:00
|
|
|
|
|
|
|
if (! g_output_stream_write_all (output, header, 32, NULL,
|
|
|
|
NULL, error))
|
|
|
|
goto fail;
|
2000-01-17 17:02:26 +00:00
|
|
|
|
|
|
|
/* Arrange for memory etc. */
|
2012-11-19 18:43:35 +01:00
|
|
|
buf = g_new (guchar, width * 4);
|
|
|
|
line = g_new (guchar, (width + 1) * 4);
|
2000-01-17 17:02:26 +00:00
|
|
|
|
2007-06-06 08:44:52 +00:00
|
|
|
/* Get the image from GIMP one line at a time and write it out */
|
2012-11-19 18:43:35 +01:00
|
|
|
for (i = 0; i < height; ++i)
|
2000-01-17 17:02:26 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, 1), 1.0,
|
|
|
|
format, line,
|
|
|
|
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
|
|
|
|
|
|
|
memset (buf, 0, width);
|
2000-01-17 17:02:26 +00:00
|
|
|
|
2004-01-05 17:37:42 +00:00
|
|
|
if (bpp == 32)
|
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
for (j = 0; j < width; j++)
|
2004-01-05 17:37:42 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
buf[4 * j] = line[4 * j + 2]; /* B */
|
|
|
|
buf[4 * j + 1] = line[4 * j + 1]; /* G */
|
|
|
|
buf[4 * j + 2] = line[4 * j + 0]; /* R */
|
|
|
|
buf[4 * j + 3] = line[4 * j + 3]; /* Alpha */
|
2004-01-05 17:37:42 +00:00
|
|
|
}
|
2012-11-19 18:43:35 +01:00
|
|
|
|
2014-10-27 23:08:41 +01:00
|
|
|
if (! g_output_stream_write_all (output, buf, width * 4, NULL,
|
|
|
|
NULL, error))
|
|
|
|
goto fail;
|
2004-01-05 17:37:42 +00:00
|
|
|
}
|
2013-06-06 23:26:16 +02:00
|
|
|
else if (colors > 16)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
for (j = 0, k = 0; j < width * 2; j += 2, ++k)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
if (line[j + 1] > 127)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
buf[k]= line[j] + 1;
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
|
|
|
}
|
2012-11-19 18:43:35 +01:00
|
|
|
|
2014-10-27 23:08:41 +01:00
|
|
|
if (! g_output_stream_write_all (output, buf, width, NULL,
|
|
|
|
NULL, error))
|
|
|
|
goto fail;
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
2000-01-17 17:02:26 +00:00
|
|
|
else
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
for (j = 0, k = 0; j < width * 2; j+= 4, ++k)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
buf[k] = 0;
|
|
|
|
|
|
|
|
if (line[j + 1] > 127)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
buf[k] += (line[j] + 1)<< 4;
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
2012-11-19 18:43:35 +01:00
|
|
|
|
|
|
|
if (line[j + 3] > 127)
|
2003-12-23 22:07:06 +00:00
|
|
|
{
|
2012-11-19 18:43:35 +01:00
|
|
|
buf[k] += (line[j + 2] + 1);
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
|
|
|
}
|
2012-11-19 18:43:35 +01:00
|
|
|
|
2014-10-27 23:08:41 +01:00
|
|
|
if (! g_output_stream_write_all (output, buf, width + 1 / 2, NULL,
|
|
|
|
NULL, error))
|
|
|
|
goto fail;
|
2003-12-23 22:07:06 +00:00
|
|
|
}
|
2000-01-17 17:02:26 +00:00
|
|
|
|
2012-11-19 18:43:35 +01:00
|
|
|
gimp_progress_update ((float) i / (float) height);
|
2000-01-17 17:02:26 +00:00
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2014-10-27 23:08:41 +01:00
|
|
|
if (! g_output_stream_close (output, NULL, error))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
gimp_progress_update (1.0);
|
|
|
|
|
2012-11-19 18:43:35 +01:00
|
|
|
g_free (buf);
|
2000-01-17 17:02:26 +00:00
|
|
|
g_free (line);
|
2012-11-19 18:43:35 +01:00
|
|
|
g_object_unref (buffer);
|
2014-10-27 23:08:41 +01:00
|
|
|
g_object_unref (output);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
|
|
|
return TRUE;
|
2014-10-27 23:08:41 +01:00
|
|
|
|
|
|
|
fail:
|
|
|
|
|
2018-11-27 12:27:20 +01:00
|
|
|
cancellable = g_cancellable_new ();
|
|
|
|
g_cancellable_cancel (cancellable);
|
|
|
|
g_output_stream_close (output, cancellable, NULL);
|
|
|
|
g_object_unref (cancellable);
|
|
|
|
|
2014-10-27 23:08:41 +01:00
|
|
|
g_free (buf);
|
|
|
|
g_free (line);
|
|
|
|
g_object_unref (buffer);
|
|
|
|
g_object_unref (output);
|
|
|
|
|
|
|
|
return FALSE;
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
1998-04-26 09:35:56 +00:00
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
static gboolean
|
|
|
|
palette_dialog (const gchar *title,
|
|
|
|
GimpProcedure *procedure,
|
|
|
|
GimpProcedureConfig *config)
|
2000-01-06 16:40:17 +00:00
|
|
|
{
|
1998-04-26 09:35:56 +00:00
|
|
|
GtkWidget *dialog;
|
2023-08-06 02:33:00 +02:00
|
|
|
gboolean run;
|
1998-04-26 09:35:56 +00:00
|
|
|
|
2019-09-20 19:39:00 +02:00
|
|
|
gimp_ui_init (PLUG_IN_BINARY);
|
1998-04-26 09:35:56 +00:00
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
dialog = gimp_procedure_dialog_new (GIMP_PROCEDURE (procedure),
|
|
|
|
GIMP_PROCEDURE_CONFIG (config),
|
|
|
|
title);
|
|
|
|
gimp_procedure_dialog_set_ok_label (GIMP_PROCEDURE_DIALOG (dialog), _("_Open"));
|
2004-05-17 18:49:44 +00:00
|
|
|
|
2023-08-06 02:33:00 +02:00
|
|
|
gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog), NULL);
|
|
|
|
run = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog));
|
2003-11-19 14:51:52 +00:00
|
|
|
|
|
|
|
gtk_widget_destroy (dialog);
|
2023-08-06 02:33:00 +02:00
|
|
|
|
|
|
|
return run;
|
1998-04-26 09:35:56 +00:00
|
|
|
}
|