fits.c 38.3 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
Elliot Lee's avatar
Elliot Lee committed
2
3
4
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 * FITS file plugin
 * reading and writing code Copyright (C) 1997 Peter Kirchgessner
5
 * e-mail: peter@kirchgessner.net, WWW: http://www.kirchgessner.net
Elliot Lee's avatar
Elliot Lee committed
6
 *
7
 * This program is free software: you can redistribute it and/or modify
Elliot Lee's avatar
Elliot Lee committed
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
Elliot Lee's avatar
Elliot Lee committed
10
11
12
13
14
15
16
17
 * (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
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
19
20
21
22
23
24
25
26
27
 *
 */

/* 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
28
 * V 1.05, nn, 20-Dec-97: Initialize image_ID in run()
29
30
31
 * V 1.06, PK, 21-Nov-99: Internationalization
 *                        Fix bug with gimp_export_image()
 *                        (moved it from load to save)
32
33
34
 * 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
Elliot Lee's avatar
Elliot Lee committed
35
 */
36

Sven Neumann's avatar
Sven Neumann committed
37
38
#include "config.h"

Elliot Lee's avatar
Elliot Lee committed
39
#include <string.h>
40
#include <errno.h>
Sven Neumann's avatar
Sven Neumann committed
41

42
#include <glib/gstdio.h>
43
44
45
46

#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

47
#include "fits-io.h"
Elliot Lee's avatar
Elliot Lee committed
48

49
50
#include "libgimp/stdplugins-intl.h"

51

52
53
#define LOAD_PROC      "file-fits-load"
#define SAVE_PROC      "file-fits-save"
54
#define PLUG_IN_BINARY "file-fits"
55
#define PLUG_IN_ROLE   "gimp-file-fits"
56
57


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
typedef struct _Fits      Fits;
typedef struct _FitsClass FitsClass;

struct _Fits
{
  GimpPlugIn      parent_instance;
};

struct _FitsClass
{
  GimpPlugInClass parent_class;
};


#define FITS_TYPE  (fits_get_type ())
#define FITS (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FITS_TYPE, Fits))

GType                   fits_get_type         (void) G_GNUC_CONST;

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,
                                               const GimpValueArray *args,
                                               gpointer              run_data);
static GimpValueArray * fits_save             (GimpProcedure        *procedure,
                                               GimpRunMode           run_mode,
                                               GimpImage            *image,
89
90
                                               gint                  n_drawables,
                                               GimpDrawable        **drawables,
91
92
93
94
                                               GFile                *file,
                                               const GimpValueArray *args,
                                               gpointer              run_data);

95
static GimpImage      * load_image            (GFile              *file,
96
97
                                               GObject            *config,
                                               GimpRunMode         run_mode,
98
                                               GError            **error);
99
static gint             save_image            (GFile              *file,
100
101
102
103
104
105
106
107
108
109
110
111
112
113
                                               GimpImage          *image,
                                               GimpDrawable       *drawable,
                                               GError            **error);

static FitsHduList    * create_fits_header    (FitsFile           *ofp,
                                               guint               width,
                                               guint               height,
                                               guint               channels,
                                               guint               bitpix);

static gint             save_fits             (FitsFile           *ofp,
                                               GimpImage          *image,
                                               GimpDrawable       *drawable);

114
static GimpImage      * create_new_image      (GFile              *file,
115
116
117
118
119
120
121
122
123
                                               guint               pagenum,
                                               guint               width,
                                               guint               height,
                                               GimpImageBaseType   itype,
                                               GimpImageType       dtype,
                                               GimpPrecision       iprecision,
                                               GimpLayer         **layer,
                                               GeglBuffer        **buffer);

124
static GimpImage      * load_fits             (GFile              *file,
125
                                               FitsFile           *ifp,
126
                                               GObject            *config,
127
128
129
                                               guint               picnum,
                                               guint               ncompose);

130
131
static gboolean         load_dialog           (GimpProcedure      *procedure,
                                               GObject            *config);
132
133
134
135
136
137
static void             show_fits_errors      (void);


G_DEFINE_TYPE (Fits, fits, GIMP_TYPE_PLUG_IN)

GIMP_MAIN (FITS_TYPE)
Elliot Lee's avatar
Elliot Lee committed
138
139


140
141
142
143
144
145
146
147
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;
}
Elliot Lee's avatar
Elliot Lee committed
148
149

static void
150
151
152
fits_init (Fits *fits)
{
}
Elliot Lee's avatar
Elliot Lee committed
153

