2006-12-09 21:33:38 +00:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
1997-11-24 22:05:25 +00:00
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
* FITS file plugin
|
|
|
|
* reading and writing code Copyright (C) 1997 Peter Kirchgessner
|
1999-11-24 23:58:46 +00:00
|
|
|
* e-mail: peter@kirchgessner.net, WWW: http://www.kirchgessner.net
|
1997-11-24 22:05:25 +00:00
|
|
|
*
|
2009-01-17 22:28:01 +00:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
1997-11-24 22:05:25 +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
|
1997-11-24 22:05:25 +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/>.
|
1997-11-24 22:05:25 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Event history:
|
|
|
|
* V 1.00, PK, 05-May-97: Creation
|
|
|
|
* V 1.01, PK, 19-May-97: Problem with compilation on Irix fixed
|
|
|
|
* V 1.02, PK, 08-Jun-97: Bug with saving gray images fixed
|
|
|
|
* V 1.03, PK, 05-Oct-97: Parse rc-file
|
|
|
|
* V 1.04, PK, 12-Oct-97: No progress bars for non-interactive mode
|
1997-12-26 07:08:20 +00:00
|
|
|
* V 1.05, nn, 20-Dec-97: Initialize image_ID in run()
|
1999-11-24 23:58:46 +00:00
|
|
|
* V 1.06, PK, 21-Nov-99: Internationalization
|
|
|
|
* Fix bug with gimp_export_image()
|
|
|
|
* (moved it from load to save)
|
2007-05-07 07:16:59 +00:00
|
|
|
* V 1.07, PK, 16-Aug-06: Fix problems with internationalization
|
|
|
|
* (writing 255,0 instead of 255.0)
|
|
|
|
* Fix problem with not filling up properly last record
|
1997-11-24 22:05:25 +00:00
|
|
|
*/
|
2003-06-13 14:37:00 +00:00
|
|
|
|
2000-01-01 15:38:59 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2024-10-28 01:20:55 +01:00
|
|
|
#include <math.h>
|
1997-11-24 22:05:25 +00:00
|
|
|
#include <string.h>
|
2005-03-04 13:23:32 +00:00
|
|
|
#include <errno.h>
|
2000-01-01 15:38:59 +00:00
|
|
|
|
2005-03-04 13:23:32 +00:00
|
|
|
#include <glib/gstdio.h>
|
2000-01-25 17:46:56 +00:00
|
|
|
|
|
|
|
#include <libgimp/gimp.h>
|
|
|
|
#include <libgimp/gimpui.h>
|
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
#include <fitsio.h>
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2000-01-25 17:46:56 +00:00
|
|
|
#include "libgimp/stdplugins-intl.h"
|
|
|
|
|
2000-04-30 18:17:55 +00:00
|
|
|
|
2005-08-12 15:46:46 +00:00
|
|
|
#define LOAD_PROC "file-fits-load"
|
2024-04-13 15:10:25 +00:00
|
|
|
#define EXPORT_PROC "file-fits-export"
|
2008-08-11 10:06:13 +00:00
|
|
|
#define PLUG_IN_BINARY "file-fits"
|
2011-04-08 20:31:34 +02:00
|
|
|
#define PLUG_IN_ROLE "gimp-file-fits"
|
2005-08-12 15:46:46 +00:00
|
|
|
|
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
typedef struct _Fits Fits;
|
|
|
|
typedef struct _FitsClass FitsClass;
|
|
|
|
|
|
|
|
struct _Fits
|
|
|
|
{
|
|
|
|
GimpPlugIn parent_instance;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _FitsClass
|
|
|
|
{
|
|
|
|
GimpPlugInClass parent_class;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#define FITS_TYPE (fits_get_type ())
|
2023-10-18 18:29:37 +02:00
|
|
|
#define FITS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FITS_TYPE, Fits))
|
2019-08-24 17:49:58 +02:00
|
|
|
|
|
|
|
GType fits_get_type (void) G_GNUC_CONST;
|
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
gint naxis;
|
|
|
|
glong naxisn[3];
|
|
|
|
gint bitpix;
|
|
|
|
gint bpp;
|
|
|
|
gint datatype;
|
|
|
|
} FitsHduData;
|
|
|
|
|
2023-08-06 02:56:44 +02:00
|
|
|
static GList * fits_query_procedures (GimpPlugIn *plug_in);
|
|
|
|
static GimpProcedure * fits_create_procedure (GimpPlugIn *plug_in,
|
|
|
|
const gchar *name);
|
|
|
|
|
|
|
|
static GimpValueArray * fits_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 * fits_export (GimpProcedure *procedure,
|
2023-08-06 02:56:44 +02:00
|
|
|
GimpRunMode run_mode,
|
|
|
|
GimpImage *image,
|
|
|
|
GFile *file,
|
2024-05-06 18:38:12 +00:00
|
|
|
GimpExportOptions *options,
|
2023-08-06 02:56:44 +02:00
|
|
|
GimpMetadata *metadata,
|
|
|
|
GimpProcedureConfig *config,
|
|
|
|
gpointer run_data);
|
|
|
|
|
|
|
|
static GimpImage * load_image (GFile *file,
|
|
|
|
GObject *config,
|
|
|
|
GimpRunMode run_mode,
|
|
|
|
GError **error);
|
2024-04-13 15:10:25 +00:00
|
|
|
static gint export_image (GFile *file,
|
2023-08-06 02:56:44 +02:00
|
|
|
GimpImage *image,
|
|
|
|
GimpDrawable *drawable,
|
|
|
|
GError **error);
|
|
|
|
|
2024-04-13 15:10:25 +00:00
|
|
|
static gint export_fits (GFile *file,
|
2023-08-06 02:56:44 +02:00
|
|
|
GimpImage *image,
|
|
|
|
GimpDrawable *drawable);
|
|
|
|
|
|
|
|
static GimpImage * create_new_image (GFile *file,
|
|
|
|
guint pagenum,
|
|
|
|
guint width,
|
|
|
|
guint height,
|
|
|
|
GimpImageBaseType itype,
|
|
|
|
GimpImageType dtype,
|
|
|
|
GimpPrecision iprecision,
|
|
|
|
GimpLayer **layer,
|
|
|
|
GeglBuffer **buffer);
|
|
|
|
|
|
|
|
static gboolean load_dialog (GimpProcedure *procedure,
|
|
|
|
GObject *config);
|
|
|
|
static void show_fits_errors (gint status);
|
2019-08-24 17:49:58 +02:00
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (Fits, fits, GIMP_TYPE_PLUG_IN)
|
|
|
|
|
|
|
|
GIMP_MAIN (FITS_TYPE)
|
2022-05-26 00:59:36 +02:00
|
|
|
DEFINE_STD_SET_I18N
|
1997-11-24 22:05:25 +00:00
|
|
|
|
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
static void
|
|
|
|
fits_class_init (FitsClass *klass)
|
|
|
|
{
|
|
|
|
GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);
|
|
|
|
|
|
|
|
plug_in_class->query_procedures = fits_query_procedures;
|
|
|
|
plug_in_class->create_procedure = fits_create_procedure;
|
2022-05-26 00:59:36 +02:00
|
|
|
plug_in_class->set_i18n = STD_SET_I18N;
|
2019-08-24 17:49:58 +02:00
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
|
|
|
static void
|
2019-08-24 17:49:58 +02:00
|
|
|
fits_init (Fits *fits)
|
|
|
|
{
|
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
static GList *
|
|
|
|
fits_query_procedures (GimpPlugIn *plug_in)
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
2019-08-24 17:49:58 +02:00
|
|
|
GList *list = NULL;
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +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));
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
return list;
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
static GimpProcedure *
|
|
|
|
fits_create_procedure (GimpPlugIn *plug_in,
|
|
|
|
const gchar *name)
|
|
|
|
{
|
|
|
|
GimpProcedure *procedure = NULL;
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +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,
|
|
|
|
fits_load, NULL, NULL);
|
2019-08-24 17:49:58 +02:00
|
|
|
|
|
|
|
gimp_procedure_set_menu_label (procedure,
|
2023-03-06 15:45:45 +00:00
|
|
|
_("Flexible Image Transport System"));
|
2019-08-24 17:49:58 +02:00
|
|
|
|
|
|
|
gimp_procedure_set_documentation (procedure,
|
2023-03-06 15:45:45 +00:00
|
|
|
_("Load file of the FITS file format"),
|
|
|
|
_("Load file of the FITS file format "
|
|
|
|
"(Flexible Image Transport System)"),
|
2019-08-24 17:49:58 +02:00
|
|
|
name);
|
|
|
|
gimp_procedure_set_attribution (procedure,
|
|
|
|
"Peter Kirchgessner",
|
2023-03-06 15:45:45 +00:00
|
|
|
"Peter Kirchgessner (peter@kirchgessner.net), "
|
|
|
|
"Alex Sa.",
|
|
|
|
"1997 - 2023");
|
2019-08-24 17:49:58 +02:00
|
|
|
|
|
|
|
gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
"image/x-fits");
|
|
|
|
gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
"fit,fits");
|
|
|
|
gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
"0,string,SIMPLE");
|
2019-10-03 16:00:17 +02:00
|
|
|
|
2024-07-14 04:12:26 +00:00
|
|
|
gimp_procedure_add_choice_aux_argument (procedure, "replace",
|
|
|
|
_("Re_placement for undefined pixels"),
|
|
|
|
_("Replacement for undefined pixels"),
|
|
|
|
gimp_choice_new_with_values ("black", 0, _("Black"), NULL,
|
|
|
|
"white", 255, _("White"), NULL,
|
|
|
|
NULL),
|
|
|
|
"black",
|
|
|
|
G_PARAM_READWRITE);
|
2019-08-24 17:49:58 +02:00
|
|
|
}
|
2024-04-13 15:10:25 +00:00
|
|
|
else if (! strcmp (name, EXPORT_PROC))
|
2019-08-24 17:49:58 +02:00
|
|
|
{
|
2024-04-20 03:08:57 +00:00
|
|
|
procedure = gimp_export_procedure_new (plug_in, name,
|
|
|
|
GIMP_PDB_PROC_TYPE_PLUGIN,
|
|
|
|
FALSE, fits_export, NULL, NULL);
|
2019-08-24 17:49:58 +02:00
|
|
|
|
|
|
|
gimp_procedure_set_image_types (procedure, "RGB, GRAY, INDEXED");
|
|
|
|
|
|
|
|
gimp_procedure_set_menu_label (procedure,
|
2023-03-06 15:45:45 +00:00
|
|
|
_("Flexible Image Transport System"));
|
2019-08-24 17:49:58 +02:00
|
|
|
|
|
|
|
gimp_procedure_set_documentation (procedure,
|
2023-03-06 15:45:45 +00:00
|
|
|
_("Export file in the FITS file format"),
|
|
|
|
_("FITS exporting handles all image "
|
|
|
|
"types except those with alpha channels."),
|
2019-08-24 17:49:58 +02:00
|
|
|
name);
|
|
|
|
gimp_procedure_set_attribution (procedure,
|
|
|
|
"Peter Kirchgessner",
|
2023-03-06 15:45:45 +00:00
|
|
|
"Peter Kirchgessner (peter@kirchgessner.net), "
|
|
|
|
"Alex Sa.",
|
|
|
|
"1997 - 2023");
|
2019-08-24 17:49:58 +02:00
|
|
|
|
|
|
|
gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
"image/x-fits");
|
|
|
|
gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
|
|
|
|
"fit,fits");
|
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,
|
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);
|
2019-08-24 17:49:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return procedure;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GimpValueArray *
|
2023-08-06 02:56:44 +02:00
|
|
|
fits_load (GimpProcedure *procedure,
|
|
|
|
GimpRunMode run_mode,
|
|
|
|
GFile *file,
|
|
|
|
GimpMetadata *metadata,
|
|
|
|
GimpMetadataLoadFlags *flags,
|
|
|
|
GimpProcedureConfig *config,
|
|
|
|
gpointer run_data)
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
2023-08-06 02:56:44 +02:00
|
|
|
GimpValueArray *return_vals;
|
|
|
|
GimpImage *image;
|
|
|
|
GError *error = NULL;
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2013-01-11 00:49:31 +01:00
|
|
|
gegl_init (NULL, NULL);
|
|
|
|
|
2019-10-03 16:00:17 +02:00
|
|
|
if (run_mode == GIMP_RUN_INTERACTIVE)
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
2019-10-03 16:00:17 +02:00
|
|
|
if (! load_dialog (procedure, G_OBJECT (config)))
|
2019-08-24 17:49:58 +02:00
|
|
|
return gimp_procedure_new_return_values (procedure,
|
|
|
|
GIMP_PDB_CANCEL,
|
|
|
|
NULL);
|
|
|
|
}
|
2013-01-10 23:17:57 +01:00
|
|
|
|
2019-10-03 16:00:17 +02:00
|
|
|
image = load_image (file, G_OBJECT (config), run_mode, &error);
|
2013-01-10 23:17:57 +01:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
if (! image)
|
|
|
|
return gimp_procedure_new_return_values (procedure,
|
|
|
|
GIMP_PDB_EXECUTION_ERROR,
|
|
|
|
error);
|
1999-11-24 23:58:46 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
return_vals = gimp_procedure_new_return_values (procedure,
|
|
|
|
GIMP_PDB_SUCCESS,
|
|
|
|
NULL);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
GIMP_VALUES_SET_IMAGE (return_vals, 1, image);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
return return_vals;
|
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
static GimpValueArray *
|
2024-04-13 15:10:25 +00:00
|
|
|
fits_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:49:58 +02:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
GimpImage *duplicate_image;
|
2024-10-21 21:59:26 +02:00
|
|
|
GimpDrawable **flipped_drawables;
|
2023-03-06 15:45:45 +00: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;
|
2023-03-06 15:45:45 +00:00
|
|
|
GError *error = NULL;
|
2019-08-19 12:05:12 +02:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
gegl_init (NULL, NULL);
|
1999-10-20 01:45:41 +00:00
|
|
|
|
2024-07-14 20:12:57 +00:00
|
|
|
if (run_mode == GIMP_RUN_INTERACTIVE)
|
|
|
|
gimp_ui_init (PLUG_IN_BINARY);
|
|
|
|
|
2024-05-06 18:38:12 +00:00
|
|
|
export = gimp_export_options_get_image (options, &image);
|
2024-07-14 20:12:57 +00:00
|
|
|
|
2024-10-21 21:59:26 +02:00
|
|
|
drawables = gimp_image_list_layers (image);
|
2020-04-14 11:46:17 +02:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
/* Flip image vertical since FITS writes from bottom to top */
|
|
|
|
duplicate_image = gimp_image_duplicate (image);
|
|
|
|
gimp_image_flip (duplicate_image, GIMP_ORIENTATION_VERTICAL);
|
2024-10-21 21:59:26 +02:00
|
|
|
flipped_drawables = gimp_image_get_selected_drawables (duplicate_image);
|
2023-03-06 15:45:45 +00:00
|
|
|
|
2024-10-21 21:59:26 +02:00
|
|
|
if (! export_image (file, image, flipped_drawables[0], &error))
|
2023-03-06 15:45:45 +00:00
|
|
|
status = GIMP_PDB_EXECUTION_ERROR;
|
|
|
|
|
|
|
|
gimp_image_delete (duplicate_image);
|
|
|
|
g_free (flipped_drawables);
|
2008-08-20 13:44:17 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
if (export == GIMP_EXPORT_EXPORT)
|
2024-04-30 13:50:24 +00:00
|
|
|
gimp_image_delete (image);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2024-04-30 13:50:24 +00:00
|
|
|
g_list_free (drawables);
|
2019-08-24 17:49:58 +02:00
|
|
|
return gimp_procedure_new_return_values (procedure, status, error);
|
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
static GimpImage *
|
2019-10-03 16:00:17 +02:00
|
|
|
load_image (GFile *file,
|
|
|
|
GObject *config,
|
|
|
|
GimpRunMode run_mode,
|
|
|
|
GError **error)
|
2000-01-25 17:46:56 +00:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
GimpImage *image = NULL;
|
|
|
|
GimpLayer *layer;
|
|
|
|
GeglBuffer *buffer;
|
|
|
|
FILE *fp;
|
|
|
|
fitsfile *ifp;
|
|
|
|
FitsHduData hdu;
|
|
|
|
gint n_pics;
|
|
|
|
gint count = 1;
|
|
|
|
gint width;
|
|
|
|
gint height;
|
|
|
|
gint row_length;
|
|
|
|
int status = 0;
|
|
|
|
glong fpixel[3] = {1, 1, 1};
|
|
|
|
GimpImageBaseType itype = GIMP_GRAY;
|
|
|
|
GimpImageType dtype = GIMP_GRAYA_IMAGE;
|
|
|
|
GimpPrecision iprecision = GIMP_PRECISION_U16_NON_LINEAR;
|
|
|
|
const Babl *type = NULL;
|
|
|
|
const Babl *format = NULL;
|
|
|
|
gdouble *pixels;
|
|
|
|
gdouble datamin = 1.0E30f;
|
|
|
|
gdouble datamax = -1.0E30f;
|
|
|
|
gint channels = 1;
|
|
|
|
gint replace;
|
|
|
|
gdouble replace_val = 0;
|
2019-10-03 16:00:17 +02:00
|
|
|
|
2024-07-14 04:12:26 +00:00
|
|
|
replace = gimp_procedure_config_get_choice_id (GIMP_PROCEDURE_CONFIG (config),
|
|
|
|
"replace");
|
2000-01-25 17:46:56 +00:00
|
|
|
|
2021-10-01 18:14:14 +02:00
|
|
|
fp = g_fopen (g_file_peek_path (file), "rb");
|
2019-09-11 21:48:34 +02:00
|
|
|
|
|
|
|
if (! fp)
|
2000-01-25 17:46:56 +00:00
|
|
|
{
|
2008-08-20 13:44:17 +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:49:58 +02:00
|
|
|
return NULL;
|
2000-01-25 17:46:56 +00:00
|
|
|
}
|
2019-09-11 21:48:34 +02:00
|
|
|
|
2000-01-25 17:46:56 +00:00
|
|
|
fclose (fp);
|
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
if (fits_open_diskfile (&ifp, g_file_peek_path (file), READONLY, &status))
|
|
|
|
show_fits_errors (status);
|
2019-09-11 21:48:34 +02:00
|
|
|
|
|
|
|
if (! ifp)
|
2000-01-25 17:46:56 +00:00
|
|
|
{
|
2008-08-20 13:44:17 +00:00
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
2023-03-06 15:45:45 +00:00
|
|
|
"%s", _("Error during opening of FITS file"));
|
2019-08-24 17:49:58 +02:00
|
|
|
return NULL;
|
2000-01-25 17:46:56 +00:00
|
|
|
}
|
2019-09-11 21:48:34 +02:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
/* Get first item */
|
|
|
|
fits_get_num_hdus (ifp, &n_pics, &status);
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
show_fits_errors (status);
|
|
|
|
|
|
|
|
if (n_pics <= 0)
|
2000-01-25 17:46:56 +00:00
|
|
|
{
|
2008-08-20 13:44:17 +00:00
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
"%s", _("FITS file keeps no displayable images"));
|
2023-03-06 15:45:45 +00:00
|
|
|
fits_close_file (ifp, &status);
|
2019-08-24 17:49:58 +02:00
|
|
|
return NULL;
|
2000-01-25 17:46:56 +00:00
|
|
|
}
|
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
while (count <= n_pics)
|
2000-01-25 17:46:56 +00:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
hdu.naxis = 0;
|
2000-01-25 17:46:56 +00:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
fits_movabs_hdu (ifp, count, NULL, &status);
|
2019-08-24 17:49:58 +02:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
fits_get_img_param (ifp, 3, &hdu.bitpix, &hdu.naxis, hdu.naxisn,
|
|
|
|
&status);
|
2000-01-25 17:46:56 +00:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
/* Skip if invalid dimensions; possibly header data */
|
|
|
|
if (hdu.naxis < 2)
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-04-14 17:20:40 +00:00
|
|
|
width = hdu.naxisn[0];
|
|
|
|
height = hdu.naxisn[1];
|
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
type = babl_type ("double");
|
|
|
|
switch (hdu.bitpix)
|
|
|
|
{
|
|
|
|
case 8:
|
|
|
|
iprecision = GIMP_PRECISION_U8_LINEAR;
|
|
|
|
if (replace)
|
|
|
|
replace_val = 255;
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
iprecision = GIMP_PRECISION_U16_NON_LINEAR;
|
|
|
|
if (replace)
|
|
|
|
replace_val = G_MAXSHORT;
|
|
|
|
break;
|
|
|
|
case 32:
|
|
|
|
iprecision = GIMP_PRECISION_U32_LINEAR;
|
|
|
|
if (replace)
|
|
|
|
replace_val = G_MAXINT;
|
|
|
|
break;
|
|
|
|
case -32:
|
|
|
|
iprecision = GIMP_PRECISION_FLOAT_LINEAR;
|
|
|
|
if (replace)
|
|
|
|
replace_val = G_MAXFLOAT;
|
|
|
|
break;
|
|
|
|
case -64:
|
|
|
|
iprecision = GIMP_PRECISION_DOUBLE_LINEAR;
|
|
|
|
if (replace)
|
|
|
|
replace_val = G_MAXDOUBLE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hdu.naxis == 2)
|
|
|
|
{
|
|
|
|
itype = GIMP_GRAY;
|
|
|
|
dtype = GIMP_GRAYA_IMAGE;
|
|
|
|
format = babl_format_new (babl_model ("Y'"),
|
|
|
|
type,
|
|
|
|
babl_component ("Y'"),
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
else if (hdu.naxisn[2])
|
|
|
|
{
|
|
|
|
/* Original RGB format */
|
|
|
|
if (hdu.naxisn[0] == 3)
|
|
|
|
{
|
|
|
|
width = hdu.naxisn[1];
|
|
|
|
height = hdu.naxisn[2];
|
|
|
|
}
|
|
|
|
channels = 3;
|
|
|
|
|
|
|
|
itype = GIMP_RGB;
|
|
|
|
dtype = GIMP_RGB_IMAGE;
|
|
|
|
format = babl_format_new (babl_model ("R'G'B'"),
|
|
|
|
type,
|
|
|
|
babl_component ("R'"),
|
|
|
|
babl_component ("G'"),
|
|
|
|
babl_component ("B'"),
|
|
|
|
NULL);
|
|
|
|
}
|
2000-01-25 17:46:56 +00:00
|
|
|
|
2023-06-29 15:31:50 +00:00
|
|
|
/* If RGB FITS image, we need to read in the whole image so we can convert
|
|
|
|
* the planes format to RGB */
|
|
|
|
if (hdu.naxis == 2)
|
|
|
|
pixels = (gdouble *) malloc (width * sizeof (gdouble) * channels);
|
|
|
|
else
|
|
|
|
pixels = (gdouble *) malloc (width * height * sizeof (gdouble) * channels);
|
2000-01-25 17:46:56 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
if (! image)
|
2023-03-06 15:45:45 +00:00
|
|
|
{
|
|
|
|
image = create_new_image (file, count, width, height,
|
|
|
|
itype, dtype, iprecision,
|
|
|
|
&layer, &buffer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
layer = gimp_layer_new (image, _("FITS HDU"), width, height,
|
|
|
|
dtype, 100,
|
|
|
|
gimp_image_get_default_new_layer_mode (image));
|
|
|
|
gimp_image_insert_layer (image, layer, NULL, 0);
|
|
|
|
buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
|
|
|
|
}
|
|
|
|
|
|
|
|
row_length = width * channels;
|
2019-08-24 17:49:58 +02:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
/* Calculate min/max pixel value for normalizing */
|
|
|
|
for (fpixel[1] = height; fpixel[1] >= 1; fpixel[1]--)
|
2013-01-10 23:17:57 +01:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
if (fits_read_pix (ifp, TDOUBLE, fpixel, row_length, NULL,
|
|
|
|
pixels, NULL, &status))
|
2019-08-24 17:49:58 +02:00
|
|
|
break;
|
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
for (gint ii = 0; ii < row_length; ii++)
|
|
|
|
{
|
|
|
|
if (pixels[ii] < datamin)
|
|
|
|
datamin = pixels[ii];
|
|
|
|
|
|
|
|
if (pixels[ii] > datamax)
|
|
|
|
datamax = pixels[ii];
|
|
|
|
}
|
2013-01-10 23:17:57 +01:00
|
|
|
}
|
2019-08-24 17:49:58 +02:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
if (status)
|
|
|
|
show_fits_errors (status);
|
2000-01-25 17:46:56 +00:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
/* Read pixel values in */
|
2023-06-29 15:31:50 +00:00
|
|
|
if (hdu.naxis == 2)
|
2023-03-06 15:45:45 +00:00
|
|
|
{
|
2023-06-29 15:31:50 +00:00
|
|
|
for (fpixel[1] = height; fpixel[1] >= 1; fpixel[1]--)
|
|
|
|
{
|
|
|
|
if (fits_read_pix (ifp, TDOUBLE, fpixel, row_length, &replace_val,
|
|
|
|
pixels, NULL, &status))
|
|
|
|
break;
|
2000-01-25 17:46:56 +00:00
|
|
|
|
2023-06-29 15:31:50 +00:00
|
|
|
if (datamin < datamax)
|
|
|
|
{
|
|
|
|
for (gint ii = 0; ii < row_length; ii++)
|
|
|
|
pixels[ii] = (pixels[ii] - datamin) / (datamax - datamin);
|
|
|
|
}
|
2000-01-25 17:46:56 +00:00
|
|
|
|
2023-06-29 15:31:50 +00:00
|
|
|
gegl_buffer_set (buffer,
|
|
|
|
GEGL_RECTANGLE (0, height - fpixel[1],
|
|
|
|
width, 1), 0,
|
|
|
|
format, pixels, GEGL_AUTO_ROWSTRIDE);
|
2023-03-06 15:45:45 +00:00
|
|
|
}
|
2023-06-29 15:31:50 +00:00
|
|
|
}
|
|
|
|
else if (hdu.naxisn[2] && hdu.naxisn[2] == 3)
|
|
|
|
{
|
|
|
|
gint total_size = width * height * channels;
|
2000-01-25 17:46:56 +00:00
|
|
|
|
2023-06-29 15:31:50 +00:00
|
|
|
fits_read_img (ifp, TDOUBLE, 1, total_size, &replace_val,
|
|
|
|
pixels, NULL, &status);
|
|
|
|
|
|
|
|
if (! status)
|
2023-03-06 15:45:45 +00:00
|
|
|
{
|
2023-06-29 15:31:50 +00:00
|
|
|
gdouble *temp;
|
|
|
|
|
|
|
|
temp = (gdouble *) malloc (width * height * sizeof (gdouble) * channels);
|
|
|
|
|
|
|
|
if (datamin < datamax)
|
|
|
|
{
|
|
|
|
for (gint ii = 0; ii < total_size; ii++)
|
|
|
|
pixels[ii] = (pixels[ii] - datamin) / (datamax - datamin);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (gint ii = 0; ii < (total_size / 3); ii++)
|
2023-03-06 15:45:45 +00:00
|
|
|
{
|
|
|
|
temp[(ii * 3)] = pixels[ii];
|
2023-06-29 15:31:50 +00:00
|
|
|
temp[(ii * 3) + 1] = pixels[ii + (total_size / 3)];
|
|
|
|
temp[(ii * 3) + 2] = pixels[ii + ((total_size / 3) * 2)];
|
2023-03-06 15:45:45 +00:00
|
|
|
}
|
|
|
|
|
2023-06-29 15:31:50 +00:00
|
|
|
gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
|
|
|
|
format, temp, GEGL_AUTO_ROWSTRIDE);
|
|
|
|
|
|
|
|
if (temp)
|
|
|
|
g_free (temp);
|
|
|
|
}
|
2013-01-10 23:17:57 +01:00
|
|
|
}
|
2023-06-29 15:31:50 +00:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
if (status)
|
|
|
|
show_fits_errors (status);
|
|
|
|
|
|
|
|
g_object_unref (buffer);
|
2023-06-30 15:11:46 +00:00
|
|
|
|
|
|
|
if (hdu.naxisn[2] && hdu.naxisn[2] == 3)
|
|
|
|
{
|
|
|
|
/* Per SiriL developers, FITS images should be loaded from the
|
|
|
|
* bottom up. fits_read_img () loads them from top down, so we
|
|
|
|
* should flip the layer. */
|
|
|
|
gimp_item_transform_flip_simple (GIMP_ITEM (layer),
|
|
|
|
GIMP_ORIENTATION_VERTICAL,
|
|
|
|
TRUE, -1.0);
|
|
|
|
}
|
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
if (pixels)
|
|
|
|
g_free (pixels);
|
|
|
|
|
|
|
|
count++;
|
2000-01-25 17:46:56 +00:00
|
|
|
}
|
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
if (! image)
|
|
|
|
{
|
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
"%s", _("FITS file keeps no displayable images"));
|
|
|
|
fits_close_file (ifp, &status);
|
|
|
|
return NULL;
|
|
|
|
}
|
2000-01-25 17:46:56 +00:00
|
|
|
|
2024-04-14 17:20:40 +00:00
|
|
|
/* As there might be different sized layers,
|
|
|
|
* we need to resize the canvas afterwards */
|
|
|
|
gimp_image_resize_to_layers (image);
|
|
|
|
|
|
|
|
fits_close_file (ifp, &status);
|
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
return image;
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gint
|
2024-04-13 15:10:25 +00:00
|
|
|
export_image (GFile *file,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpDrawable *drawable,
|
|
|
|
GError **error)
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
2013-01-10 23:07:35 +01:00
|
|
|
GimpImageType drawable_type;
|
|
|
|
gint retval;
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
drawable_type = gimp_drawable_type (drawable);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2016-02-16 02:35:43 +01:00
|
|
|
/* Make sure we're not exporting an image with an alpha channel */
|
2019-08-24 17:49:58 +02:00
|
|
|
if (gimp_drawable_has_alpha (drawable))
|
2000-01-25 17:46:56 +00:00
|
|
|
{
|
2008-08-20 13:44:17 +00:00
|
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
|
|
"%s",
|
2016-02-16 02:35:43 +01:00
|
|
|
_("FITS export cannot handle images with alpha channels"));
|
2000-01-25 17:46:56 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
|
|
|
switch (drawable_type)
|
2000-01-25 17:46:56 +00:00
|
|
|
{
|
2000-08-22 03:27:14 +00:00
|
|
|
case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE:
|
|
|
|
case GIMP_GRAY_IMAGE: case GIMP_GRAYA_IMAGE:
|
|
|
|
case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE:
|
1997-11-24 22:05:25 +00:00
|
|
|
break;
|
|
|
|
default:
|
2003-11-15 13:53:33 +00:00
|
|
|
g_message (_("Cannot operate on unknown image types."));
|
1997-11-24 22:05:25 +00:00
|
|
|
return (FALSE);
|
|
|
|
break;
|
2000-01-25 17:46:56 +00:00
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2016-02-16 02:35:43 +01:00
|
|
|
gimp_progress_init_printf (_("Exporting '%s'"),
|
2019-09-11 21:48:34 +02:00
|
|
|
gimp_file_get_utf8_name (file));
|
2014-07-23 16:39:00 +02:00
|
|
|
|
2019-09-11 21:48:34 +02:00
|
|
|
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2024-04-13 15:10:25 +00:00
|
|
|
retval = export_fits (file, image, drawable);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
return retval;
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
|
2019-08-24 17:49:58 +02:00
|
|
|
static GimpImage *
|
2019-09-11 21:48:34 +02:00
|
|
|
create_new_image (GFile *file,
|
2003-07-02 00:15:09 +00:00
|
|
|
guint pagenum,
|
|
|
|
guint width,
|
|
|
|
guint height,
|
|
|
|
GimpImageBaseType itype,
|
|
|
|
GimpImageType dtype,
|
2013-02-17 18:32:08 -05:00
|
|
|
GimpPrecision iprecision,
|
2019-08-24 17:49:58 +02:00
|
|
|
GimpLayer **layer,
|
2013-01-11 00:49:31 +01:00
|
|
|
GeglBuffer **buffer)
|
2000-01-25 17:46:56 +00:00
|
|
|
{
|
2019-08-24 17:49:58 +02:00
|
|
|
GimpImage *image;
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
image = gimp_image_new_with_precision (width, height, itype, iprecision);
|
2013-02-17 18:32:08 -05:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
gimp_image_undo_disable (image);
|
|
|
|
*layer = gimp_layer_new (image, _("Background"), width, height,
|
|
|
|
dtype, 100,
|
|
|
|
gimp_image_get_default_new_layer_mode (image));
|
|
|
|
gimp_image_insert_layer (image, *layer, NULL, 0);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
*buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (*layer));
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
return image;
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
|
|
|
|
2024-04-13 15:10:25 +00:00
|
|
|
/* Export direct colors (GRAY, GRAYA, RGB, RGBA) */
|
1997-11-24 22:05:25 +00:00
|
|
|
static gint
|
2024-04-13 15:10:25 +00:00
|
|
|
export_fits (GFile *file,
|
|
|
|
GimpImage *image,
|
|
|
|
GimpDrawable *drawable)
|
2000-01-25 17:46:56 +00:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
fitsfile *fptr;
|
|
|
|
gint status = 0;
|
|
|
|
gint height, width, channelnum;
|
|
|
|
gint bitpix;
|
|
|
|
gint naxis = 2;
|
|
|
|
glong naxes[3];
|
|
|
|
gint export_type;
|
|
|
|
gint nelements;
|
|
|
|
void *data = NULL;
|
2013-01-11 00:49:31 +01:00
|
|
|
GeglBuffer *buffer;
|
2013-03-02 11:36:55 -05:00
|
|
|
const Babl *format, *type;
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
buffer = gimp_drawable_get_buffer (drawable);
|
2013-01-11 00:49:31 +01:00
|
|
|
|
|
|
|
width = gegl_buffer_get_width (buffer);
|
|
|
|
height = gegl_buffer_get_height (buffer);
|
|
|
|
|
2013-03-02 11:36:55 -05:00
|
|
|
format = gegl_buffer_get_format (buffer);
|
|
|
|
type = babl_format_get_type (format, 0);
|
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
naxes[0] = width;
|
|
|
|
naxes[1] = height;
|
|
|
|
nelements = width * height;
|
|
|
|
|
2013-03-02 11:36:55 -05:00
|
|
|
if (type == babl_type ("u8"))
|
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
bitpix = 8;
|
|
|
|
export_type = TBYTE;
|
2013-03-02 11:36:55 -05:00
|
|
|
}
|
|
|
|
else if (type == babl_type ("u16"))
|
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
bitpix = 16;
|
|
|
|
export_type = TSHORT;
|
2013-03-02 11:36:55 -05:00
|
|
|
}
|
|
|
|
else if (type == babl_type ("u32"))
|
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
bitpix = 32;
|
|
|
|
export_type = TLONG;
|
2013-03-02 11:36:55 -05:00
|
|
|
}
|
2023-03-06 15:45:45 +00:00
|
|
|
else if (type == babl_type ("half") ||
|
|
|
|
type == babl_type ("float"))
|
2013-03-02 11:36:55 -05:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
bitpix = -32;
|
|
|
|
type = babl_type ("float");
|
|
|
|
export_type = TFLOAT;
|
2014-10-06 13:19:20 +02:00
|
|
|
}
|
|
|
|
else if (type == babl_type ("double"))
|
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
bitpix = -64;
|
|
|
|
export_type = TDOUBLE;
|
2013-03-02 11:36:55 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2019-08-24 17:49:58 +02:00
|
|
|
switch (gimp_drawable_type (drawable))
|
2013-01-11 00:49:31 +01:00
|
|
|
{
|
|
|
|
case GIMP_GRAY_IMAGE:
|
2013-03-02 11:36:55 -05:00
|
|
|
format = babl_format_new (babl_model ("Y'"),
|
|
|
|
type,
|
|
|
|
babl_component ("Y'"),
|
|
|
|
NULL);
|
2013-01-11 00:49:31 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GIMP_GRAYA_IMAGE:
|
2013-03-02 11:36:55 -05:00
|
|
|
format = babl_format_new (babl_model ("Y'A"),
|
|
|
|
type,
|
|
|
|
babl_component ("Y'"),
|
|
|
|
babl_component ("A"),
|
|
|
|
NULL);
|
2013-01-11 00:49:31 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GIMP_RGB_IMAGE:
|
|
|
|
case GIMP_INDEXED_IMAGE:
|
2013-03-02 11:36:55 -05:00
|
|
|
format = babl_format_new (babl_model ("R'G'B'"),
|
|
|
|
type,
|
|
|
|
babl_component ("R'"),
|
|
|
|
babl_component ("G'"),
|
|
|
|
babl_component ("B'"),
|
|
|
|
NULL);
|
2023-03-06 15:45:45 +00:00
|
|
|
naxis = 3;
|
|
|
|
naxes[2] = 3;
|
2013-01-11 00:49:31 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GIMP_RGBA_IMAGE:
|
|
|
|
case GIMP_INDEXEDA_IMAGE:
|
2013-03-02 11:36:55 -05:00
|
|
|
format = babl_format_new (babl_model ("R'G'B'A"),
|
|
|
|
type,
|
|
|
|
babl_component ("R'"),
|
|
|
|
babl_component ("G'"),
|
|
|
|
babl_component ("B'"),
|
|
|
|
babl_component ("A"),
|
|
|
|
NULL);
|
2023-03-06 15:45:45 +00:00
|
|
|
naxis = 4;
|
|
|
|
naxes[2] = 4;
|
2013-01-11 00:49:31 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-03-02 11:36:55 -05:00
|
|
|
channelnum = babl_format_get_n_components (format);
|
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
/* allocate a buffer for retrieving information from the pixel region */
|
|
|
|
if (export_type == TFLOAT)
|
|
|
|
data = (gfloat *) g_malloc (width * height * sizeof (gfloat) * channelnum);
|
|
|
|
else if (export_type == TDOUBLE)
|
|
|
|
data = (gdouble *) g_malloc (width * height * sizeof (gdouble) *
|
|
|
|
channelnum);
|
|
|
|
else
|
|
|
|
data = (guchar *) g_malloc (width * height * sizeof (guchar *) *
|
|
|
|
(bitpix / 8) * channelnum);
|
2013-01-11 00:49:31 +01:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
/* CFITSIO can't overwrite files unless you start the filename
|
|
|
|
* with a "!". Instead, we'll try to open the existing file
|
|
|
|
* in READWRITE mode, clear it, and then recreate it.
|
|
|
|
*/
|
|
|
|
if (fits_create_file (&fptr, g_file_peek_path (file), &status))
|
|
|
|
{
|
|
|
|
/* You have to set status back to 0 - subsequent successful
|
|
|
|
functions do not remove the error value */
|
|
|
|
status = 0;
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
if (fits_open_file (&fptr, g_file_peek_path (file), READWRITE, &status))
|
|
|
|
{
|
|
|
|
show_fits_errors (status);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
fits_delete_file (fptr, &status);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
if (fits_create_file (&fptr, g_file_peek_path (file), &status))
|
|
|
|
{
|
|
|
|
show_fits_errors (status);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
if (fits_create_img (fptr, bitpix, naxis, naxes, &status))
|
|
|
|
{
|
|
|
|
show_fits_errors (status);
|
|
|
|
return FALSE;
|
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
/* FITS uses signed 16/32 integers, so we need to convert the unsigned
|
|
|
|
* values to that range via an offset */
|
|
|
|
if (bitpix == 16 || bitpix == 32)
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
GeglBufferIterator *iter;
|
|
|
|
|
|
|
|
iter = gegl_buffer_iterator_new (buffer,
|
|
|
|
GEGL_RECTANGLE (0, 0, width, height), 0,
|
|
|
|
format,
|
|
|
|
GEGL_BUFFER_READWRITE,
|
|
|
|
GEGL_ABYSS_NONE, 1);
|
|
|
|
|
|
|
|
while (gegl_buffer_iterator_next (iter))
|
2013-01-10 23:17:57 +01:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
gint length = iter->length;
|
|
|
|
|
|
|
|
if (bitpix == 16)
|
2013-01-10 23:07:35 +01:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
gushort *pixel = iter->items[0].data;
|
|
|
|
gushort offset = pow (2, 15);
|
2013-01-10 23:07:35 +01:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
while (length--)
|
|
|
|
{
|
|
|
|
for (gint i = 0; i < channelnum; i++)
|
|
|
|
pixel[i] -= offset;
|
2013-01-10 23:07:35 +01:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
pixel += channelnum;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
guint32 *pixel = iter->items[0].data;
|
|
|
|
guint32 offset = pow (2, 31);
|
|
|
|
|
|
|
|
while (length--)
|
|
|
|
{
|
|
|
|
for (gint i = 0; i < channelnum; i++)
|
|
|
|
pixel[i] -= offset;
|
2013-01-10 23:07:35 +01:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
pixel += channelnum;
|
|
|
|
}
|
2013-01-10 23:07:35 +01:00
|
|
|
}
|
2023-03-06 15:45:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Grayscale images can be exported as-is. RGB images must be
|
|
|
|
* converted into planes of RRR...GGG...BBB... */
|
|
|
|
if (naxis == 2)
|
|
|
|
{
|
|
|
|
gegl_buffer_get (buffer,
|
|
|
|
GEGL_RECTANGLE (0, 0, width, height), 1.0,
|
|
|
|
format, data,
|
|
|
|
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
2000-01-25 17:46:56 +00:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
if (fits_write_img (fptr, export_type, 1, nelements, data, &status))
|
|
|
|
{
|
|
|
|
show_fits_errors (status);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gdouble *rgb_data;
|
|
|
|
gdouble *rgb_output;
|
|
|
|
const Babl *rgb_format;
|
|
|
|
const Babl *output_format = babl_format ("Y' double");
|
|
|
|
const Babl *converted_format;
|
|
|
|
|
|
|
|
rgb_format = (channelnum == 3) ? babl_format ("R'G'B' double") :
|
|
|
|
babl_format ("R'G'B'A double");
|
|
|
|
|
|
|
|
/* We export a single channel at a time, so we need a
|
|
|
|
* an output format with a single channel */
|
|
|
|
converted_format = babl_format_new (babl_model ("Y'"), type,
|
|
|
|
babl_component ("Y'"),
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
rgb_data = (gdouble *) g_malloc (width * height * sizeof (gdouble) *
|
|
|
|
channelnum);
|
|
|
|
rgb_output = (gdouble *) g_malloc (width * height * sizeof (gdouble));
|
|
|
|
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, width, height), 1.0,
|
|
|
|
rgb_format, rgb_data,
|
|
|
|
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
|
|
|
|
|
|
|
for (gint rgb = 0; rgb < channelnum; rgb++)
|
|
|
|
{
|
|
|
|
gint offset = 0;
|
|
|
|
gint src_offset = 0;
|
|
|
|
void *converted_output = NULL;
|
|
|
|
|
|
|
|
for (gint i = 0; i < height - 1; i++)
|
2013-01-10 23:17:57 +01:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
for (gint j = 0; j < (width * channelnum); j += channelnum)
|
|
|
|
{
|
|
|
|
rgb_output[(j / channelnum) + offset] =
|
|
|
|
rgb_data[j + src_offset + rgb];
|
|
|
|
}
|
|
|
|
|
|
|
|
src_offset += width * channelnum;
|
|
|
|
offset += width;
|
2013-01-10 23:17:57 +01:00
|
|
|
}
|
2023-03-06 15:45:45 +00:00
|
|
|
|
|
|
|
if (export_type == TFLOAT)
|
|
|
|
converted_output = (gfloat *) g_malloc (width * height *
|
|
|
|
sizeof (gfloat));
|
|
|
|
else if (export_type == TDOUBLE)
|
|
|
|
converted_output = (gdouble *) g_malloc (width * height *
|
|
|
|
sizeof (gdouble));
|
|
|
|
else
|
|
|
|
converted_output = (guchar *) g_malloc (width * height *
|
|
|
|
sizeof (guchar *) *
|
|
|
|
(bitpix / 8));
|
|
|
|
|
|
|
|
babl_process (babl_fish (output_format, converted_format),
|
|
|
|
rgb_output, converted_output, nelements);
|
|
|
|
|
|
|
|
if (fits_write_img (fptr, export_type, 1, nelements,
|
|
|
|
converted_output, &status))
|
2013-01-10 23:17:57 +01:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
show_fits_errors (status);
|
|
|
|
return FALSE;
|
2013-01-10 23:17:57 +01:00
|
|
|
}
|
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
g_free (converted_output);
|
2013-01-10 23:17:57 +01:00
|
|
|
}
|
2013-01-10 23:07:35 +01:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
g_free (rgb_data);
|
|
|
|
g_free (rgb_output);
|
2000-01-25 17:46:56 +00:00
|
|
|
}
|
1997-11-24 22:05:25 +00:00
|
|
|
|
|
|
|
g_free (data);
|
|
|
|
|
2013-01-11 00:49:31 +01:00
|
|
|
g_object_unref (buffer);
|
2013-01-10 23:07:35 +01:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
/* Add history of file update */
|
|
|
|
fits_write_history (fptr,
|
|
|
|
"THIS FITS FILE WAS GENERATED BY GIMP USING CFITSIO",
|
|
|
|
&status);
|
|
|
|
|
2011-04-10 19:05:08 +02:00
|
|
|
gimp_progress_update (1.0);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2023-03-06 15:45:45 +00:00
|
|
|
if (fits_close_file (fptr, &status))
|
2000-01-25 17:46:56 +00:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
show_fits_errors (status);
|
2013-01-10 23:07:35 +01:00
|
|
|
return FALSE;
|
2000-01-25 17:46:56 +00:00
|
|
|
}
|
2013-01-10 23:07:35 +01:00
|
|
|
|
|
|
|
return TRUE;
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
|
|
|
|
2013-01-11 00:49:31 +01:00
|
|
|
|
1997-11-24 22:05:25 +00:00
|
|
|
/* Load interface functions */
|
|
|
|
|
2004-05-20 15:51:25 +00:00
|
|
|
static gboolean
|
2019-10-03 16:00:17 +02:00
|
|
|
load_dialog (GimpProcedure *procedure,
|
|
|
|
GObject *config)
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
2024-07-14 04:12:26 +00:00
|
|
|
GtkWidget *dialog;
|
|
|
|
GtkWidget *frame;
|
|
|
|
gboolean run;
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-09-20 19:39:00 +02:00
|
|
|
gimp_ui_init (PLUG_IN_BINARY);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-10-03 16:00:17 +02:00
|
|
|
dialog = gimp_procedure_dialog_new (procedure,
|
|
|
|
GIMP_PROCEDURE_CONFIG (config),
|
|
|
|
_("Open FITS File"));
|
2007-05-15 11:58:56 +00:00
|
|
|
|
2024-07-14 04:12:26 +00:00
|
|
|
frame = gimp_procedure_dialog_get_widget (GIMP_PROCEDURE_DIALOG (dialog),
|
|
|
|
"replace", GIMP_TYPE_INT_RADIO_FRAME);
|
2023-03-06 15:45:45 +00:00
|
|
|
gtk_widget_set_margin_bottom (frame, 12);
|
|
|
|
|
|
|
|
gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog),
|
2013-01-10 23:17:57 +01:00
|
|
|
NULL);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2000-01-25 17:46:56 +00:00
|
|
|
gtk_widget_show (dialog);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2019-10-03 16:00:17 +02:00
|
|
|
run = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog));
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2003-11-06 15:27:05 +00:00
|
|
|
gtk_widget_destroy (dialog);
|
1997-11-24 22:05:25 +00:00
|
|
|
|
2003-11-06 15:27:05 +00:00
|
|
|
return run;
|
1997-11-24 22:05:25 +00:00
|
|
|
}
|
|
|
|
|
2000-01-25 17:46:56 +00:00
|
|
|
static void
|
2023-03-06 15:45:45 +00:00
|
|
|
show_fits_errors (gint status)
|
1997-11-24 22:05:25 +00:00
|
|
|
{
|
2023-03-06 15:45:45 +00:00
|
|
|
gchar status_str[FLEN_STATUS];
|
1997-11-24 22:05:25 +00:00
|
|
|
|
|
|
|
/* Write out error messages of FITS-Library */
|
2023-03-06 15:45:45 +00:00
|
|
|
fits_get_errstatus (status, status_str);
|
|
|
|
g_message ("FITS: %s\n", status_str);
|
2023-07-21 18:08:00 +02:00
|
|
|
}
|