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