154
155
static GList *
fits_query_procedures (GimpPlugIn *plug_in)
Elliot Lee's avatar
Elliot Lee committed
156
{
157
  GList *list = NULL;
Elliot Lee's avatar
Elliot Lee committed
158

159
160
  list = g_list_append (list, g_strdup (LOAD_PROC));
  list = g_list_append (list, g_strdup (SAVE_PROC));
Elliot Lee's avatar
Elliot Lee committed
161

162
  return list;
Elliot Lee's avatar
Elliot Lee committed
163
164
}

165
166
167
168
169
static GimpProcedure *
fits_create_procedure (GimpPlugIn  *plug_in,
                       const gchar *name)
{
  GimpProcedure *procedure = NULL;
Elliot Lee's avatar
Elliot Lee committed
170

171
172
  if (! strcmp (name, LOAD_PROC))
    {
173
174
      procedure = gimp_load_procedure_new (plug_in, name,
                                           GIMP_PDB_PROC_TYPE_PLUGIN,
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
                                           fits_load, NULL, NULL);

      gimp_procedure_set_menu_label (procedure,
                                     N_("Flexible Image Transport System"));

      gimp_procedure_set_documentation (procedure,
                                        "Load file of the FITS file format",
                                        "Load file of the FITS file format "
                                        "(Flexible Image Transport System)",
                                        name);
      gimp_procedure_set_attribution (procedure,
                                      "Peter Kirchgessner",
                                      "Peter Kirchgessner (peter@kirchgessner.net)",
                                      "1997");

      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");
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

      GIMP_PROC_AUX_ARG_INT (procedure, "replace",
                             "Replace",
                             "Replacement for undefined pixels",
                             0, 255, 0,
                             G_PARAM_READWRITE);

      GIMP_PROC_AUX_ARG_BOOLEAN (procedure, "use-data-min-max",
                                 "Use data min max",
                                 "Use DATAMIN/DATAMAX-scaling if possible",
                                 FALSE,
                                 G_PARAM_READWRITE);

      GIMP_PROC_AUX_ARG_BOOLEAN (procedure, "compose",
                                 "Compose",
                                 "Image composing",
                                 FALSE,
                                 G_PARAM_READWRITE);
214
215
216
    }
  else if (! strcmp (name, SAVE_PROC))
    {
217
218
      procedure = gimp_save_procedure_new (plug_in, name,
                                           GIMP_PDB_PROC_TYPE_PLUGIN,
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
                                           fits_save, NULL, NULL);

      gimp_procedure_set_image_types (procedure, "RGB, GRAY, INDEXED");

      gimp_procedure_set_menu_label (procedure,
                                     N_("Flexible Image Transport System"));

      gimp_procedure_set_documentation (procedure,
                                        "Export file in the FITS file format",
                                        "FITS exporting handles all image "
                                        "types except those with alpha channels.",
                                        name);
      gimp_procedure_set_attribution (procedure,
                                      "Peter Kirchgessner",
                                      "Peter Kirchgessner (peter@kirchgessner.net)",
                                      "1997");

      gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
                                          "image/x-fits");
      gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
                                          "fit,fits");
    }

  return procedure;
}

static GimpValueArray *
fits_load (GimpProcedure        *procedure,
247
248
249
250
           GimpRunMode           run_mode,
           GFile                *file,
           const GimpValueArray *args,
           gpointer              run_data)
Elliot Lee's avatar
Elliot Lee committed
251
{
252
253
254
255
  GimpProcedureConfig *config;
  GimpValueArray      *return_vals;
  GimpImage           *image;
  GError              *error = NULL;
Elliot Lee's avatar
Elliot Lee committed
256

257
  INIT_I18N ();
258
259
  gegl_init (NULL, NULL);

260
261
  config = gimp_procedure_create_config (procedure);
  gimp_procedure_config_begin_run (config, NULL, run_mode, args);
262

263
  if (run_mode == GIMP_RUN_INTERACTIVE)
Elliot Lee's avatar
Elliot Lee committed
264
    {
265
      if (! load_dialog (procedure, G_OBJECT (config)))
266
267
268
269
        return gimp_procedure_new_return_values (procedure,
                                                 GIMP_PDB_CANCEL,
                                                 NULL);
    }
270

271
  image = load_image (file, G_OBJECT (config), run_mode, &error);
272

273
274
  /* Write out error messages of FITS-Library */
  show_fits_errors ();
275

276
277
278
279
  if (! image)
    return gimp_procedure_new_return_values (procedure,
                                             GIMP_PDB_EXECUTION_ERROR,
                                             error);
280

281
282
  gimp_procedure_config_end_run (config, GIMP_PDB_SUCCESS);
  g_object_unref (config);
Elliot Lee's avatar
Elliot Lee committed
283

284
285
286
  return_vals = gimp_procedure_new_return_values (procedure,
                                                  GIMP_PDB_SUCCESS,
                                                  NULL);
Elliot Lee's avatar
Elliot Lee committed
287

288
  GIMP_VALUES_SET_IMAGE (return_vals, 1, image);
Elliot Lee's avatar
Elliot Lee committed
289

290
291
  return return_vals;
}
Elliot Lee's avatar
Elliot Lee committed
292

