1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  * FITS file plugin
4  * reading and writing code Copyright (C) 1997 Peter Kirchgessner
5  * e-mail: peter@kirchgessner.net, WWW: http://www.kirchgessner.net
6  *
7  * This program is free software: you can redistribute it and/or modify
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
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * 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/>.
19  *
20  */
21 
22 /* Event history:
23  * V 1.00, PK, 05-May-97: Creation
24  * V 1.01, PK, 19-May-97: Problem with compilation on Irix fixed
25  * V 1.02, PK, 08-Jun-97: Bug with saving gray images fixed
26  * V 1.03, PK, 05-Oct-97: Parse rc-file
27  * 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  * V 1.06, PK, 21-Nov-99: Internationalization
30  *                        Fix bug with gimp_export_image()
31  *                        (moved it from load to save)
32  * V 1.07, PK, 16-Aug-06: Fix problems with internationalization
33  *                        (writing 255,0 instead of 255.0)
34  *                        Fix problem with not filling up properly last record
35  */
36 
37 #include "config.h"
38 
39 #include <string.h>
40 #include <errno.h>
41 
42 #include <glib/gstdio.h>
43 
44 #include <libgimp/gimp.h>
45 #include <libgimp/gimpui.h>
46 
47 #include "fits-io.h"
48 
49 #include "libgimp/stdplugins-intl.h"
50 
51 
52 #define LOAD_PROC      "file-fits-load"
53 #define SAVE_PROC      "file-fits-save"
54 #define PLUG_IN_BINARY "file-fits"
55 #define PLUG_IN_ROLE   "gimp-file-fits"
56 
57 
58 /* Load info */
59 typedef struct
60 {
61   gint replace;     /* replacement for blank/NaN-values    */
62   gint use_datamin; /* Use DATAMIN/MAX-scaling if possible */
63   gint compose;     /* compose images with naxis==3        */
64 } FITSLoadVals;
65 
66 
67 /* Declare some local functions.
68  */
69 static void          query              (void);
70 static void          run                (const gchar        *name,
71                                          gint                nparams,
72                                          const GimpParam    *param,
73                                          gint               *nreturn_vals,
74                                          GimpParam         **return_vals);
75 
76 static gint32        load_image         (const gchar        *filename,
77                                          GError            **error);
78 static gint          save_image         (const gchar        *filename,
79                                          gint32              image_ID,
80                                          gint32              drawable_ID,
81                                          GError            **error);
82 
83 static FitsHduList * create_fits_header (FitsFile           *ofp,
84                                          guint               width,
85                                          guint               height,
86                                          guint               channels,
87                                          guint               bitpix);
88 
89 static gint          save_fits        (FitsFile           *ofp,
90                                        gint32              image_ID,
91                                        gint32              drawable_ID);
92 
93 static gint32        create_new_image   (const gchar        *filename,
94                                          guint               pagenum,
95                                          guint               width,
96                                          guint               height,
97                                          GimpImageBaseType   itype,
98                                          GimpImageType       dtype,
99                                          GimpPrecision       iprecision,
100                                          gint32             *layer_ID,
101                                          GeglBuffer        **buffer);
102 
103 static void          check_load_vals    (void);
104 
105 static gint32        load_fits          (const gchar        *filename,
106                                          FitsFile           *ifp,
107                                          guint               picnum,
108                                          guint               ncompose);
109 
110 static gboolean      load_dialog        (void);
111 static void          show_fits_errors   (void);
112 
113 
114 static FITSLoadVals plvals =
115 {
116   0,        /* Replace with black */
117   0,        /* Do autoscale on pixel-values */
118   0         /* Don't compose images */
119 };
120 
121 const GimpPlugInInfo PLUG_IN_INFO =
122 {
123   NULL,  /* init_proc  */
124   NULL,  /* quit_proc  */
125   query, /* query_proc */
126   run,   /* run_proc   */
127 };
128 
129 /* The run mode */
130 static GimpRunMode l_run_mode;
131 
132 
MAIN()133 MAIN ()
134 
135 static void
136 query (void)
137 
138 {
139   static const GimpParamDef load_args[] =
140   {
141     { GIMP_PDB_INT32,    "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
142     { GIMP_PDB_STRING,   "filename",     "The name of the file to load" },
143     { GIMP_PDB_STRING,   "raw-filename", "The name of the file to load" },
144   };
145   static const GimpParamDef load_return_vals[] =
146   {
147     { GIMP_PDB_IMAGE,    "image",        "Output image" },
148   };
149 
150   static const GimpParamDef save_args[] =
151   {
152     { GIMP_PDB_INT32,    "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
153     { GIMP_PDB_IMAGE,    "image",        "Input image" },
154     { GIMP_PDB_DRAWABLE, "drawable",     "Drawable to export" },
155     { GIMP_PDB_STRING,   "filename",     "The name of the file to export the image in" },
156     { GIMP_PDB_STRING,   "raw-filename", "The name of the file to export the image in" },
157   };
158 
159   gimp_install_procedure (LOAD_PROC,
160                           "load file of the FITS file format",
161                           "load file of the FITS file format "
162                           "(Flexible Image Transport System)",
163                           "Peter Kirchgessner",
164                           "Peter Kirchgessner (peter@kirchgessner.net)",
165                           "1997",
166                           N_("Flexible Image Transport System"),
167                           NULL,
168                           GIMP_PLUGIN,
169                           G_N_ELEMENTS (load_args),
170                           G_N_ELEMENTS (load_return_vals),
171                           load_args, load_return_vals);
172 
173   gimp_register_file_handler_mime (LOAD_PROC, "image/x-fits");
174   gimp_register_magic_load_handler (LOAD_PROC,
175                                     "fit,fits",
176                                     "",
177                                     "0,string,SIMPLE");
178 
179   gimp_install_procedure (SAVE_PROC,
180                           "export file in the FITS file format",
181                           "FITS exporting handles all image types except "
182                           "those with alpha channels.",
183                           "Peter Kirchgessner",
184                           "Peter Kirchgessner (peter@kirchgessner.net)",
185                           "1997",
186                           N_("Flexible Image Transport System"),
187                           "RGB, GRAY, INDEXED",
188                           GIMP_PLUGIN,
189                           G_N_ELEMENTS (save_args), 0,
190                           save_args, NULL);
191 
192   gimp_register_file_handler_mime (SAVE_PROC, "image/x-fits");
193   gimp_register_save_handler (SAVE_PROC, "fit,fits", "");
194 }
195 
196 
197 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)198 run (const gchar      *name,
199      gint              nparams,
200      const GimpParam  *param,
201      gint             *nreturn_vals,
202      GimpParam       **return_vals)
203 {
204   static GimpParam   values[2];
205   GimpRunMode        run_mode;
206   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
207   gint32             image_ID;
208   gint32             drawable_ID;
209   GimpExportReturn   export = GIMP_EXPORT_CANCEL;
210   GError            *error  = NULL;
211 
212   INIT_I18N ();
213   gegl_init (NULL, NULL);
214 
215   l_run_mode = run_mode = (GimpRunMode)param[0].data.d_int32;
216 
217   *nreturn_vals = 1;
218   *return_vals  = values;
219 
220   values[0].type          = GIMP_PDB_STATUS;
221   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
222 
223   if (strcmp (name, LOAD_PROC) == 0)
224     {
225       switch (run_mode)
226         {
227         case GIMP_RUN_INTERACTIVE:
228           /*  Possibly retrieve data  */
229           gimp_get_data (LOAD_PROC, &plvals);
230 
231           if (!load_dialog ())
232             status = GIMP_PDB_CANCEL;
233           break;
234 
235         case GIMP_RUN_NONINTERACTIVE:
236           if (nparams != 3)
237             status = GIMP_PDB_CALLING_ERROR;
238           break;
239 
240         case GIMP_RUN_WITH_LAST_VALS:
241           /* Possibly retrieve data */
242           gimp_get_data (LOAD_PROC, &plvals);
243           break;
244 
245         default:
246           break;
247         }
248 
249       if (status == GIMP_PDB_SUCCESS)
250         {
251           check_load_vals ();
252           image_ID = load_image (param[1].data.d_string, &error);
253 
254           /* Write out error messages of FITS-Library */
255           show_fits_errors ();
256 
257           if (image_ID != -1)
258             {
259               *nreturn_vals = 2;
260               values[1].type         = GIMP_PDB_IMAGE;
261               values[1].data.d_image = image_ID;
262             }
263           else
264             {
265               status = GIMP_PDB_EXECUTION_ERROR;
266             }
267 
268           /*  Store plvals data  */
269           if (status == GIMP_PDB_SUCCESS)
270             gimp_set_data (LOAD_PROC, &plvals, sizeof (FITSLoadVals));
271         }
272     }
273   else if (strcmp (name, SAVE_PROC) == 0)
274     {
275       image_ID = param[1].data.d_int32;
276       drawable_ID = param[2].data.d_int32;
277 
278       /*  eventually export the image */
279       switch (run_mode)
280         {
281         case GIMP_RUN_INTERACTIVE:
282         case GIMP_RUN_WITH_LAST_VALS:
283           gimp_ui_init (PLUG_IN_BINARY, FALSE);
284 
285           export = gimp_export_image (&image_ID, &drawable_ID, "FITS",
286                                       GIMP_EXPORT_CAN_HANDLE_RGB  |
287                                       GIMP_EXPORT_CAN_HANDLE_GRAY |
288                                       GIMP_EXPORT_CAN_HANDLE_INDEXED);
289 
290         if (export == GIMP_EXPORT_CANCEL)
291           {
292             values[0].data.d_status = GIMP_PDB_CANCEL;
293             return;
294           }
295         break;
296       default:
297         break;
298       }
299 
300       switch (run_mode)
301         {
302         case GIMP_RUN_INTERACTIVE:
303           break;
304 
305         case GIMP_RUN_NONINTERACTIVE:
306           /*  Make sure all the arguments are there!  */
307           if (nparams != 5)
308             status = GIMP_PDB_CALLING_ERROR;
309           break;
310 
311         case GIMP_RUN_WITH_LAST_VALS:
312           break;
313 
314         default:
315           break;
316         }
317 
318       if (status == GIMP_PDB_SUCCESS)
319         {
320           if (! save_image (param[3].data.d_string, image_ID, drawable_ID,
321                             &error))
322             status = GIMP_PDB_EXECUTION_ERROR;
323         }
324 
325       if (export == GIMP_EXPORT_EXPORT)
326         gimp_image_delete (image_ID);
327     }
328   else
329     {
330       status = GIMP_PDB_CALLING_ERROR;
331     }
332 
333   if (status != GIMP_PDB_SUCCESS && error)
334     {
335       *nreturn_vals = 2;
336       values[1].type          = GIMP_PDB_STRING;
337       values[1].data.d_string = error->message;
338     }
339 
340   values[0].data.d_status = status;
341 }
342 
343 
344 static gint32
load_image(const gchar * filename,GError ** error)345 load_image (const gchar  *filename,
346             GError      **error)
347 {
348   gint32       image_ID, *image_list, *nl;
349   guint        picnum;
350   gint         k, n_images, max_images, hdu_picnum;
351   gint         compose;
352   FILE        *fp;
353   FitsFile    *ifp;
354   FitsHduList *hdu;
355 
356   fp = g_fopen (filename, "rb");
357   if (!fp)
358     {
359       g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
360                    _("Could not open '%s' for reading: %s"),
361                    gimp_filename_to_utf8 (filename), g_strerror (errno));
362       return -1;
363     }
364   fclose (fp);
365 
366   ifp = fits_open (filename, "r");
367   if (ifp == NULL)
368     {
369       g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
370                    "%s", _("Error during open of FITS file"));
371       return -1;
372     }
373   if (ifp->n_pic <= 0)
374     {
375       g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
376                    "%s", _("FITS file keeps no displayable images"));
377       fits_close (ifp);
378       return -1;
379     }
380 
381   image_list = g_new (gint32, 10);
382   n_images = 0;
383   max_images = 10;
384 
385   for (picnum = 1; picnum <= ifp->n_pic; )
386     {
387       /* Get image info to see if we can compose them */
388       hdu = fits_image_info (ifp, picnum, &hdu_picnum);
389       if (hdu == NULL) break;
390 
391       /* Get number of FITS-images to compose */
392       compose = (   plvals.compose && (hdu_picnum == 1) && (hdu->naxis == 3)
393                     && (hdu->naxisn[2] > 1) && (hdu->naxisn[2] <= 4));
394       if (compose)
395         compose = hdu->naxisn[2];
396       else
397         compose = 1;  /* Load as GRAY */
398 
399       image_ID = load_fits (filename, ifp, picnum, compose);
400 
401       /* Write out error messages of FITS-Library */
402       show_fits_errors ();
403 
404       if (image_ID == -1) break;
405       if (n_images == max_images)
406         {
407           nl = (gint32 *)g_realloc (image_list, (max_images+10)*sizeof (gint32));
408           if (nl == NULL) break;
409           image_list = nl;
410           max_images += 10;
411         }
412       image_list[n_images++] = image_ID;
413 
414       picnum += compose;
415     }
416 
417   /* Write out error messages of FITS-Library */
418   show_fits_errors ();
419 
420   fits_close (ifp);
421 
422   /* Display images in reverse order. The last will be displayed by GIMP itself*/
423   if (l_run_mode != GIMP_RUN_NONINTERACTIVE)
424     {
425       for (k = n_images-1; k >= 1; k--)
426         {
427           gimp_image_undo_enable (image_list[k]);
428           gimp_image_clean_all (image_list[k]);
429           gimp_display_new (image_list[k]);
430         }
431     }
432 
433   image_ID = (n_images > 0) ? image_list[0] : -1;
434   g_free (image_list);
435 
436   return (image_ID);
437 }
438 
439 
440 static gint
save_image(const gchar * filename,gint32 image_ID,gint32 drawable_ID,GError ** error)441 save_image (const gchar  *filename,
442             gint32        image_ID,
443             gint32        drawable_ID,
444             GError      **error)
445 {
446   FitsFile      *ofp;
447   GimpImageType  drawable_type;
448   gint           retval;
449 
450   drawable_type = gimp_drawable_type (drawable_ID);
451 
452   /*  Make sure we're not exporting an image with an alpha channel  */
453   if (gimp_drawable_has_alpha (drawable_ID))
454     {
455       g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
456                    "%s",
457                    _("FITS export cannot handle images with alpha channels"));
458       return FALSE;
459     }
460 
461   switch (drawable_type)
462     {
463     case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE:
464     case GIMP_GRAY_IMAGE:    case GIMP_GRAYA_IMAGE:
465     case GIMP_RGB_IMAGE:     case GIMP_RGBA_IMAGE:
466       break;
467     default:
468       g_message (_("Cannot operate on unknown image types."));
469       return (FALSE);
470       break;
471     }
472 
473   gimp_progress_init_printf (_("Exporting '%s'"),
474                              gimp_filename_to_utf8 (filename));
475 
476   /* Open the output file. */
477   ofp = fits_open (filename, "w");
478   if (!ofp)
479     {
480       g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
481                    _("Could not open '%s' for writing: %s"),
482                    gimp_filename_to_utf8 (filename), g_strerror (errno));
483       return (FALSE);
484     }
485 
486   retval = save_fits (ofp,image_ID, drawable_ID);
487 
488   fits_close (ofp);
489 
490   return (retval);
491 }
492 
493 
494 /* Check (and correct) the load values plvals */
495 static void
check_load_vals(void)496 check_load_vals (void)
497 {
498   if (plvals.replace > 255) plvals.replace = 255;
499 }
500 
501 
502 /* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
503 static gint32
create_new_image(const gchar * filename,guint pagenum,guint width,guint height,GimpImageBaseType itype,GimpImageType dtype,GimpPrecision iprecision,gint32 * layer_ID,GeglBuffer ** buffer)504 create_new_image (const gchar        *filename,
505                   guint               pagenum,
506                   guint               width,
507                   guint               height,
508                   GimpImageBaseType   itype,
509                   GimpImageType       dtype,
510                   GimpPrecision       iprecision,
511                   gint32             *layer_ID,
512                   GeglBuffer        **buffer)
513 {
514   gint32  image_ID;
515   char   *tmp;
516 
517   image_ID = gimp_image_new_with_precision (width, height, itype, iprecision);
518 
519   if ((tmp = g_malloc (strlen (filename) + 64)) != NULL)
520     {
521       sprintf (tmp, "%s-img%ld", filename, (long)pagenum);
522       gimp_image_set_filename (image_ID, tmp);
523       g_free (tmp);
524     }
525   else
526     gimp_image_set_filename (image_ID, filename);
527 
528   gimp_image_undo_disable (image_ID);
529   *layer_ID = gimp_layer_new (image_ID, _("Background"), width, height,
530                               dtype, 100,
531                               gimp_image_get_default_new_layer_mode (image_ID));
532   gimp_image_insert_layer (image_ID, *layer_ID, -1, 0);
533 
534   *buffer = gimp_drawable_get_buffer (*layer_ID);
535 
536   return image_ID;
537 }
538 
539 
540 /* Load FITS image. ncompose gives the number of FITS-images which have
541  * to be composed together. This will result in different GIMP image types:
542  * 1: GRAY, 2: GRAYA, 3: RGB, 4: RGBA
543  */
544 static gint32
load_fits(const gchar * filename,FitsFile * ifp,guint picnum,guint ncompose)545 load_fits (const gchar *filename,
546            FitsFile    *ifp,
547            guint        picnum,
548            guint        ncompose)
549 {
550   register guchar   *dest, *src;
551   guchar            *data, *data_end, *linebuf;
552   int                width, height, tile_height, scan_lines;
553   int                i, j, max_scan;
554   double             a, b;
555   gint32             layer_ID, image_ID;
556   GeglBuffer        *buffer;
557   GimpImageBaseType  itype;
558   GimpImageType      dtype;
559   GimpPrecision      iprecision;
560   gint               err = 0;
561   FitsHduList       *hdulist;
562   FitsPixTransform   trans;
563   double             datamax, replacetransform;
564   const Babl        *type, *format;
565 
566   hdulist = fits_seek_image (ifp, (int)picnum);
567   if (hdulist == NULL)
568     return -1;
569 
570   width  = hdulist->naxisn[0];  /* Set the size of the FITS image */
571   height = hdulist->naxisn[1];
572 
573   switch (hdulist->bitpix)
574     {
575     case 8:
576       iprecision = GIMP_PRECISION_U8_GAMMA;
577       type = babl_type ("u8");
578       datamax = 255.0;
579       replacetransform = 1.0;
580       break;
581     case 16:
582       iprecision = GIMP_PRECISION_U16_GAMMA; /* FIXME precision */
583       type = babl_type ("u16");
584       datamax = 65535.0;
585       replacetransform = 257;
586       break;
587     case 32:
588       iprecision = GIMP_PRECISION_U32_LINEAR;
589       type = babl_type ("u32");
590       datamax = 4294967295.0;
591       replacetransform = 16843009;
592       break;
593     case -32:
594       iprecision = GIMP_PRECISION_FLOAT_LINEAR;
595       type = babl_type ("float");
596       datamax = 1.0;
597       replacetransform = 1.0 / 255.0;
598       break;
599     case -64:
600       iprecision = GIMP_PRECISION_DOUBLE_LINEAR;
601       type = babl_type ("double");
602       datamax = 1.0;
603       replacetransform = 1.0 / 255.0;
604       break;
605     default:
606       return -1;
607     }
608 
609   if (ncompose == 2)
610     {
611       itype = GIMP_GRAY;
612       dtype = GIMP_GRAYA_IMAGE;
613       format = babl_format_new (babl_model ("Y'A"),
614                                 type,
615                                 babl_component ("Y'"),
616                                 babl_component ("A"),
617                                 NULL);
618     }
619   else if (ncompose == 3)
620     {
621       itype = GIMP_RGB;
622       dtype = GIMP_RGB_IMAGE;
623       format = babl_format_new (babl_model ("R'G'B'"),
624                                 type,
625                                 babl_component ("R'"),
626                                 babl_component ("G'"),
627                                 babl_component ("B'"),
628                                 NULL);
629     }
630   else if (ncompose == 4)
631     {
632       itype = GIMP_RGB;
633       dtype = GIMP_RGBA_IMAGE;
634       format = babl_format_new (babl_model ("R'G'B'A"),
635                                 type,
636                                 babl_component ("R'"),
637                                 babl_component ("G'"),
638                                 babl_component ("B'"),
639                                 babl_component ("A"),
640                                 NULL);
641     }
642   else
643     {
644       ncompose = 1;
645       itype = GIMP_GRAY;
646       dtype = GIMP_GRAY_IMAGE;
647       format = babl_format_new (babl_model ("Y'"),
648                                 type,
649                                 babl_component ("Y'"),
650                                 NULL);
651     }
652 
653   image_ID = create_new_image (filename, picnum, width, height,
654                                itype, dtype, iprecision,
655                                &layer_ID, &buffer);
656 
657   tile_height = gimp_tile_height ();
658 
659   data = g_malloc (tile_height * width * ncompose * hdulist->bpp);
660   if (data == NULL)
661     return -1;
662 
663   data_end = data + tile_height * width * ncompose * hdulist->bpp;
664 
665   /* If the transformation from pixel value to data value has been
666    * specified, use it
667    */
668   if (plvals.use_datamin    &&
669       hdulist->used.datamin && hdulist->used.datamax &&
670       hdulist->used.bzero   && hdulist->used.bscale)
671     {
672       a = (hdulist->datamin - hdulist->bzero) / hdulist->bscale;
673       b = (hdulist->datamax - hdulist->bzero) / hdulist->bscale;
674 
675       if (a < b)
676         trans.pixmin = a, trans.pixmax = b;
677       else
678         trans.pixmin = b, trans.pixmax = a;
679     }
680   else
681     {
682       trans.pixmin = hdulist->pixmin;
683       trans.pixmax = hdulist->pixmax;
684     }
685 
686   trans.datamin     = 0.0;
687   trans.datamax     = datamax;
688   trans.replacement = plvals.replace * replacetransform;
689   trans.dsttyp      = 'k';
690 
691   /* FITS stores images with bottom row first. Therefore we have to
692    * fill the image from bottom to top.
693    */
694 
695   if (ncompose == 1)
696     {
697       dest = data + tile_height * width * hdulist->bpp;
698       scan_lines = 0;
699 
700       for (i = 0; i < height; i++)
701         {
702           /* Read FITS line */
703           dest -= width * hdulist->bpp;
704           if (fits_read_pixel (ifp, hdulist, width, &trans, dest) != width)
705             {
706               err = 1;
707               break;
708             }
709 
710           scan_lines++;
711 
712           if ((i % 20) == 0)
713             gimp_progress_update ((gdouble) (i + 1) / (gdouble) height);
714 
715           if ((scan_lines == tile_height) || ((i + 1) == height))
716             {
717               gegl_buffer_set (buffer,
718                                GEGL_RECTANGLE (0, height - i - 1,
719                                                width, scan_lines), 0,
720                                format, dest, GEGL_AUTO_ROWSTRIDE);
721 
722               scan_lines = 0;
723               dest = data + tile_height * width * hdulist->bpp;
724             }
725 
726           if (err)
727             break;
728         }
729     }
730   else   /* multiple images to compose */
731     {
732       gint channel;
733 
734       linebuf = g_malloc (width * hdulist->bpp);
735       if (linebuf == NULL)
736         return -1;
737 
738       for (channel = 0; channel < ncompose; channel++)
739         {
740           dest = data + tile_height * width * hdulist->bpp * ncompose + channel * hdulist->bpp;
741           scan_lines = 0;
742 
743           for (i = 0; i < height; i++)
744             {
745               if ((channel > 0) && ((i % tile_height) == 0))
746                 {
747                   /* Reload a region for follow up channels */
748                   max_scan = tile_height;
749 
750                   if (i + tile_height > height)
751                     max_scan = height - i;
752 
753                   gegl_buffer_get (buffer,
754                                    GEGL_RECTANGLE (0, height - i - max_scan,
755                                                    width, max_scan), 1.0,
756                                    format, data_end - max_scan * width * hdulist->bpp * ncompose,
757                                    GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
758                 }
759 
760               /* Read FITS scanline */
761               dest -= width * ncompose * hdulist->bpp;
762               if (fits_read_pixel (ifp, hdulist, width, &trans, linebuf) != width)
763                 {
764                   err = 1;
765                   break;
766                 }
767               j = width;
768               src = linebuf;
769               while (j--)
770                 {
771                   memcpy (dest, src, hdulist->bpp);
772                   src += hdulist->bpp;
773                   dest += ncompose * hdulist->bpp;
774                 }
775               dest -= width * ncompose * hdulist->bpp;
776               scan_lines++;
777 
778               if ((i % 20) == 0)
779                 gimp_progress_update ((gdouble) (channel * height + i + 1) /
780                                       (gdouble) (height * ncompose));
781 
782               if ((scan_lines == tile_height) || ((i + 1) == height))
783                 {
784                   gegl_buffer_set (buffer,
785                                    GEGL_RECTANGLE (0, height - i - 1,
786                                                    width, scan_lines), 0,
787                                    format, dest - channel * hdulist->bpp, GEGL_AUTO_ROWSTRIDE);
788 
789                   scan_lines = 0;
790                   dest = data + tile_height * width * ncompose * hdulist->bpp + channel * hdulist->bpp;
791                 }
792 
793               if (err)
794                 break;
795             }
796         }
797 
798       g_free (linebuf);
799     }
800 
801   g_free (data);
802 
803   if (err)
804     g_message (_("EOF encountered on reading"));
805 
806   g_object_unref (buffer);
807 
808   gimp_progress_update (1.0);
809 
810   return err ? -1 : image_ID;
811 }
812 
813 
814 static FitsHduList *
create_fits_header(FitsFile * ofp,guint width,guint height,guint channels,guint bitpix)815 create_fits_header (FitsFile *ofp,
816                     guint     width,
817                     guint     height,
818                     guint     channels,
819                     guint     bitpix)
820 {
821   FitsHduList *hdulist;
822   gint         print_ctype3 = 0; /* The CTYPE3-card may not be FITS-conforming */
823 
824   static const char *ctype3_card[] =
825   {
826     NULL, NULL, NULL,  /* bpp = 0: no additional card */
827     "COMMENT Image type within GIMP: GIMP_GRAY_IMAGE",
828     NULL,
829     NULL,
830     "COMMENT Image type within GIMP: GIMP_GRAYA_IMAGE (gray with alpha channel)",
831     "COMMENT Sequence for NAXIS3   : GRAY, ALPHA",
832     "CTYPE3  = 'GRAYA   '           / GRAY IMAGE WITH ALPHA CHANNEL",
833     "COMMENT Image type within GIMP: GIMP_RGB_IMAGE",
834     "COMMENT Sequence for NAXIS3   : RED, GREEN, BLUE",
835     "CTYPE3  = 'RGB     '           / RGB IMAGE",
836     "COMMENT Image type within GIMP: GIMP_RGBA_IMAGE (rgb with alpha channel)",
837     "COMMENT Sequence for NAXIS3   : RED, GREEN, BLUE, ALPHA",
838     "CTYPE3  = 'RGBA    '           / RGB IMAGE WITH ALPHA CHANNEL"
839   };
840 
841   hdulist = fits_add_hdu (ofp);
842   if (hdulist == NULL)
843     return NULL;
844 
845   hdulist->used.simple  = 1;
846   hdulist->bitpix       = bitpix;
847   hdulist->naxis        = (channels == 1) ? 2 : 3;
848   hdulist->naxisn[0]    = width;
849   hdulist->naxisn[1]    = height;
850   hdulist->naxisn[2]    = channels;
851   hdulist->used.datamin = 1;
852   hdulist->datamin      = 0.0;
853   hdulist->used.datamax = 1;
854   hdulist->used.bzero   = 1;
855   hdulist->bzero        = 0.0;
856   hdulist->used.bscale  = 1;
857   hdulist->bscale       = 1.0;
858 
859   switch (bitpix)
860     {
861     case 8:
862       hdulist->datamax = 255;
863       break;
864     case 16:
865       hdulist->datamax = 65535;
866       break;
867     case 32:
868       hdulist->datamax = 4294967295.0; /* .0 to silence gcc */
869       break;
870     case -32:
871       hdulist->datamax = 1.0;
872       break;
873     case -64:
874       hdulist->datamax = 1.0;
875       break;
876     default:
877       return NULL;
878     }
879 
880   fits_add_card (hdulist, "");
881   fits_add_card (hdulist,
882                  "HISTORY THIS FITS FILE WAS GENERATED BY GIMP USING FITSRW");
883   fits_add_card (hdulist, "");
884   fits_add_card (hdulist,
885                  "COMMENT FitsRW is (C) Peter Kirchgessner (peter@kirchgessner.net), but available");
886   fits_add_card (hdulist,
887                  "COMMENT under the GNU general public licence.");
888   fits_add_card (hdulist,
889                  "COMMENT For sources see http://www.kirchgessner.net");
890   fits_add_card (hdulist, "");
891   fits_add_card (hdulist, ctype3_card[channels * 3]);
892 
893   if (ctype3_card[channels * 3 + 1] != NULL)
894     fits_add_card (hdulist, ctype3_card[channels * 3 + 1]);
895 
896   if (print_ctype3 && (ctype3_card[channels * 3 + 2] != NULL))
897     fits_add_card (hdulist, ctype3_card[channels * 3 + 2]);
898 
899   fits_add_card (hdulist, "");
900 
901   return hdulist;
902 }
903 
904 
905 /* Save direct colors (GRAY, GRAYA, RGB, RGBA) */
906 static gint
save_fits(FitsFile * ofp,gint32 image_ID,gint32 drawable_ID)907 save_fits (FitsFile *ofp,
908            gint32    image_ID,
909            gint32    drawable_ID)
910 {
911   gint           height, width, i, j, channel, channelnum;
912   gint           tile_height, bpp, bpsl, bitpix, bpc;
913   long           nbytes;
914   guchar        *data, *src;
915   GeglBuffer    *buffer;
916   const Babl    *format, *type;
917   FitsHduList   *hdu;
918 
919   buffer = gimp_drawable_get_buffer (drawable_ID);
920 
921   width  = gegl_buffer_get_width  (buffer);
922   height = gegl_buffer_get_height (buffer);
923 
924   format = gegl_buffer_get_format (buffer);
925   type   = babl_format_get_type (format, 0);
926 
927   if (type == babl_type ("u8"))
928     {
929       bitpix = 8;
930     }
931   else if (type == babl_type ("u16"))
932     {
933       bitpix = 16;
934     }
935   else if (type == babl_type ("u32"))
936     {
937       bitpix = 32;
938     }
939   else if (type == babl_type ("half"))
940     {
941       bitpix = -32;
942       type = babl_type ("float");
943     }
944   else if (type == babl_type ("float"))
945     {
946       bitpix = -32;
947     }
948   else if (type == babl_type ("double"))
949     {
950       bitpix = -64;
951     }
952   else
953     {
954       return FALSE;
955     }
956 
957   switch (gimp_drawable_type (drawable_ID))
958     {
959     case GIMP_GRAY_IMAGE:
960       format = babl_format_new (babl_model ("Y'"),
961                                 type,
962                                 babl_component ("Y'"),
963                                 NULL);
964       break;
965 
966     case GIMP_GRAYA_IMAGE:
967       format = babl_format_new (babl_model ("Y'A"),
968                                 type,
969                                 babl_component ("Y'"),
970                                 babl_component ("A"),
971                                 NULL);
972       break;
973 
974     case GIMP_RGB_IMAGE:
975     case GIMP_INDEXED_IMAGE:
976       format = babl_format_new (babl_model ("R'G'B'"),
977                                 type,
978                                 babl_component ("R'"),
979                                 babl_component ("G'"),
980                                 babl_component ("B'"),
981                                 NULL);
982       break;
983 
984     case GIMP_RGBA_IMAGE:
985     case GIMP_INDEXEDA_IMAGE:
986       format = babl_format_new (babl_model ("R'G'B'A"),
987                                 type,
988                                 babl_component ("R'"),
989                                 babl_component ("G'"),
990                                 babl_component ("B'"),
991                                 babl_component ("A"),
992                                 NULL);
993       break;
994     }
995 
996   channelnum = babl_format_get_n_components (format);
997   bpp        = babl_format_get_bytes_per_pixel (format);
998 
999   bpc  = bpp / channelnum; /* Bytes per channel */
1000   bpsl = width * bpp;      /* Bytes per scanline */
1001 
1002   tile_height = gimp_tile_height ();
1003 
1004   /* allocate a buffer for retrieving information from the pixel region  */
1005   src = data = (guchar *) g_malloc (width * height * bpp);
1006 
1007   hdu = create_fits_header (ofp, width, height, channelnum, bitpix);
1008   if (hdu == NULL)
1009     return FALSE;
1010 
1011   if (fits_write_header (ofp, hdu) < 0)
1012     return FALSE;
1013 
1014   nbytes = 0;
1015   for (channel = 0; channel < channelnum; channel++)
1016     {
1017       for (i = 0; i < height; i++)
1018         {
1019           if ((i % tile_height) == 0)
1020             {
1021               gint scan_lines;
1022 
1023               scan_lines = (i + tile_height-1 < height) ?
1024                             tile_height : (height - i);
1025 
1026               gegl_buffer_get (buffer,
1027                                GEGL_RECTANGLE (0, height - i - scan_lines,
1028                                                width, scan_lines), 1.0,
1029                                format, data,
1030                                GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
1031 
1032               src = data + bpsl * (scan_lines - 1) + channel * bpc;
1033             }
1034 
1035           if (channelnum == 1 && bitpix == 8)  /* One channel and 8 bit? Write the scanline */
1036             {
1037               fwrite (src, bpc, width, ofp->fp);
1038               src += bpsl;
1039             }
1040           else           /* Multiple channels or high bit depth */
1041             {
1042             /* Write out bytes for current channel */
1043             /* FIXME: Don't assume a little endian arch */
1044             switch (bitpix)
1045               {
1046               case 8:
1047                 for (j = 0; j < width; j++)
1048                   {
1049                     putc (*src, ofp->fp);
1050                     src += bpp;
1051                   }
1052                 break;
1053               case 16:
1054                 for (j = 0; j < width; j++)
1055                   {
1056                     *((guint16*)src) += 32768;
1057                     putc (*(src + 1), ofp->fp);
1058                     putc (*(src + 0), ofp->fp);
1059                     src += bpp;
1060                   }
1061                 break;
1062               case 32:
1063                 for (j = 0; j < width; j++)
1064                   {
1065                     *((guint32*)src) += 2147483648.0; /* .0 to silence gcc */
1066                     putc (*(src + 3), ofp->fp);
1067                     putc (*(src + 2), ofp->fp);
1068                     putc (*(src + 1), ofp->fp);
1069                     putc (*(src + 0), ofp->fp);
1070                     src += bpp;
1071                   }
1072                 break;
1073               case -32:
1074                 for (j = 0; j < width; j++)
1075                   {
1076                     putc (*(src + 3), ofp->fp);
1077                     putc (*(src + 2), ofp->fp);
1078                     putc (*(src + 1), ofp->fp);
1079                     putc (*(src + 0), ofp->fp);
1080                     src += bpp;
1081                   }
1082                 break;
1083               case -64:
1084                 for (j = 0; j < width; j++)
1085                   {
1086                     putc (*(src + 7), ofp->fp);
1087                     putc (*(src + 6), ofp->fp);
1088                     putc (*(src + 5), ofp->fp);
1089                     putc (*(src + 4), ofp->fp);
1090                     putc (*(src + 3), ofp->fp);
1091                     putc (*(src + 2), ofp->fp);
1092                     putc (*(src + 1), ofp->fp);
1093                     putc (*(src + 0), ofp->fp);
1094                     src += bpp;
1095                   }
1096                 break;
1097               default:
1098                 return FALSE;
1099               }
1100             }
1101 
1102           nbytes += width * bpc;
1103           src -= 2 * bpsl;
1104 
1105           if ((i % 20) == 0)
1106             gimp_progress_update ((gdouble) (i + channel * height) /
1107                                   (gdouble) (height * channelnum));
1108         }
1109     }
1110 
1111   nbytes = nbytes % FITS_RECORD_SIZE;
1112   if (nbytes)
1113     {
1114       while (nbytes++ < FITS_RECORD_SIZE)
1115         putc (0, ofp->fp);
1116     }
1117 
1118   g_free (data);
1119 
1120   g_object_unref (buffer);
1121 
1122   gimp_progress_update (1.0);
1123 
1124   if (ferror (ofp->fp))
1125     {
1126       g_message (_("Write error occurred"));
1127       return FALSE;
1128     }
1129 
1130   return TRUE;
1131 }
1132 
1133 
1134 /*  Load interface functions  */
1135 
1136 static gboolean
load_dialog(void)1137 load_dialog (void)
1138 {
1139   GtkWidget *dialog;
1140   GtkWidget *vbox;
1141   GtkWidget *frame;
1142   gboolean   run;
1143 
1144   gimp_ui_init (PLUG_IN_BINARY, FALSE);
1145 
1146   dialog = gimp_dialog_new (_("Load FITS File"), PLUG_IN_ROLE,
1147                             NULL, 0,
1148                             gimp_standard_help_func, LOAD_PROC,
1149 
1150                             _("_Cancel"), GTK_RESPONSE_CANCEL,
1151                             _("_Open"),   GTK_RESPONSE_OK,
1152 
1153                             NULL);
1154 
1155   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1156                                            GTK_RESPONSE_OK,
1157                                            GTK_RESPONSE_CANCEL,
1158                                            -1);
1159 
1160   gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
1161 
1162   gimp_window_set_transient (GTK_WINDOW (dialog));
1163 
1164   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
1165   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
1166   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
1167                       vbox, TRUE, TRUE, 0);
1168   gtk_widget_show (vbox);
1169 
1170   frame = gimp_int_radio_group_new (TRUE, _("Replacement for undefined pixels"),
1171                                     G_CALLBACK (gimp_radio_button_update),
1172                                     &plvals.replace, plvals.replace,
1173 
1174                                     _("_Black"), 0,   NULL,
1175                                     _("_White"), 255, NULL,
1176 
1177                                     NULL);
1178   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
1179   gtk_widget_show (frame);
1180 
1181   frame =
1182     gimp_int_radio_group_new (TRUE, _("Pixel value scaling"),
1183                               G_CALLBACK (gimp_radio_button_update),
1184                               &plvals.use_datamin, plvals.use_datamin,
1185 
1186                               _("_Automatic"),          FALSE, NULL,
1187                               _("By _DATAMIN/DATAMAX"), TRUE,  NULL,
1188 
1189                               NULL);
1190   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
1191   gtk_widget_show (frame);
1192 
1193   frame =
1194     gimp_int_radio_group_new (TRUE, _("Image Composing"),
1195                               G_CALLBACK (gimp_radio_button_update),
1196                               &plvals.compose, plvals.compose,
1197 
1198                               C_("composing", "_None"),   FALSE, NULL,
1199                               "NA_XIS=3, NAXIS3=2,...,4", TRUE,  NULL,
1200 
1201                               NULL);
1202   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
1203   gtk_widget_show (frame);
1204 
1205   gtk_widget_show (dialog);
1206 
1207   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
1208 
1209   gtk_widget_destroy (dialog);
1210 
1211   return run;
1212 }
1213 
1214 static void
show_fits_errors(void)1215 show_fits_errors (void)
1216 {
1217   const gchar *msg;
1218 
1219   /* Write out error messages of FITS-Library */
1220   while ((msg = fits_get_error ()) != NULL)
1221     g_message ("%s", msg);
1222 }
1223