home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / fits / fits.c next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  29.4 KB  |  1,066 lines

  1. /* The GIMP -- an 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 2 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, write to the Free Software
  19.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20.  *
  21.  */
  22.  
  23. /* Event history:
  24.  * V 1.00, PK, 05-May-97: Creation
  25.  * V 1.01, PK, 19-May-97: Problem with compilation on Irix fixed
  26.  * V 1.02, PK, 08-Jun-97: Bug with saving gray images fixed
  27.  * V 1.03, PK, 05-Oct-97: Parse rc-file
  28.  * V 1.04, PK, 12-Oct-97: No progress bars for non-interactive mode
  29.  * V 1.05, nn, 20-Dec-97: Initialize image_ID in run()
  30.  * V 1.06, PK, 21-Nov-99: Internationalization
  31.  *                        Fix bug with gimp_export_image()
  32.  *                        (moved it from load to save)
  33.  */
  34. static char ident[] = "@(#) GIMP FITS file-plugin v1.06  21-Nov-99";
  35.  
  36. #include "config.h"
  37.  
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41.  
  42. #include <gtk/gtk.h>
  43.  
  44. #include <libgimp/gimp.h>
  45. #include <libgimp/gimpui.h>
  46.  
  47. #include "fitsrw.h"
  48.  
  49. #include "libgimp/stdplugins-intl.h"
  50.  
  51.  
  52. /* Load info */
  53. typedef struct
  54. {
  55.   gint replace;     /* replacement for blank/NaN-values    */
  56.   gint use_datamin; /* Use DATAMIN/MAX-scaling if possible */
  57.   gint compose;     /* compose images with naxis==3        */
  58. } FITSLoadVals;
  59.  
  60. typedef struct
  61. {
  62.   gint run;
  63. } FITSLoadInterface;
  64.  
  65.  
  66. /* Declare some local functions.
  67.  */
  68. static void   query      (void);
  69. static void   run        (gchar   *name,
  70.                           gint     nparams,
  71.                           GimpParam  *param,
  72.                           gint    *nreturn_vals,
  73.                           GimpParam **return_vals);
  74.  
  75. static gint32 load_image (gchar  *filename);
  76. static gint   save_image (gchar  *filename,
  77.                           gint32  image_ID,
  78.                           gint32  drawable_ID);
  79.  
  80. static FITS_HDU_LIST *create_fits_header (FITS_FILE *ofp,
  81.                       guint width, guint height, guint bpp);
  82. static gint save_index  (FITS_FILE *ofp,
  83.              gint32 image_ID,
  84.              gint32 drawable_ID);
  85. static gint save_direct (FITS_FILE *ofp,
  86.              gint32 image_ID,
  87.              gint32 drawable_ID);
  88.  
  89. static gint32 create_new_image (gchar          *filename,
  90.                 guint           pagenum,
  91.                 guint           width,
  92.                 guint           height,
  93.                 GimpImageBaseType      itype,
  94.                 GimpImageType   dtype,
  95.                 gint32         *layer_ID,
  96.                 GimpDrawable     **drawable,
  97.                 GimpPixelRgn       *pixel_rgn);
  98.  
  99. static void   check_load_vals (void);
  100.  
  101. static gint32 load_fits (gchar     *filename,
  102.              FITS_FILE *ifp,
  103.                          guint      picnum,
  104.              guint      ncompose);
  105.  
  106.  
  107. static gint   load_dialog              (void);
  108. static void   load_ok_callback         (GtkWidget *widget,
  109.                                         gpointer   data);
  110. static void   show_fits_errors         (void);
  111.  
  112.  
  113. static FITSLoadVals plvals =
  114. {
  115.   0,        /* Replace with black */
  116.   0,        /* Do autoscale on pixel-values */
  117.   0         /* Dont compose images */
  118. };
  119.  
  120. static FITSLoadInterface plint =
  121. {
  122.   FALSE
  123. };
  124.  
  125. GimpPlugInInfo PLUG_IN_INFO =
  126. {
  127.   NULL,  /* init_proc  */
  128.   NULL,  /* quit_proc  */
  129.   query, /* query_proc */
  130.   run,   /* run_proc   */
  131. };
  132.  
  133. /* The run mode */
  134. static GimpRunModeType l_run_mode;
  135.  
  136.  
  137. MAIN ()
  138.  
  139. static void
  140. query (void)
  141.  
  142. {
  143.   static GimpParamDef load_args[] =
  144.   {
  145.     { GIMP_PDB_INT32,    "run_mode",     "Interactive, non-interactive" },
  146.     { GIMP_PDB_STRING,   "filename",     "The name of the file to load" },
  147.     { GIMP_PDB_STRING,   "raw_filename", "The name of the file to load" },
  148.   };
  149.   static GimpParamDef load_return_vals[] =
  150.   {
  151.     { GIMP_PDB_IMAGE,    "image",        "Output image" },
  152.   };
  153.   static gint nload_args = sizeof (load_args) / sizeof (load_args[0]);
  154.   static gint nload_return_vals = (sizeof (load_return_vals) /
  155.                    sizeof (load_return_vals[0]));
  156.  
  157.   static GimpParamDef save_args[] =
  158.   {
  159.     { GIMP_PDB_INT32,    "run_mode",     "Interactive, non-interactive" },
  160.     { GIMP_PDB_IMAGE,    "image",        "Input image" },
  161.     { GIMP_PDB_DRAWABLE, "drawable",     "Drawable to save" },
  162.     { GIMP_PDB_STRING,   "filename",     "The name of the file to save the image in" },
  163.     { GIMP_PDB_STRING,   "raw_filename", "The name of the file to save the image in" },
  164.   };
  165.   static gint nsave_args = sizeof (save_args) / sizeof (save_args[0]);
  166.  
  167.   INIT_I18N();
  168.  
  169.   gimp_install_procedure ("file_fits_load",
  170.                           "load file of the FITS file format",
  171.                           "load file of the FITS file format (Flexible Image Transport System)",
  172.                           "Peter Kirchgessner",
  173.                           "Peter Kirchgessner (peter@kirchgessner.net)",
  174.                           "1997",
  175.                           "<Load>/FITS",
  176.                           NULL,
  177.                           GIMP_PLUGIN,
  178.                           nload_args, nload_return_vals,
  179.                           load_args, load_return_vals);
  180.  
  181.   gimp_install_procedure ("file_fits_save",
  182.                           "save file in the FITS file format",
  183.                           "FITS saving handles all image types except those with alpha channels.",
  184.                           "Peter Kirchgessner",
  185.                           "Peter Kirchgessner (peter@kirchgessner.net)",
  186.                           "1997",
  187.                           "<Save>/FITS",
  188.                           "RGB, GRAY, INDEXED",
  189.                           GIMP_PLUGIN,
  190.                           nsave_args, 0,
  191.                           save_args, NULL);
  192.  
  193.   /* Register file plugin by plugin name and handable extensions */
  194.   gimp_register_magic_load_handler ("file_fits_load",
  195.                     "fit,fits",
  196.                     "",
  197.                                     "0,string,SIMPLE");
  198.   gimp_register_save_handler       ("file_fits_save",
  199.                     "fit,fits",
  200.                     "");
  201. }
  202.  
  203.  
  204. static void
  205. run (gchar   *name,
  206.      gint     nparams,
  207.      GimpParam  *param,
  208.      gint    *nreturn_vals,
  209.      GimpParam **return_vals)
  210. {
  211.   static GimpParam values[2];
  212.   GimpRunModeType  run_mode;
  213.   GimpPDBStatusType   status = GIMP_PDB_SUCCESS;
  214.   gint32        image_ID;
  215.   gint32        drawable_ID;
  216.   GimpExportReturnType export = GIMP_EXPORT_CANCEL;
  217.  
  218.   l_run_mode = run_mode = (GimpRunModeType)param[0].data.d_int32;
  219.  
  220.   *nreturn_vals = 1;
  221.   *return_vals  = values;
  222.   values[0].type          = GIMP_PDB_STATUS;
  223.   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  224.  
  225.   if (strcmp (name, "file_fits_load") == 0)
  226.     {
  227.       INIT_I18N_UI();
  228.  
  229.       switch (run_mode)
  230.     {
  231.         case GIMP_RUN_INTERACTIVE:
  232.           /*  Possibly retrieve data  */
  233.           gimp_get_data ("file_fits_load", &plvals);
  234.  
  235.           if (!load_dialog ())
  236.         status = GIMP_PDB_CANCEL;
  237.       break;
  238.  
  239.         case GIMP_RUN_NONINTERACTIVE:
  240.           if (nparams != 3)
  241.         status = GIMP_PDB_CALLING_ERROR;
  242.           break;
  243.  
  244.         case GIMP_RUN_WITH_LAST_VALS:
  245.           /* Possibly retrieve data */
  246.           gimp_get_data ("file_fits_load", &plvals);
  247.           break;
  248.  
  249.         default:
  250.           break;
  251.     }
  252.  
  253.       if (status == GIMP_PDB_SUCCESS)
  254.     {
  255.       check_load_vals ();
  256.       image_ID = load_image (param[1].data.d_string);
  257.  
  258.       /* Write out error messages of FITS-Library */
  259.       show_fits_errors ();
  260.  
  261.       if (image_ID != -1)
  262.         {
  263.           *nreturn_vals = 2;
  264.           values[1].type         = GIMP_PDB_IMAGE;
  265.           values[1].data.d_image = image_ID;
  266.         }
  267.       else
  268.         {
  269.           status = GIMP_PDB_EXECUTION_ERROR;
  270.         }
  271.  
  272.       /*  Store plvals data  */
  273.       if (status == GIMP_PDB_SUCCESS)
  274.         gimp_set_data ("file_fits_load", &plvals, sizeof (FITSLoadVals));
  275.     }
  276.     }
  277.   else if (strcmp (name, "file_fits_save") == 0)
  278.     {
  279.       INIT_I18N_UI();
  280.  
  281.       image_ID = param[1].data.d_int32;
  282.       drawable_ID = param[2].data.d_int32;
  283.  
  284.       /*  eventually export the image */
  285.       switch (run_mode)
  286.     {
  287.     case GIMP_RUN_INTERACTIVE:
  288.     case GIMP_RUN_WITH_LAST_VALS:
  289.       gimp_ui_init ("fits", FALSE);
  290.       export = gimp_export_image (&image_ID, &drawable_ID, "FITS",
  291.                       (GIMP_EXPORT_CAN_HANDLE_RGB |
  292.                        GIMP_EXPORT_CAN_HANDLE_GRAY |
  293.                        GIMP_EXPORT_CAN_HANDLE_INDEXED));
  294.     if (export == GIMP_EXPORT_CANCEL)
  295.       {
  296.         values[0].data.d_status = GIMP_PDB_CANCEL;
  297.         return;
  298.       }
  299.     break;
  300.       default:
  301.     break;
  302.       }
  303.  
  304.       switch (run_mode)
  305.         {
  306.         case GIMP_RUN_INTERACTIVE:
  307.           break;
  308.  
  309.         case GIMP_RUN_NONINTERACTIVE:
  310.           /*  Make sure all the arguments are there!  */
  311.           if (nparams != 5)
  312.             status = GIMP_PDB_CALLING_ERROR;
  313.           break;
  314.  
  315.         case GIMP_RUN_WITH_LAST_VALS:
  316.           break;
  317.  
  318.         default:
  319.           break;
  320.         }
  321.  
  322.       if (status == GIMP_PDB_SUCCESS)
  323.     {
  324.       if (! save_image (param[3].data.d_string, image_ID, drawable_ID))
  325.         status = GIMP_PDB_EXECUTION_ERROR;
  326.     }
  327.  
  328.       if (export == GIMP_EXPORT_EXPORT)
  329.     gimp_image_delete (image_ID);
  330.     }
  331.   else
  332.     {
  333.       status = GIMP_PDB_CALLING_ERROR;
  334.     }
  335.  
  336.   values[0].data.d_status = status;
  337. }
  338.  
  339.  
  340. static gint32
  341. load_image (gchar *filename)
  342. {
  343.   gint32 image_ID, *image_list, *nl;
  344.   guint picnum;
  345.   int   k, n_images, max_images, hdu_picnum;
  346.   int   compose;
  347.   FILE *fp;
  348.   FITS_FILE *ifp;
  349.   FITS_HDU_LIST *hdu;
  350.  
  351.   fp = fopen (filename, "rb");
  352.   if (!fp)
  353.     {
  354.       g_message (_("Can't open file for reading"));
  355.       return (-1);
  356.     }
  357.   fclose (fp);
  358.  
  359.   ifp = fits_open (filename, "r");
  360.   if (ifp == NULL)
  361.     {
  362.       g_message (_("Error during open of FITS file"));
  363.       return (-1);
  364.     }
  365.   if (ifp->n_pic <= 0)
  366.     {
  367.       g_message (_("FITS file keeps no displayable images"));
  368.       fits_close (ifp);
  369.       return (-1);
  370.     }
  371.  
  372.   image_list = (gint32 *)g_malloc (10 * sizeof (gint32));
  373.   n_images = 0;
  374.   max_images = 10;
  375.  
  376.   for (picnum = 1; picnum <= ifp->n_pic; )
  377.     {
  378.       /* Get image info to see if we can compose them */
  379.       hdu = fits_image_info (ifp, picnum, &hdu_picnum);
  380.       if (hdu == NULL) break;
  381.  
  382.       /* Get number of FITS-images to compose */
  383.       compose = (   plvals.compose && (hdu_picnum == 1) && (hdu->naxis == 3)
  384.             && (hdu->naxisn[2] > 1) && (hdu->naxisn[2] <= 4));
  385.       if (compose)
  386.     compose = hdu->naxisn[2];
  387.       else
  388.     compose = 1;  /* Load as GRAY */
  389.  
  390.       image_ID = load_fits (filename, ifp, picnum, compose);
  391.  
  392.       /* Write out error messages of FITS-Library */
  393.       show_fits_errors ();
  394.  
  395.       if (image_ID == -1) break;
  396.       if (n_images == max_images)
  397.     {
  398.       nl = (gint32 *)g_realloc (image_list, (max_images+10)*sizeof (gint32));
  399.       if (nl == NULL) break;
  400.       image_list = nl;
  401.       max_images += 10;
  402.     }
  403.       image_list[n_images++] = image_ID;
  404.  
  405.       picnum += compose;
  406.     }
  407.  
  408.   /* Write out error messages of FITS-Library */
  409.   show_fits_errors ();
  410.  
  411.   fits_close (ifp);
  412.  
  413.   /* Display images in reverse order. The last will be displayed by GIMP itself*/
  414.   if (l_run_mode != GIMP_RUN_NONINTERACTIVE)
  415.     {
  416.       for (k = n_images-1; k >= 1; k--)
  417.     {
  418.       gimp_image_undo_enable (image_list[k]);
  419.       gimp_image_clean_all (image_list[k]);
  420.       gimp_display_new (image_list[k]);
  421.     }
  422.     }
  423.  
  424.   image_ID = (n_images > 0) ? image_list[0] : -1;
  425.   g_free (image_list);
  426.  
  427.   return (image_ID);
  428. }
  429.  
  430.  
  431. static gint
  432. save_image (gchar  *filename,
  433.             gint32  image_ID,
  434.             gint32  drawable_ID)
  435. {
  436.   FITS_FILE* ofp;
  437.   GimpImageType drawable_type;
  438.   gint retval;
  439.   char *temp = ident; /* Just to satisfy lint/gcc */
  440.  
  441.   drawable_type = gimp_drawable_type (drawable_ID);
  442.  
  443.   /*  Make sure we're not saving an image with an alpha channel  */
  444.   if (gimp_drawable_has_alpha (drawable_ID))
  445.     {
  446.       g_message (_("FITS save cannot handle images with alpha channels"));
  447.       return FALSE;
  448.     }
  449.  
  450.   switch (drawable_type)
  451.     {
  452.     case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE:
  453.     case GIMP_GRAY_IMAGE:    case GIMP_GRAYA_IMAGE:
  454.     case GIMP_RGB_IMAGE:     case GIMP_RGBA_IMAGE:
  455.       break;
  456.     default:
  457.       g_message (_("Cannot operate on unknown image types"));
  458.       return (FALSE);
  459.       break;
  460.     }
  461.  
  462.   /* Open the output file. */
  463.   ofp = fits_open (filename, "w");
  464.   if (!ofp)
  465.     {
  466.       g_message (_("Can't open file for writing"));
  467.       return (FALSE);
  468.     }
  469.  
  470.   if (l_run_mode != GIMP_RUN_NONINTERACTIVE)
  471.     {
  472.       temp = g_strdup_printf (_("Saving %s:"), filename);
  473.       gimp_progress_init (temp);
  474.       g_free (temp);
  475.     }
  476.  
  477.   if ((drawable_type == GIMP_INDEXED_IMAGE) || (drawable_type == GIMP_INDEXEDA_IMAGE))
  478.     retval = save_index (ofp,image_ID, drawable_ID);
  479.   else
  480.     retval = save_direct (ofp,image_ID, drawable_ID);
  481.  
  482.   fits_close (ofp);
  483.  
  484.   return (retval);
  485. }
  486.  
  487.  
  488. /* Check (and correct) the load values plvals */
  489. static void
  490. check_load_vals (void)
  491. {
  492.   if (plvals.replace > 255) plvals.replace = 255;
  493. }
  494.  
  495.  
  496. /* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
  497. static gint32
  498. create_new_image (gchar *filename,
  499.                   guint pagenum,
  500.                   guint width,
  501.                   guint height,
  502.                   GimpImageBaseType itype,
  503.                   GimpImageType dtype,
  504.                   gint32 *layer_ID,
  505.                   GimpDrawable **drawable,
  506.                   GimpPixelRgn *pixel_rgn)
  507. {
  508.   gint32 image_ID;
  509.   char *tmp;
  510.  
  511.   image_ID = gimp_image_new (width, height, itype);
  512.   if ((tmp = g_malloc (strlen (filename) + 64)) != NULL)
  513.     {
  514.       sprintf (tmp, "%s-img%ld", filename, (long)pagenum);
  515.       gimp_image_set_filename (image_ID, tmp);
  516.       g_free (tmp);
  517.     }
  518.   else
  519.     gimp_image_set_filename (image_ID, filename);
  520.  
  521.   *layer_ID = gimp_layer_new (image_ID, _("Background"), width, height,
  522.                   dtype, 100, GIMP_NORMAL_MODE);
  523.   gimp_image_add_layer (image_ID, *layer_ID, 0);
  524.  
  525.   *drawable = gimp_drawable_get (*layer_ID);
  526.   gimp_pixel_rgn_init (pixel_rgn, *drawable, 0, 0, (*drawable)->width,
  527.                (*drawable)->height, TRUE, FALSE);
  528.  
  529.   return (image_ID);
  530. }
  531.  
  532.  
  533. /* Load FITS image. ncompose gives the number of FITS-images which have */
  534. /* to be composed together. This will result in different GIMP image types: */
  535. /* 1: GRAY, 2: GRAYA, 3: RGB, 4: RGBA */
  536. static gint32
  537. load_fits (gchar     *filename,
  538.            FITS_FILE *ifp,
  539.            guint      picnum,
  540.            guint      ncompose)
  541. {
  542.   register guchar *dest, *src;
  543.   guchar *data, *data_end, *linebuf;
  544.   int width, height, tile_height, scan_lines;
  545.   int i, j, channel, max_scan;
  546.   double a, b;
  547.   gint32 layer_ID, image_ID;
  548.   GimpPixelRgn pixel_rgn;
  549.   GimpDrawable *drawable;
  550.   GimpImageBaseType itype;
  551.   GimpImageType dtype;
  552.   gint err = 0;
  553.   FITS_HDU_LIST *hdulist;
  554.   FITS_PIX_TRANSFORM trans;
  555.  
  556.   hdulist = fits_seek_image (ifp, (int)picnum);
  557.   if (hdulist == NULL) return (-1);
  558.  
  559.   width = hdulist->naxisn[0];  /* Set the size of the FITS image */
  560.   height = hdulist->naxisn[1];
  561.  
  562.   if (ncompose == 2) { itype = GIMP_GRAY; dtype = GIMP_GRAYA_IMAGE; }
  563.   else if (ncompose == 3) { itype = GIMP_RGB; dtype = GIMP_RGB_IMAGE; }
  564.   else if (ncompose == 4) { itype = GIMP_RGB; dtype = GIMP_RGBA_IMAGE; }
  565.   else { ncompose = 1; itype = GIMP_GRAY; dtype = GIMP_GRAY_IMAGE;}
  566.  
  567.   image_ID = create_new_image (filename, picnum, width, height, itype, dtype,
  568.                    &layer_ID, &drawable, &pixel_rgn);
  569.  
  570.   tile_height = gimp_tile_height ();
  571.   data = g_malloc (tile_height * width * ncompose);
  572.   if (data == NULL) return (-1);
  573.   data_end = data + tile_height * width * ncompose;
  574.  
  575.   /* If the transformation from pixel value to */
  576.   /* data value has been specified, use it */
  577.   if (   plvals.use_datamin
  578.       && hdulist->used.datamin && hdulist->used.datamax
  579.       && hdulist->used.bzero && hdulist->used.bscale)
  580.     {
  581.       a = (hdulist->datamin - hdulist->bzero) / hdulist->bscale;
  582.       b = (hdulist->datamax - hdulist->bzero) / hdulist->bscale;
  583.       if (a < b) trans.pixmin = a, trans.pixmax = b;
  584.       else trans.pixmin = b, trans.pixmax = a;
  585.     }
  586.   else
  587.     {
  588.       trans.pixmin = hdulist->pixmin;
  589.       trans.pixmax = hdulist->pixmax;
  590.     }
  591.   trans.datamin = 0.0;
  592.   trans.datamax = 255.0;
  593.   trans.replacement = plvals.replace;
  594.   trans.dsttyp = 'c';
  595.  
  596.   /* FITS stores images with bottom row first. Therefore we have */
  597.   /* to fill the image from bottom to top. */
  598.  
  599.   if (ncompose == 1)
  600.     {
  601.       dest = data + tile_height * width;
  602.       scan_lines = 0;
  603.  
  604.       for (i = 0; i < height; i++)
  605.     {
  606.       /* Read FITS line */
  607.       dest -= width;
  608.       if (fits_read_pixel (ifp, hdulist, width, &trans, dest) != width)
  609.         {
  610.           err = 1;
  611.           break;
  612.         }
  613.  
  614.       scan_lines++;
  615.  
  616.       if ((l_run_mode != GIMP_RUN_NONINTERACTIVE) && ((i % 20) == 0))
  617.         gimp_progress_update ((double)(i+1) / (double)height);
  618.  
  619.       if ((scan_lines == tile_height) || ((i+1) == height))
  620.         {
  621.           gimp_pixel_rgn_set_rect (&pixel_rgn, dest, 0, height-i-1,
  622.                        width, scan_lines);
  623.           scan_lines = 0;
  624.           dest = data + tile_height * width;
  625.         }
  626.       if (err) break;
  627.     }
  628.     }
  629.   else   /* multiple images to compose */
  630.     {
  631.       linebuf = g_malloc (width);
  632.       if (linebuf == NULL) return (-1);
  633.  
  634.       for (channel = 0; channel < ncompose; channel++)
  635.     {
  636.       dest = data + tile_height * width * ncompose + channel;
  637.       scan_lines = 0;
  638.  
  639.       for (i = 0; i < height; i++)
  640.         {
  641.           if ((channel > 0) && ((i % tile_height) == 0))
  642.         { /* Reload a region for follow up channels */
  643.           max_scan = tile_height;
  644.           if (i + tile_height > height) max_scan = height - i;
  645.           gimp_pixel_rgn_get_rect (&pixel_rgn,
  646.                        data_end-max_scan*width*ncompose,
  647.                        0, height-i-max_scan, width,
  648.                        max_scan);
  649.         }
  650.  
  651.           /* Read FITS scanline */
  652.           dest -= width*ncompose;
  653.           if (fits_read_pixel (ifp, hdulist, width, &trans, linebuf) != width)
  654.         {
  655.           err = 1;
  656.           break;
  657.         }
  658.           j = width;
  659.           src = linebuf;
  660.           while (j--)
  661.         {
  662.           *dest = *(src++);
  663.           dest += ncompose;
  664.         }
  665.           dest -= width*ncompose;
  666.           scan_lines++;
  667.  
  668.           if ((l_run_mode != GIMP_RUN_NONINTERACTIVE) && ((i % 20) == 0))
  669.         gimp_progress_update (  (double)(channel*height+i+1)
  670.                     / (double)(height*ncompose));
  671.  
  672.           if ((scan_lines == tile_height) || ((i+1) == height))
  673.         {
  674.           gimp_pixel_rgn_set_rect (&pixel_rgn, dest-channel,
  675.                        0, height-i-1, width, scan_lines);
  676.           scan_lines = 0;
  677.           dest = data + tile_height * width * ncompose + channel;
  678.         }
  679.           if (err) break;
  680.         }
  681.     }
  682.       g_free (linebuf);
  683.     }
  684.  
  685.   g_free (data);
  686.  
  687.   if (err)
  688.     g_message (_("EOF encountered on reading"));
  689.  
  690.   gimp_drawable_flush (drawable);
  691.  
  692.   return (err ? -1 : image_ID);
  693. }
  694.  
  695.  
  696. static FITS_HDU_LIST *
  697. create_fits_header (FITS_FILE *ofp,
  698.             guint      width,
  699.             guint      height,
  700.             guint      bpp)
  701. {
  702.   FITS_HDU_LIST *hdulist;
  703.   int print_ctype3 = 0;   /* The CTYPE3-card may not be FITS-conforming */
  704.   static char *ctype3_card[] =
  705.   {
  706.     NULL, NULL, NULL,  /* bpp = 0: no additional card */
  707.     "COMMENT Image type within GIMP: GIMP_GRAY_IMAGE",
  708.     NULL,
  709.     NULL,
  710.     "COMMENT Image type within GIMP: GIMP_GRAYA_IMAGE (gray with alpha channel)",
  711.     "COMMENT Sequence for NAXIS3   : GRAY, ALPHA",
  712.     "CTYPE3  = 'GRAYA   '           / GRAY IMAGE WITH ALPHA CHANNEL",
  713.     "COMMENT Image type within GIMP: GIMP_RGB_IMAGE",
  714.     "COMMENT Sequence for NAXIS3   : RED, GREEN, BLUE",
  715.     "CTYPE3  = 'RGB     '           / RGB IMAGE",
  716.     "COMMENT Image type within GIMP: GIMP_RGBA_IMAGE (rgb with alpha channel)",
  717.     "COMMENT Sequence for NAXIS3   : RED, GREEN, BLUE, ALPHA",
  718.     "CTYPE3  = 'RGBA    '           / RGB IMAGE WITH ALPHA CHANNEL"
  719.   };
  720.  
  721.   hdulist = fits_add_hdu (ofp);
  722.   if (hdulist == NULL) return (NULL);
  723.  
  724.   hdulist->used.simple = 1;
  725.   hdulist->bitpix = 8;
  726.   hdulist->naxis = (bpp == 1) ? 2 : 3;
  727.   hdulist->naxisn[0] = width;
  728.   hdulist->naxisn[1] = height;
  729.   hdulist->naxisn[2] = bpp;
  730.   hdulist->used.datamin = 1;
  731.   hdulist->datamin = 0.0;
  732.   hdulist->used.datamax = 1;
  733.   hdulist->datamax = 255.0;
  734.   hdulist->used.bzero = 1;
  735.   hdulist->bzero = 0.0;
  736.   hdulist->used.bscale = 1;
  737.   hdulist->bscale = 1.0;
  738.  
  739.   fits_add_card (hdulist, "");
  740.   fits_add_card (hdulist,
  741.          "HISTORY THIS FITS FILE WAS GENERATED BY GIMP USING FITSRW");
  742.   fits_add_card (hdulist, "");
  743.   fits_add_card (hdulist,
  744.          "COMMENT FitsRW is (C) Peter Kirchgessner (peter@kirchgessner.net), but available");
  745.   fits_add_card (hdulist,
  746.          "COMMENT under the GNU general public licence."),
  747.     fits_add_card (hdulist,
  748.            "COMMENT For sources see http://www.kirchgessner.net");
  749.   fits_add_card (hdulist, "");
  750.   fits_add_card (hdulist, ctype3_card[bpp*3]);
  751.   if (ctype3_card[bpp*3+1] != NULL)
  752.     fits_add_card (hdulist, ctype3_card[bpp*3+1]);
  753.   if (print_ctype3 && (ctype3_card[bpp*3+2] != NULL))
  754.     fits_add_card (hdulist, ctype3_card[bpp*3+2]);
  755.   fits_add_card (hdulist, "");
  756.  
  757.   return (hdulist);
  758. }
  759.  
  760.  
  761. /* Save direct colours (GRAY, GRAYA, RGB, RGBA) */
  762. static gint
  763. save_direct (FITS_FILE *ofp,
  764.          gint32     image_ID,
  765.          gint32     drawable_ID)
  766. {
  767.   int height, width, i, j, channel;
  768.   int tile_height, bpp, bpsl;
  769.   long nbytes;
  770.   guchar *data, *src;
  771.   GimpPixelRgn pixel_rgn;
  772.   GimpDrawable *drawable;
  773.   GimpImageType drawable_type;
  774.   FITS_HDU_LIST *hdu;
  775.  
  776.   drawable = gimp_drawable_get (drawable_ID);
  777.   drawable_type = gimp_drawable_type (drawable_ID);
  778.   width = drawable->width;
  779.   height = drawable->height;
  780.   bpp = drawable->bpp;       /* Bytes per pixel */
  781.   bpsl = width * bpp;        /* Bytes per scanline */
  782.   tile_height = gimp_tile_height ();
  783.   gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, FALSE, FALSE);
  784.  
  785.   /* allocate a buffer for retrieving information from the pixel region  */
  786.   src = data = (unsigned char *)g_malloc (width * height * bpp);
  787.  
  788.   hdu = create_fits_header (ofp, width, height, bpp);
  789.   if (hdu == NULL) return (FALSE);
  790.   if (fits_write_header (ofp, hdu) < 0) return (FALSE);
  791.  
  792. #define GET_DIRECT_TILE(begin) \
  793.   {int scan_lines; \
  794.     scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
  795.     gimp_pixel_rgn_get_rect (&pixel_rgn, begin, 0, height-i-scan_lines, \
  796.     width, scan_lines); \
  797.     src = begin+bpsl*(scan_lines-1)+channel; }
  798.  
  799.   nbytes = 0;
  800.   for (channel = 0; channel < bpp; channel++)
  801.     {
  802.       for (i = 0; i < height; i++)
  803.     {
  804.       if ((i % tile_height) == 0) GET_DIRECT_TILE (data); /* get more data */
  805.  
  806.       if (bpp == 1)  /* One channel only ? Write the scanline */
  807.         {
  808.           fwrite (src, 1, width, ofp->fp);
  809.           src += bpsl;
  810.         }
  811.       else           /* Multiple channels */
  812.         {
  813.           for (j = 0; j < width; j++)  /* Write out bytes for current channel */
  814.         {
  815.           putc (*src, ofp->fp);
  816.           src += bpp;
  817.         }
  818.         }
  819.       nbytes += bpsl;
  820.       src -= 2*bpsl;
  821.  
  822.       if ((l_run_mode != GIMP_RUN_NONINTERACTIVE) && ((i % 20) == 0))
  823.         gimp_progress_update ((double)(i+channel*height)/(double)(height*bpp));
  824.     }
  825.     }
  826.  
  827.   nbytes = nbytes % FITS_RECORD_SIZE;
  828.   if (nbytes)
  829.     {
  830.       while (nbytes++ < FITS_RECORD_SIZE)
  831.     putc (0, ofp->fp);
  832.     }
  833.  
  834.   g_free (data);
  835.  
  836.   gimp_drawable_detach (drawable);
  837.  
  838.   if (ferror (ofp->fp))
  839.     {
  840.       g_message (_("Write error occured"));
  841.       return (FALSE);
  842.     }
  843.   return (TRUE);
  844. #undef GET_DIRECT_TILE
  845. }
  846.  
  847.  
  848. /* Save indexed colours (INDEXED, INDEXEDA) */
  849. static gint
  850. save_index (FITS_FILE *ofp,
  851.             gint32     image_ID,
  852.             gint32     drawable_ID)
  853. {
  854.   int height, width, i, j, channel;
  855.   int tile_height, bpp, bpsl, ncols;
  856.   long nbytes;
  857.   guchar *data, *src, *cmap, *cmapptr;
  858.   guchar red[256], green[256], blue[256];
  859.   guchar *channels[3];
  860.   GimpPixelRgn pixel_rgn;
  861.   GimpDrawable *drawable;
  862.   GimpImageType drawable_type;
  863.   FITS_HDU_LIST *hdu;
  864.  
  865.   channels[0] = red;   channels[1] = green;   channels[2] = blue;
  866.  
  867.   drawable = gimp_drawable_get (drawable_ID);
  868.   drawable_type = gimp_drawable_type (drawable_ID);
  869.   width = drawable->width;
  870.   height = drawable->height;
  871.   bpp = drawable->bpp;       /* Bytes per pixel */
  872.   bpsl = width * bpp;        /* Bytes per scanline */
  873.   tile_height = gimp_tile_height ();
  874.   gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, FALSE, FALSE);
  875.  
  876.   /* allocate a buffer for retrieving information from the pixel region  */
  877.   src = data = (guchar *)g_malloc (width * height * bpp);
  878.  
  879.   cmapptr = cmap = gimp_image_get_cmap (image_ID, &ncols);
  880.   if (ncols > sizeof (red)) ncols = sizeof (red);
  881.   for (i = 0; i < ncols; i++)
  882.     {
  883.       red[i] = *(cmapptr++);
  884.       green[i] = *(cmapptr++);
  885.       blue[i] = *(cmapptr++);
  886.     }
  887.   for (i = ncols; i < sizeof (red); i++)
  888.     red[i] = green[i] = blue[i] = 0;
  889.  
  890.   hdu = create_fits_header (ofp, width, height, bpp+2);
  891.   if (hdu == NULL) return (FALSE);
  892.   if (fits_write_header (ofp, hdu) < 0) return (FALSE);
  893.  
  894. #define GET_INDEXED_TILE(begin) \
  895.   {int scan_lines; \
  896.     scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
  897.     gimp_pixel_rgn_get_rect (&pixel_rgn, begin, 0, height-i-scan_lines, \
  898.                              width, scan_lines); \
  899.     src = begin+bpsl*(scan_lines-1); }
  900.  
  901.   nbytes = 0;
  902.  
  903.   /* Write the RGB-channels */
  904.   for (channel = 0; channel < 3; channel++)
  905.     {
  906.       cmapptr = channels[channel];
  907.       for (i = 0; i < height; i++)
  908.     {
  909.       if ((i % tile_height) == 0)
  910.         GET_INDEXED_TILE (data); /* get more data */
  911.  
  912.       for (j = 0; j < width; j++)  /* Write out bytes for current channel */
  913.         {
  914.           putc (cmapptr[*src], ofp->fp);
  915.           src += bpp;
  916.         }
  917.       nbytes += width;
  918.       src -= 2*bpsl;
  919.     }
  920.  
  921.       if ((l_run_mode != GIMP_RUN_NONINTERACTIVE) && ((i % 20) == 0))
  922.     gimp_progress_update ((double) (i+channel*height) /
  923.                   (double) (height*(bpp+2)));
  924.     }
  925.  
  926.   /* Write the Alpha-channel */
  927.   if (bpp > 1)
  928.     {
  929.       for (i = 0; i < height; i++)
  930.     {
  931.       if ((i % tile_height) == 0)
  932.         {
  933.           GET_INDEXED_TILE (data); /* get more data */
  934.           src++;                   /* Step to alpha channel data */
  935.         }
  936.  
  937.       for (j = 0; j < width; j++)  /* Write out bytes for alpha channel */
  938.         {
  939.           putc (*src, ofp->fp);
  940.           src += bpp;
  941.         }
  942.       nbytes += width;
  943.       src -= 2*bpsl;
  944.     }
  945.  
  946.       if ((l_run_mode != GIMP_RUN_NONINTERACTIVE) && ((i % 20) == 0))
  947.     gimp_progress_update ((double) (i+channel*height) /
  948.                   (double) (height*(bpp+2)));
  949.     }
  950.  
  951.   nbytes = nbytes % FITS_RECORD_SIZE;
  952.   if (nbytes)
  953.     {
  954.       while (nbytes++ < FITS_RECORD_SIZE)
  955.     putc (0, ofp->fp);
  956.     }
  957.  
  958.   g_free (data);
  959.  
  960.   gimp_drawable_detach (drawable);
  961.  
  962.   if (ferror (ofp->fp))
  963.     {
  964.       g_message (_("Write error occured"));
  965.       return (FALSE);
  966.     }
  967.   return (TRUE);
  968. #undef GET_INDEXED_TILE
  969. }
  970.  
  971. /*  Load interface functions  */
  972.  
  973. static gint
  974. load_dialog (void)
  975. {
  976.   GtkWidget *dialog;
  977.   GtkWidget *vbox;
  978.   GtkWidget *frame;
  979.  
  980.   gimp_ui_init ("fits", FALSE);
  981.  
  982.   dialog = gimp_dialog_new (_("Load FITS File"), "fits",
  983.                 gimp_standard_help_func, "filters/fits.html",
  984.                 GTK_WIN_POS_MOUSE,
  985.                 FALSE, FALSE, FALSE,
  986.  
  987.                 _("OK"), load_ok_callback,
  988.                 NULL, NULL, NULL, TRUE, FALSE,
  989.                 _("Cancel"), gtk_widget_destroy,
  990.                 NULL, 1, NULL, FALSE, TRUE,
  991.  
  992.                 NULL);
  993.  
  994.   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  995.                       GTK_SIGNAL_FUNC (gtk_main_quit),
  996.                       NULL);
  997.  
  998.   vbox = gtk_vbox_new (FALSE, 4);
  999.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  1000.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
  1001.               TRUE, TRUE, 0);
  1002.   gtk_widget_show (vbox);
  1003.  
  1004.   frame = gimp_radio_group_new2 (TRUE, _("BLANK/NaN Pixel Replacement"),
  1005.                  gimp_radio_button_update,
  1006.                  &plvals.replace, (gpointer) plvals.replace,
  1007.  
  1008.                  _("Black"), (gpointer) 0, NULL,
  1009.                  _("White"), (gpointer) 255, NULL,
  1010.  
  1011.                  NULL);
  1012.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  1013.   gtk_widget_show (frame);
  1014.  
  1015.   frame =
  1016.     gimp_radio_group_new2 (TRUE, _("Pixel Value Scaling"),
  1017.                gimp_radio_button_update,
  1018.                &plvals.use_datamin, (gpointer) plvals.use_datamin,
  1019.  
  1020.                _("Automatic"),          (gpointer) FALSE, NULL,
  1021.                _("By DATAMIN/DATAMAX"), (gpointer) TRUE, NULL,
  1022.  
  1023.                NULL);
  1024.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  1025.   gtk_widget_show (frame);
  1026.  
  1027.   frame =
  1028.     gimp_radio_group_new2 (TRUE, _("Image Composing"),
  1029.                gimp_radio_button_update,
  1030.                &plvals.compose, (gpointer) plvals.compose,
  1031.  
  1032.                _("None"),                 (gpointer) FALSE, NULL,
  1033.                "NAXIS=3, NAXIS3=2,...,4", (gpointer) TRUE, NULL,
  1034.  
  1035.                NULL);
  1036.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  1037.   gtk_widget_show (frame);
  1038.  
  1039.   gtk_widget_show (dialog);
  1040.  
  1041.   gtk_main ();
  1042.   gdk_flush ();
  1043.  
  1044.   return plint.run;
  1045. }
  1046.  
  1047. static void
  1048. load_ok_callback (GtkWidget *widget,
  1049.                   gpointer   data)
  1050.  
  1051. {
  1052.   plint.run = TRUE;
  1053.  
  1054.   gtk_widget_destroy (GTK_WIDGET (data));
  1055. }
  1056.  
  1057. static void
  1058. show_fits_errors (void)
  1059. {
  1060.   gchar *msg;
  1061.  
  1062.   /* Write out error messages of FITS-Library */
  1063.   while ((msg = fits_get_error ()) != NULL)
  1064.     g_message (msg);
  1065. }
  1066.