293
294
static GimpValueArray *
fits_save (GimpProcedure        *procedure,
295
296
           GimpRunMode           run_mode,
           GimpImage            *image,
297
298
           gint                  n_drawables,
           GimpDrawable        **drawables,
299
300
301
           GFile                *file,
           const GimpValueArray *args,
           gpointer              run_data)
302
303
304
305
{
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
  GimpExportReturn   export = GIMP_EXPORT_CANCEL;
  GError            *error = NULL;
306

307
308
  INIT_I18N ();
  gegl_init (NULL, NULL);
309

310
  switch (run_mode)
311
    {
312
313
    case GIMP_RUN_INTERACTIVE:
    case GIMP_RUN_WITH_LAST_VALS:
314
      gimp_ui_init (PLUG_IN_BINARY);
315

316
      export = gimp_export_image (&image, &n_drawables, &drawables, "FITS",
317
318
319
320
321
322
323
324
325
326
327
328
                                  GIMP_EXPORT_CAN_HANDLE_RGB  |
                                  GIMP_EXPORT_CAN_HANDLE_GRAY |
                                  GIMP_EXPORT_CAN_HANDLE_INDEXED);

      if (export == GIMP_EXPORT_CANCEL)
        return gimp_procedure_new_return_values (procedure,
                                                 GIMP_PDB_CANCEL,
                                                 NULL);
      break;

    default:
      break;
329
330
    }

331
332
333
334
335
336
337
338
339
340
341
  if (n_drawables != 1)
    {
      g_set_error (&error, G_FILE_ERROR, 0,
                   _("FITS format does not support multiple layers."));

      return gimp_procedure_new_return_values (procedure,
                                               GIMP_PDB_CALLING_ERROR,
                                               error);
    }

  if (! save_image (file, image, drawables[0], &error))
342
    {
343
      status = GIMP_PDB_EXECUTION_ERROR;
344
345
    }

346
  if (export == GIMP_EXPORT_EXPORT)
347
348
349
350
    {
      gimp_image_delete (image);
      g_free (drawables);
    }
Elliot Lee's avatar
Elliot Lee committed
351

352
353
  return gimp_procedure_new_return_values (procedure, status, error);
}
Elliot Lee's avatar
Elliot Lee committed
354

355
static GimpImage *
356
357
358
359
load_image (GFile        *file,
            GObject      *config,
            GimpRunMode   run_mode,
            GError      **error)
360
{
361
362
363
  GimpImage   *image;
  GimpImage  **image_list;
  GimpImage  **nl;
364
365
366
367
368
369
  guint        picnum;
  gint         k, n_images, max_images, hdu_picnum;
  gint         compose;
  FILE        *fp;
  FitsFile    *ifp;
  FitsHduList *hdu;
370
371
372
373
374
  gboolean     compose_arg;

  g_object_get (config,
                "compose", &compose_arg,
                NULL);
375

376
  fp = g_fopen (g_file_peek_path (file), "rb");
377
378

  if (! fp)
379
    {
380
381
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Could not open '%s' for reading: %s"),
382
                   gimp_file_get_utf8_name (file), g_strerror (errno));
383
      return NULL;
384
    }
385

386
387
  fclose (fp);

388
  ifp = fits_open (g_file_peek_path (file), "r");
389
390

  if (! ifp)
391
    {
392
393
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   "%s", _("Error during open of FITS file"));
394
      return NULL;
395
    }
396

397
398
  if (ifp->n_pic <= 0)
    {
399
400
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   "%s", _("FITS file keeps no displayable images"));
401
      fits_close (ifp);
402
      return NULL;
403
404
    }

405
  image_list = g_new (GimpImage *, 10);
406
407
408
409
410
411
412
  n_images = 0;
  max_images = 10;

  for (picnum = 1; picnum <= ifp->n_pic; )
    {
      /* Get image info to see if we can compose them */
      hdu = fits_image_info (ifp, picnum, &hdu_picnum);
413
414
      if (hdu == NULL)
        break;
415
416

      /* Get number of FITS-images to compose */
417
      compose = (compose_arg && (hdu_picnum == 1) && (hdu->naxis == 3) &&
418
419
                 (hdu->naxisn[2] > 1) && (hdu->naxisn[2] <= 4));

420
      if (compose)
421
        compose = hdu->naxisn[2];
422
      else
423
        compose = 1;  /* Load as GRAY */
424

425
      image = load_fits (file, ifp, config, picnum, compose);
426
427
428
429

      /* Write out error messages of FITS-Library */
      show_fits_errors ();

430
431
432
      if (! image)
        break;

433
      if (n_images == max_images)
434
        {
435
436
437
438
439
          nl = (GimpImage **) g_realloc (image_list,
                                         (max_images + 10) * sizeof (GimpImage *));
          if (nl == NULL)
            break;

440
441
442
          image_list = nl;
          max_images += 10;
        }
443
444

      image_list[n_images++] = image;
445
446
447
448
449
450
451
452
453
454

      picnum += compose;
    }

  /* Write out error messages of FITS-Library */
  show_fits_errors ();

  fits_close (ifp);

  /* Display images in reverse order. The last will be displayed by GIMP itself*/
455
  if (run_mode != GIMP_RUN_NONINTERACTIVE)
456
457
    {
      for (k = n_images-1; k >= 1; k--)
458
459
460
461
462
        {
          gimp_image_undo_enable (image_list[k]);
          gimp_image_clean_all (image_list[k]);
          gimp_display_new (image_list[k]);
        }
463
464
    }

465
  image = (n_images > 0) ? image_list[0] : NULL;
466
467
  g_free (image_list);

468
  return image;
Elliot Lee's avatar
Elliot Lee committed
469
470
471
}

static gint
472
save_image (GFile         *file,
473
474
475
            GimpImage     *image,
            GimpDrawable  *drawable,
            GError       **error)
Elliot Lee's avatar
Elliot Lee committed
476
{
477
  FitsFile      *ofp;
478
479
  GimpImageType  drawable_type;
  gint           retval;
Elliot Lee's avatar
Elliot Lee committed
480

481
  drawable_type = gimp_drawable_type (drawable);
Elliot Lee's avatar
Elliot Lee committed
482

483
  /*  Make sure we're not exporting an image with an alpha channel  */
484
  if (gimp_drawable_has_alpha (drawable))
485
    {
486
487
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   "%s",
488
                   _("FITS export cannot handle images with alpha channels"));
489
490
      return FALSE;
    }
Elliot Lee's avatar
Elliot Lee committed
491
492

  switch (drawable_type)
493
    {
494
495
496
    case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE:
    case GIMP_GRAY_IMAGE:    case GIMP_GRAYA_IMAGE:
    case GIMP_RGB_IMAGE:     case GIMP_RGBA_IMAGE:
Elliot Lee's avatar
Elliot Lee committed
497
498
      break;
    default:
499
      g_message (_("Cannot operate on unknown image types."));
Elliot Lee's avatar
Elliot Lee committed
500
501
      return (FALSE);
      break;
502
    }
Elliot Lee's avatar
Elliot Lee committed
503

504
  gimp_progress_init_printf (_("Exporting '%s'"),
505
                             gimp_file_get_utf8_name (file));
506

Elliot Lee's avatar
Elliot Lee committed
507
  /* Open the output file. */
508
  ofp = fits_open (g_file_peek_path (file), "w");
509
510

  if (! ofp)
511
    {
512
513
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Could not open '%s' for writing: %s"),
514
                   gimp_file_get_utf8_name (file), g_strerror (errno));
515
516
      return (FALSE);
    }
Elliot Lee's avatar
Elliot Lee committed
517

518
  retval = save_fits (ofp, image, drawable);
Elliot Lee's avatar
Elliot Lee committed
519
520
521

  fits_close (ofp);

522
  return retval;
Elliot Lee's avatar
Elliot Lee committed
523
524
525
}

/* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
526
static GimpImage *
527
create_new_image (GFile              *file,
528
529
530
531
532
                  guint               pagenum,
                  guint               width,
                  guint               height,
                  GimpImageBaseType   itype,
                  GimpImageType       dtype,
533
                  GimpPrecision       iprecision,
534
                  GimpLayer         **layer,
535
                  GeglBuffer        **buffer)
536
{
537
  GimpImage *image;
538
539
540
  GFile     *new_file;
  gchar     *uri;
  gchar     *new_uri;
Elliot Lee's avatar
Elliot Lee committed
541

542
  image = gimp_image_new_with_precision (width, height, itype, iprecision);
543

544
545
546
547
548
549
550
551
552
553
  uri = g_file_get_uri (file);

  new_uri = g_strdup_printf ("%s-img%d", uri, pagenum);
  g_free (uri);

  new_file = g_file_new_for_uri (new_uri);
  g_free (new_uri);

  gimp_image_set_file (image, new_file);
  g_object_unref (new_file);
Elliot Lee's avatar
Elliot Lee committed
554

555
556
557
558
559
  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);
Elliot Lee's avatar
Elliot Lee committed
560

561
  *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (*layer));
Elliot Lee's avatar
Elliot Lee committed
562

563
  return image;
Elliot Lee's avatar
Elliot Lee committed
564
565
566
}


567
568
569
570
/* Load FITS image. ncompose gives the number of FITS-images which have
 * to be composed together. This will result in different GIMP image types:
 * 1: GRAY, 2: GRAYA, 3: RGB, 4: RGBA
 */
571
static GimpImage *
572
573
load_fits (GFile    *file,
           FitsFile *ifp,
574
           GObject  *config,
575
576
           guint     picnum,
           guint     ncompose)
577
{
578
579
580
581
582
  register guchar   *dest, *src;
  guchar            *data, *data_end, *linebuf;
  int                width, height, tile_height, scan_lines;
  int                i, j, max_scan;
  double             a, b;
583
584
  GimpImage         *image;
  GimpLayer         *layer;
585
586
587
  GeglBuffer        *buffer;
  GimpImageBaseType  itype;
  GimpImageType      dtype;
588
  GimpPrecision      iprecision;
589
590
591
  gint               err = 0;
  FitsHduList       *hdulist;
  FitsPixTransform   trans;
592
593
  double             datamax, replacetransform;
  const Babl        *type, *format;
594
595
596
597
598
599
600
  gint               replace;
  gboolean           use_datamin;

  g_object_get (config,
                "replace",          &replace,
                "use-data-min-max", &use_datamin,
                NULL);
601
602

  hdulist = fits_seek_image (ifp, (int)picnum);
603
  if (hdulist == NULL)
604
    return NULL;
605

606
  width  = hdulist->naxisn[0];  /* Set the size of the FITS image */
607
608
  height = hdulist->naxisn[1];

609
610
611
  switch (hdulist->bitpix)
    {
    case 8:
612
      iprecision = GIMP_PRECISION_U8_NON_LINEAR;
613
614
615
616
617
      type = babl_type ("u8");
      datamax = 255.0;
      replacetransform = 1.0;
      break;
    case 16:
618
      iprecision = GIMP_PRECISION_U16_NON_LINEAR; /* FIXME precision */
619
620
621
622
623
      type = babl_type ("u16");
      datamax = 65535.0;
      replacetransform = 257;
      break;
    case 32:
624
      iprecision = GIMP_PRECISION_U32_LINEAR;
625
626
627
628
629
      type = babl_type ("u32");
      datamax = 4294967295.0;
      replacetransform = 16843009;
      break;
    case -32:
630
      iprecision = GIMP_PRECISION_FLOAT_LINEAR;
631
632
633
634
635
      type = babl_type ("float");
      datamax = 1.0;
      replacetransform = 1.0 / 255.0;
      break;
    case -64:
636
      iprecision = GIMP_PRECISION_DOUBLE_LINEAR;
637
638
639
640
641
      type = babl_type ("double");
      datamax = 1.0;
      replacetransform = 1.0 / 255.0;
      break;
    default:
642
      return NULL;
643
644
    }

645
646
647
648
  if (ncompose == 2)
    {
      itype = GIMP_GRAY;
      dtype = GIMP_GRAYA_IMAGE;
649
650
651
652
653
      format = babl_format_new (babl_model ("Y'A"),
                                type,
                                babl_component ("Y'"),
                                babl_component ("A"),
                                NULL);
654
655
656
657
658
    }
  else if (ncompose == 3)
    {
      itype = GIMP_RGB;
      dtype = GIMP_RGB_IMAGE;
659
660
661
662
663
664
      format = babl_format_new (babl_model ("R'G'B'"),
                                type,
                                babl_component ("R'"),
                                babl_component ("G'"),
                                babl_component ("B'"),
                                NULL);
665
666
667
668
669
    }
  else if (ncompose == 4)
    {
      itype = GIMP_RGB;
      dtype = GIMP_RGBA_IMAGE;
670
671
672
673
674
675
676
      format = babl_format_new (babl_model ("R'G'B'A"),
                                type,
                                babl_component ("R'"),
                                babl_component ("G'"),
                                babl_component ("B'"),
                                babl_component ("A"),
                                NULL);
677
678
679
680
681
682
    }
  else
    {
      ncompose = 1;
      itype = GIMP_GRAY;
      dtype = GIMP_GRAY_IMAGE;
683
684
685
686
      format = babl_format_new (babl_model ("Y'"),
                                type,
                                babl_component ("Y'"),
                                NULL);
687
    }
688

689
  image = create_new_image (file, picnum, width, height,
690
691
                            itype, dtype, iprecision,
                            &layer, &buffer);
692
693

  tile_height = gimp_tile_height ();
694

695
  data = g_malloc (tile_height * width * ncompose * hdulist->bpp);
696
  if (data == NULL)
697
    return NULL;
698

699
  data_end = data + tile_height * width * ncompose * hdulist->bpp;
700

701
702
703
  /* If the transformation from pixel value to data value has been
   * specified, use it
   */
704
  if (use_datamin &&
705
706
      hdulist->used.datamin && hdulist->used.datamax &&
      hdulist->used.bzero   && hdulist->used.bscale)
707
708
709
    {
      a = (hdulist->datamin - hdulist->bzero) / hdulist->bscale;
      b = (hdulist->datamax - hdulist->bzero) / hdulist->bscale;
710
711
712
713
714

      if (a < b)
        trans.pixmin = a, trans.pixmax = b;
      else
        trans.pixmin = b, trans.pixmax = a;
715
716
717
718
719
720
    }
  else
    {
      trans.pixmin = hdulist->pixmin;
      trans.pixmax = hdulist->pixmax;
    }
721
722

  trans.datamin     = 0.0;
723
  trans.datamax     = datamax;
724
  trans.replacement = replace * replacetransform;
725
  trans.dsttyp      = 'k';
726

727
728
729
  /* FITS stores images with bottom row first. Therefore we have to
   * fill the image from bottom to top.
   */
730
731
732

  if (ncompose == 1)
    {
733
      dest = data + tile_height * width * hdulist->bpp;
734
735
736
      scan_lines = 0;

      for (i = 0; i < height; i++)
737
738
        {
          /* Read FITS line */
739
          dest -= width * hdulist->bpp;
740
741
742
743
744
745
746
747
748
749
750
          if (fits_read_pixel (ifp, hdulist, width, &trans, dest) != width)
            {
              err = 1;
              break;
            }

          scan_lines++;

          if ((i % 20) == 0)
            gimp_progress_update ((gdouble) (i + 1) / (gdouble) height);

751
          if ((scan_lines == tile_height) || ((i + 1) == height))
752
            {
753
754
755
              gegl_buffer_set (buffer,
                               GEGL_RECTANGLE (0, height - i - 1,
                                               width, scan_lines), 0,
756
                               format, dest, GEGL_AUTO_ROWSTRIDE);
757

758
              scan_lines = 0;
759
              dest = data + tile_height * width * hdulist->bpp;
760
761
762
            }

          if (err)
763
            break;
764
        }
765
766
767
    }
  else   /* multiple images to compose */
    {
768
769
      gint channel;

770
      linebuf = g_malloc (width * hdulist->bpp);
771
      if (linebuf == NULL)
772
        return NULL;
773
774

      for (channel = 0; channel < ncompose; channel++)
775
        {
776
          dest = data + tile_height * width * hdulist->bpp * ncompose + channel * hdulist->bpp;
777
778
779
780
781
782
          scan_lines = 0;

          for (i = 0; i < height; i++)
            {
              if ((channel > 0) && ((i % tile_height) == 0))
                {
783
                  /* Reload a region for follow up channels */
784
                  max_scan = tile_height;
785

786
                  if (i + tile_height > height)
787
788
                    max_scan = height - i;

789
790
791
                  gegl_buffer_get (buffer,
                                   GEGL_RECTANGLE (0, height - i - max_scan,
                                                   width, max_scan), 1.0,
792
                                   format, data_end - max_scan * width * hdulist->bpp * ncompose,
793
                                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
794
795
796
                }

              /* Read FITS scanline */
797
              dest -= width * ncompose * hdulist->bpp;
798
799
800
801
802
803
804
805
806
              if (fits_read_pixel (ifp, hdulist, width, &trans, linebuf) != width)
                {
                  err = 1;
                  break;
                }
              j = width;
              src = linebuf;
              while (j--)
                {
807
808
809
                  memcpy (dest, src, hdulist->bpp);
                  src += hdulist->bpp;
                  dest += ncompose * hdulist->bpp;
810
                }
811
              dest -= width * ncompose * hdulist->bpp;
812
813
814
815
              scan_lines++;

              if ((i % 20) == 0)
                gimp_progress_update ((gdouble) (channel * height + i + 1) /
816
                                      (gdouble) (height * ncompose));
817

818
819
              if ((scan_lines == tile_height) || ((i + 1) == height))
                {
820
821
822
                  gegl_buffer_set (buffer,
                                   GEGL_RECTANGLE (0, height - i - 1,
                                                   width, scan_lines), 0,
823
                                   format, dest - channel * hdulist->bpp, GEGL_AUTO_ROWSTRIDE);
824

825
                  scan_lines = 0;
826
                  dest = data + tile_height * width * ncompose * hdulist->bpp + channel * hdulist->bpp;
827
                }
828

829
              if (err)
830
                break;
831
832
            }
        }
833

834
835
      g_free (linebuf);
    }
836

837
838
839
840
841
  g_free (data);

  if (err)
    g_message (_("EOF encountered on reading"));

842
843
844
  g_object_unref (buffer);

  gimp_progress_update (1.0);
845

846
  return err ? NULL : image;
Elliot Lee's avatar
Elliot Lee committed
847
848
849
}


850
851
852
853
static FitsHduList *
create_fits_header (FitsFile *ofp,
                    guint     width,
                    guint     height,
854
855
                    guint     channels,
                    guint     bitpix)
856
{
857
858
  FitsHduList *hdulist;
  gint         print_ctype3 = 0; /* The CTYPE3-card may not be FITS-conforming */
859

860
  static const char *ctype3_card[] =
861
862
  {
    NULL, NULL, NULL,  /* bpp = 0: no additional card */
863
    "COMMENT Image type within GIMP: GIMP_GRAY_IMAGE",
864
865
    NULL,
    NULL,
866
    "COMMENT Image type within GIMP: GIMP_GRAYA_IMAGE (gray with alpha channel)",
867
868
    "COMMENT Sequence for NAXIS3   : GRAY, ALPHA",
    "CTYPE3  = 'GRAYA   '           / GRAY IMAGE WITH ALPHA CHANNEL",
869
    "COMMENT Image type within GIMP: GIMP_RGB_IMAGE",
870
871
    "COMMENT Sequence for NAXIS3   : RED, GREEN, BLUE",
    "CTYPE3  = 'RGB     '           / RGB IMAGE",
872
    "COMMENT Image type within GIMP: GIMP_RGBA_IMAGE (rgb with alpha channel)",
873
874
875
876
877
    "COMMENT Sequence for NAXIS3   : RED, GREEN, BLUE, ALPHA",
    "CTYPE3  = 'RGBA    '           / RGB IMAGE WITH ALPHA CHANNEL"
  };

  hdulist = fits_add_hdu (ofp);
878
879
880
881
  if (hdulist == NULL)
    return NULL;

  hdulist->used.simple  = 1;
882
883
  hdulist->bitpix       = bitpix;
  hdulist->naxis        = (channels == 1) ? 2 : 3;
884
885
  hdulist->naxisn[0]    = width;
  hdulist->naxisn[1]    = height;
886
  hdulist->naxisn[2]    = channels;
887
  hdulist->used.datamin = 1;
888
  hdulist->datamin      = 0.0;
889
  hdulist->used.datamax = 1;
890
891
892
893
  hdulist->used.bzero   = 1;
  hdulist->bzero        = 0.0;
  hdulist->used.bscale  = 1;
  hdulist->bscale       = 1.0;
894

895
896
897
898
899
900
901
902
903
  switch (bitpix)
    {
    case 8:
      hdulist->datamax = 255;
      break;
    case 16:
      hdulist->datamax = 65535;
      break;
    case 32:
Richard Kreckel's avatar
Richard Kreckel committed
904
      hdulist->datamax = 4294967295.0; /* .0 to silence gcc */
905
906
907
908
909
910
      break;
    case -32:
      hdulist->datamax = 1.0;
      break;
    case -64:
      hdulist->datamax = 1.0;
911
      break;
912
913
914
915
    default:
      return NULL;
    }

916
917
  fits_add_card (hdulist, "");
  fits_add_card (hdulist,
918
                 "HISTORY THIS FITS FILE WAS GENERATED BY GIMP USING FITSRW");
919
920
  fits_add_card (hdulist, "");
  fits_add_card (hdulist,
921
                 "COMMENT FitsRW is (C) Peter Kirchgessner (peter@kirchgessner.net), but available");
922
  fits_add_card (hdulist,
923
                 "COMMENT under the GNU general public licence.");
924
925
  fits_add_card (hdulist,
                 "COMMENT For sources see http://www.kirchgessner.net");
926
  fits_add_card (hdulist, "");
927
  fits_add_card (hdulist, ctype3_card[channels * 3]);
928

929
930
  if (ctype3_card[channels * 3 + 1] != NULL)
    fits_add_card (hdulist, ctype3_card[channels * 3 + 1]);
931

932
933
  if (print_ctype3 && (ctype3_card[channels * 3 + 2] != NULL))
    fits_add_card (hdulist, ctype3_card[channels * 3 + 2]);
934

935
936
  fits_add_card (hdulist, "");

937
  return hdulist;
Elliot Lee's avatar
Elliot Lee committed
938
939
940
}


941
/* Save direct colors (GRAY, GRAYA, RGB, RGBA) */
Elliot Lee's avatar
Elliot Lee committed
942
static gint
943
944
945
save_fits (FitsFile     *ofp,
           GimpImage    *image,
           GimpDrawable *drawable)
946
{
947
948
  gint           height, width, i, j, channel, channelnum;
  gint           tile_height, bpp, bpsl, bitpix, bpc;
949
950
  long           nbytes;
  guchar        *data, *src;
951
  GeglBuffer    *buffer;
952
953
  const Babl    *format, *type;
  FitsHduList   *hdu;
Elliot Lee's avatar
Elliot Lee committed
954

955
  buffer = gimp_drawable_get_buffer (drawable);
956
957
958
959

  width  = gegl_buffer_get_width  (buffer);
  height = gegl_buffer_get_height (buffer);

960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
  format = gegl_buffer_get_format (buffer);
  type   = babl_format_get_type (format, 0);

  if (type == babl_type ("u8"))
    {
      bitpix = 8;
    }
  else if (type == babl_type ("u16"))
    {
      bitpix = 16;
    }
  else if (type == babl_type ("u32"))
    {
      bitpix = 32;
    }
975
  else if (type == babl_type ("half"))
976
977
    {
      bitpix = -32;
978
      type = babl_type ("float");
979
    }
980
  else if (type == babl_type ("float"))
981
982
    {
      bitpix = -32;
983
984
985
986
    }
  else if (type == babl_type ("double"))
    {
      bitpix = -64;
987
988
989
990
991
992
    }
  else
    {
      return FALSE;
    }

993
  switch (gimp_drawable_type (drawable))
994
995
    {
    case GIMP_GRAY_IMAGE:
996
997
998
999
      format = babl_format_new (babl_model ("Y'"),
                                type,
                                babl_component ("Y'"),
                                NULL);
1000
1001
1002
      break;

    case GIMP_GRAYA_IMAGE:
1003
1004
1005
1006
1007
      format = babl_format_new (babl_model ("Y'A"),
                                type,
                                babl_component ("Y'"),
                                babl_component ("A"),
                                NULL);
1008
1009
1010
1011
      break;

    case GIMP_RGB_IMAGE:
    case GIMP_INDEXED_IMAGE:
1012
1013
1014
1015
1016
1017
      format = babl_format_new (babl_model ("R'G'B'"),
                                type,
                                babl_component ("R'"),
                                babl_component ("G'"),
                                babl_component ("B'"),
                                NULL);
1018
1019
1020
1021
      break;

    case GIMP_RGBA_IMAGE:
    case GIMP_INDEXEDA_IMAGE:
1022
1023
1024
1025
1026
1027
1028
      format = babl_format_new (babl_model ("R'G'B'A"),
                                type,
                                babl_component ("R'"),
                                babl_component ("G'"),
                                babl_component ("B'"),
                                babl_component ("A"),
                                NULL);
1029
1030
1031
      break;
    }

1032
1033
1034
1035
1036
  channelnum = babl_format_get_n_components (format);
  bpp        = babl_format_get_bytes_per_pixel (format);

  bpc  = bpp / channelnum; /* Bytes per channel */
  bpsl = width * bpp;      /* Bytes per scanline */
1037

Elliot Lee's avatar
Elliot Lee committed
1038
1039
1040
  tile_height = gimp_tile_height ();

  /* allocate a buffer for retrieving information from the pixel region  */
1041
  src = data = (guchar *) g_malloc (width * height * bpp);
Elliot Lee's avatar
Elliot Lee committed
1042

1043
  hdu = create_fits_header (ofp, width, height, channelnum, bitpix);
1044
1045
  if (hdu == NULL)
    return FALSE;
Elliot Lee's avatar
Elliot Lee committed
1046

1047
1048
  if (fits_write_header (ofp, hdu) < 0)
    return FALSE;
Elliot Lee's avatar
Elliot Lee committed
1049
1050

  nbytes = 0;
1051
  for (channel = 0; channel < channelnum; channel++)
Elliot Lee's avatar
Elliot Lee committed
1052
    {
1053
      for (i = 0; i < height; i++)
1054
1055
        {
          if ((i % tile_height) == 0)
1056
            {