Home | History | Annotate | Download | only in coders
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %                        FFFFF  IIIII  TTTTT  SSSSS                           %
      7 %                        F        I      T    SS                              %
      8 %                        FFF      I      T     SSS                            %
      9 %                        F        I      T       SS                           %
     10 %                        F      IIIII    T    SSSSS                           %
     11 %                                                                             %
     12 %                                                                             %
     13 %            Read/Write Flexible Image Transport System Images.               %
     14 %                                                                             %
     15 %                              Software Design                                %
     16 %                                   Cristy                                    %
     17 %                                 July 1992                                   %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
     21 %  dedicated to making software imaging solutions freely available.           %
     22 %                                                                             %
     23 %  You may not use this file except in compliance with the License.  You may  %
     24 %  obtain a copy of the License at                                            %
     25 %                                                                             %
     26 %    http://www.imagemagick.org/script/license.php                            %
     27 %                                                                             %
     28 %  Unless required by applicable law or agreed to in writing, software        %
     29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
     30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
     31 %  See the License for the specific language governing permissions and        %
     32 %  limitations under the License.                                             %
     33 %                                                                             %
     34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     35 %
     36 %
     37 */
     38 
     39 /*
     41   Include declarations.
     42 */
     43 #include "MagickCore/studio.h"
     44 #include "MagickCore/attribute.h"
     45 #include "MagickCore/blob.h"
     46 #include "MagickCore/blob-private.h"
     47 #include "MagickCore/cache.h"
     48 #include "MagickCore/color-private.h"
     49 #include "MagickCore/colorspace.h"
     50 #include "MagickCore/colorspace-private.h"
     51 #include "MagickCore/constitute.h"
     52 #include "MagickCore/exception.h"
     53 #include "MagickCore/exception-private.h"
     54 #include "MagickCore/image.h"
     55 #include "MagickCore/image-private.h"
     56 #include "MagickCore/list.h"
     57 #include "MagickCore/magick.h"
     58 #include "MagickCore/memory_.h"
     59 #include "MagickCore/module.h"
     60 #include "MagickCore/monitor.h"
     61 #include "MagickCore/monitor-private.h"
     62 #include "MagickCore/pixel-accessor.h"
     63 #include "MagickCore/property.h"
     64 #include "MagickCore/quantum-private.h"
     65 #include "MagickCore/static.h"
     66 #include "MagickCore/statistic.h"
     67 #include "MagickCore/string_.h"
     68 #include "MagickCore/string-private.h"
     69 #include "MagickCore/module.h"
     70 
     71 /*
     73   Forward declarations.
     74 */
     75 #define FITSBlocksize  2880UL
     76 
     77 /*
     79   Forward declarations.
     80 */
     81 static MagickBooleanType
     82   WriteFITSImage(const ImageInfo *,Image *,ExceptionInfo *);
     83 
     84 /*
     86 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     87 %                                                                             %
     88 %                                                                             %
     89 %                                                                             %
     90 %   I s F I T S                                                               %
     91 %                                                                             %
     92 %                                                                             %
     93 %                                                                             %
     94 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     95 %
     96 %  IsFITS() returns MagickTrue if the image format type, identified by the
     97 %  magick string, is FITS.
     98 %
     99 %  The format of the IsFITS method is:
    100 %
    101 %      MagickBooleanType IsFITS(const unsigned char *magick,const size_t length)
    102 %
    103 %  A description of each parameter follows:
    104 %
    105 %    o magick: compare image format pattern against these bytes.
    106 %
    107 %    o length: Specifies the length of the magick string.
    108 %
    109 */
    110 static MagickBooleanType IsFITS(const unsigned char *magick,const size_t length)
    111 {
    112   if (length < 6)
    113     return(MagickFalse);
    114   if (LocaleNCompare((const char *) magick,"IT0",3) == 0)
    115     return(MagickTrue);
    116   if (LocaleNCompare((const char *) magick,"SIMPLE",6) == 0)
    117     return(MagickTrue);
    118   return(MagickFalse);
    119 }
    120 
    121 /*
    123 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    124 %                                                                             %
    125 %                                                                             %
    126 %                                                                             %
    127 %   R e a d F I T S I m a g e                                                 %
    128 %                                                                             %
    129 %                                                                             %
    130 %                                                                             %
    131 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    132 %
    133 %  ReadFITSImage() reads a FITS image file and returns it.  It allocates the
    134 %  memory necessary for the new Image structure and returns a pointer to the
    135 %  new image.
    136 %
    137 %  The format of the ReadFITSImage method is:
    138 %
    139 %      Image *ReadFITSImage(const ImageInfo *image_info,
    140 %        ExceptionInfo *exception)
    141 %
    142 %  A description of each parameter follows:
    143 %
    144 %    o image_info: the image info.
    145 %
    146 %    o exception: return any errors or warnings in this structure.
    147 %
    148 */
    149 
    150 static inline double GetFITSPixel(Image *image,int bits_per_pixel)
    151 {
    152   switch (image->depth >> 3)
    153   {
    154     case 1:
    155       return((double) ReadBlobByte(image));
    156     case 2:
    157       return((double) ((short) ReadBlobShort(image)));
    158     case 4:
    159     {
    160       if (bits_per_pixel > 0)
    161         return((double) ReadBlobSignedLong(image));
    162       return((double) ReadBlobFloat(image));
    163     }
    164     case 8:
    165     {
    166       if (bits_per_pixel > 0)
    167         return((double) ((MagickOffsetType) ReadBlobLongLong(image)));
    168     }
    169     default:
    170       break;
    171   }
    172   return(ReadBlobDouble(image));
    173 }
    174 
    175 static MagickOffsetType GetFITSPixelExtrema(Image *image,
    176   const int bits_per_pixel,double *minima,double *maxima)
    177 {
    178   double
    179     pixel;
    180 
    181   MagickOffsetType
    182     offset;
    183 
    184   MagickSizeType
    185     number_pixels;
    186 
    187   register MagickOffsetType
    188     i;
    189 
    190   offset=TellBlob(image);
    191   if (offset == -1)
    192     return(-1);
    193   number_pixels=(MagickSizeType) image->columns*image->rows;
    194   *minima=GetFITSPixel(image,bits_per_pixel);
    195   *maxima=(*minima);
    196   for (i=1; i < (MagickOffsetType) number_pixels; i++)
    197   {
    198     pixel=GetFITSPixel(image,bits_per_pixel);
    199     if (pixel < *minima)
    200       *minima=pixel;
    201     if (pixel > *maxima)
    202       *maxima=pixel;
    203   }
    204   return(SeekBlob(image,offset,SEEK_SET));
    205 }
    206 
    207 static inline double GetFITSPixelRange(const size_t depth)
    208 {
    209   return((double) ((MagickOffsetType) GetQuantumRange(depth)));
    210 }
    211 
    212 static void SetFITSUnsignedPixels(const size_t length,
    213   const size_t bits_per_pixel,const EndianType endian,unsigned char *pixels)
    214 {
    215   register ssize_t
    216     i;
    217 
    218   if (endian != MSBEndian)
    219     pixels+=(bits_per_pixel >> 3)-1;
    220   for (i=0; i < (ssize_t) length; i++)
    221   {
    222     *pixels^=0x80;
    223     pixels+=bits_per_pixel >> 3;
    224   }
    225 }
    226 
    227 static Image *ReadFITSImage(const ImageInfo *image_info,
    228   ExceptionInfo *exception)
    229 {
    230   typedef struct _FITSInfo
    231   {
    232     MagickBooleanType
    233       extend,
    234       simple;
    235 
    236     int
    237       bits_per_pixel,
    238       columns,
    239       rows,
    240       number_axes,
    241       number_planes;
    242 
    243     double
    244       min_data,
    245       max_data,
    246       zero,
    247       scale;
    248 
    249     EndianType
    250       endian;
    251   } FITSInfo;
    252 
    253   char
    254     *comment,
    255     keyword[9],
    256     property[MagickPathExtent],
    257     value[73];
    258 
    259   double
    260     pixel,
    261     scale;
    262 
    263   FITSInfo
    264     fits_info;
    265 
    266   Image
    267     *image;
    268 
    269   int
    270     c;
    271 
    272   MagickBooleanType
    273     status;
    274 
    275   MagickSizeType
    276     number_pixels;
    277 
    278   register ssize_t
    279     i,
    280     x;
    281 
    282   register Quantum
    283     *q;
    284 
    285   ssize_t
    286     count,
    287     scene,
    288     y;
    289 
    290   /*
    291     Open image file.
    292   */
    293   assert(image_info != (const ImageInfo *) NULL);
    294   assert(image_info->signature == MagickCoreSignature);
    295   if (image_info->debug != MagickFalse)
    296     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
    297       image_info->filename);
    298   assert(exception != (ExceptionInfo *) NULL);
    299   assert(exception->signature == MagickCoreSignature);
    300   image=AcquireImage(image_info,exception);
    301   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
    302   if (status == MagickFalse)
    303     {
    304       image=DestroyImageList(image);
    305       return((Image *) NULL);
    306     }
    307   /*
    308     Initialize image header.
    309   */
    310   (void) ResetMagickMemory(&fits_info,0,sizeof(fits_info));
    311   fits_info.extend=MagickFalse;
    312   fits_info.simple=MagickFalse;
    313   fits_info.bits_per_pixel=8;
    314   fits_info.columns=1;
    315   fits_info.rows=1;
    316   fits_info.number_planes=1;
    317   fits_info.min_data=0.0;
    318   fits_info.max_data=0.0;
    319   fits_info.zero=0.0;
    320   fits_info.scale=1.0;
    321   fits_info.endian=MSBEndian;
    322   /*
    323     Decode image header.
    324   */
    325   for (comment=(char *) NULL; EOFBlob(image) == MagickFalse; )
    326   {
    327     for ( ; EOFBlob(image) == MagickFalse; )
    328     {
    329       register char
    330         *p;
    331 
    332       count=ReadBlob(image,8,(unsigned char *) keyword);
    333       if (count != 8)
    334         break;
    335       for (i=0; i < 8; i++)
    336       {
    337         if (isspace((int) ((unsigned char) keyword[i])) != 0)
    338           break;
    339         keyword[i]=tolower((int) ((unsigned char) keyword[i]));
    340       }
    341       keyword[i]='\0';
    342       count=ReadBlob(image,72,(unsigned char *) value);
    343       value[72]='\0';
    344       if (count != 72)
    345         break;
    346       p=value;
    347       if (*p == '=')
    348         {
    349           p+=2;
    350           while (isspace((int) ((unsigned char) *p)) != 0)
    351             p++;
    352         }
    353       if (LocaleCompare(keyword,"end") == 0)
    354         break;
    355       if (LocaleCompare(keyword,"extend") == 0)
    356         fits_info.extend=(*p == 'T') || (*p == 't') ? MagickTrue : MagickFalse;
    357       if (LocaleCompare(keyword,"simple") == 0)
    358         fits_info.simple=(*p == 'T') || (*p == 't') ? MagickTrue : MagickFalse;
    359       if (LocaleCompare(keyword,"bitpix") == 0)
    360         fits_info.bits_per_pixel=StringToLong(p);
    361       if (LocaleCompare(keyword,"naxis") == 0)
    362         fits_info.number_axes=StringToLong(p);
    363       if (LocaleCompare(keyword,"naxis1") == 0)
    364         fits_info.columns=StringToLong(p);
    365       if (LocaleCompare(keyword,"naxis2") == 0)
    366         fits_info.rows=StringToLong(p);
    367       if (LocaleCompare(keyword,"naxis3") == 0)
    368         fits_info.number_planes=StringToLong(p);
    369       if (LocaleCompare(keyword,"datamax") == 0)
    370         fits_info.max_data=StringToDouble(p,(char **) NULL);
    371       if (LocaleCompare(keyword,"datamin") == 0)
    372         fits_info.min_data=StringToDouble(p,(char **) NULL);
    373       if (LocaleCompare(keyword,"bzero") == 0)
    374         fits_info.zero=StringToDouble(p,(char **) NULL);
    375       if (LocaleCompare(keyword,"bscale") == 0)
    376         fits_info.scale=StringToDouble(p,(char **) NULL);
    377       if (LocaleCompare(keyword,"comment") == 0)
    378         {
    379           if (comment == (char *) NULL)
    380             comment=ConstantString(p);
    381           else
    382             (void) ConcatenateString(&comment,p);
    383         }
    384       if (LocaleCompare(keyword,"xendian") == 0)
    385         {
    386           if (LocaleNCompare(p,"big",3) == 0)
    387             fits_info.endian=MSBEndian;
    388           else
    389             fits_info.endian=LSBEndian;
    390         }
    391       (void) FormatLocaleString(property,MagickPathExtent,"fits:%s",keyword);
    392       (void) SetImageProperty(image,property,p,exception);
    393     }
    394     c=0;
    395     while (((TellBlob(image) % FITSBlocksize) != 0) && (c != EOF))
    396       c=ReadBlobByte(image);
    397     if (fits_info.extend == MagickFalse)
    398       break;
    399     if ((fits_info.bits_per_pixel != 8) && (fits_info.bits_per_pixel != 16) &&
    400         (fits_info.bits_per_pixel != 32) && (fits_info.bits_per_pixel != 64) &&
    401         (fits_info.bits_per_pixel != -32) && (fits_info.bits_per_pixel != -64))
    402       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    403     number_pixels=(MagickSizeType) fits_info.columns*fits_info.rows;
    404     if ((fits_info.simple != MagickFalse) && (fits_info.number_axes >= 1) &&
    405         (fits_info.number_axes <= 4) && (number_pixels != 0))
    406       break;
    407   }
    408   /*
    409     Verify that required image information is defined.
    410   */
    411   if (comment != (char *) NULL)
    412     {
    413       (void) SetImageProperty(image,"comment",comment,exception);
    414       comment=DestroyString(comment);
    415     }
    416   if (EOFBlob(image) != MagickFalse)
    417     ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
    418       image->filename);
    419   number_pixels=(MagickSizeType) fits_info.columns*fits_info.rows;
    420   if ((fits_info.simple == MagickFalse) || (fits_info.number_axes < 1) ||
    421       (fits_info.number_axes > 4) || (number_pixels == 0))
    422     ThrowReaderException(CorruptImageError,"ImageTypeNotSupported");
    423   for (scene=0; scene < (ssize_t) fits_info.number_planes; scene++)
    424   {
    425     image->columns=(size_t) fits_info.columns;
    426     image->rows=(size_t) fits_info.rows;
    427     image->depth=(size_t) (fits_info.bits_per_pixel < 0 ? -1 : 1)*
    428       fits_info.bits_per_pixel;
    429     image->endian=fits_info.endian;
    430     image->scene=(size_t) scene;
    431     if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
    432       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
    433         break;
    434     status=SetImageExtent(image,image->columns,image->rows,exception);
    435     if (status == MagickFalse)
    436       return(DestroyImageList(image));
    437     /*
    438       Initialize image structure.
    439     */
    440     (void) SetImageColorspace(image,GRAYColorspace,exception);
    441     if ((fits_info.min_data == 0.0) && (fits_info.max_data == 0.0))
    442       {
    443         if (fits_info.zero == 0.0)
    444           (void) GetFITSPixelExtrema(image,fits_info.bits_per_pixel,
    445             &fits_info.min_data,&fits_info.max_data);
    446         else
    447           fits_info.max_data=GetFITSPixelRange((size_t)
    448             fits_info.bits_per_pixel);
    449       }
    450     else
    451       fits_info.max_data=GetFITSPixelRange((size_t) fits_info.bits_per_pixel);
    452     /*
    453       Convert FITS pixels to pixel packets.
    454     */
    455     scale=QuantumRange/(fits_info.max_data-fits_info.min_data);
    456     for (y=(ssize_t) image->rows-1; y >= 0; y--)
    457     {
    458       q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
    459       if (q == (Quantum *) NULL)
    460         break;
    461       for (x=0; x < (ssize_t) image->columns; x++)
    462       {
    463         pixel=GetFITSPixel(image,fits_info.bits_per_pixel);
    464         if ((image->depth == 16) || (image->depth == 32) ||
    465             (image->depth == 64))
    466           SetFITSUnsignedPixels(1,image->depth,image->endian,
    467             (unsigned char *) &pixel);
    468         SetPixelGray(image,ClampToQuantum(scale*(fits_info.scale*(pixel-
    469           fits_info.min_data)+fits_info.zero)),q);
    470         q+=GetPixelChannels(image);
    471       }
    472       if (SyncAuthenticPixels(image,exception) == MagickFalse)
    473         break;
    474       if (image->previous == (Image *) NULL)
    475         {
    476           status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
    477             image->rows);
    478           if (status == MagickFalse)
    479             break;
    480         }
    481     }
    482     if (EOFBlob(image) != MagickFalse)
    483       {
    484         ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
    485           image->filename);
    486         break;
    487       }
    488     /*
    489       Proceed to next image.
    490     */
    491     if (image_info->number_scenes != 0)
    492       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
    493         break;
    494     if (scene < (ssize_t) (fits_info.number_planes-1))
    495       {
    496         /*
    497           Allocate next image structure.
    498         */
    499         AcquireNextImage(image_info,image,exception);
    500         if (GetNextImageInList(image) == (Image *) NULL)
    501           {
    502             image=DestroyImageList(image);
    503             return((Image *) NULL);
    504           }
    505         image=SyncNextImageInList(image);
    506         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
    507           GetBlobSize(image));
    508         if (status == MagickFalse)
    509           break;
    510       }
    511   }
    512   (void) CloseBlob(image);
    513   return(GetFirstImageInList(image));
    514 }
    515 
    516 /*
    518 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    519 %                                                                             %
    520 %                                                                             %
    521 %                                                                             %
    522 %   R e g i s t e r F I T S I m a g e                                         %
    523 %                                                                             %
    524 %                                                                             %
    525 %                                                                             %
    526 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    527 %
    528 %  RegisterFITSImage() adds attributes for the FITS image format to
    529 %  the list of supported formats.  The attributes include the image format
    530 %  tag, a method to read and/or write the format, whether the format
    531 %  supports the saving of more than one frame to the same file or blob,
    532 %  whether the format supports native in-memory I/O, and a brief
    533 %  description of the format.
    534 %
    535 %  The format of the RegisterFITSImage method is:
    536 %
    537 %      size_t RegisterFITSImage(void)
    538 %
    539 */
    540 ModuleExport size_t RegisterFITSImage(void)
    541 {
    542   MagickInfo
    543     *entry;
    544 
    545   entry=AcquireMagickInfo("FITS","FITS","Flexible Image Transport System");
    546   entry->decoder=(DecodeImageHandler *) ReadFITSImage;
    547   entry->encoder=(EncodeImageHandler *) WriteFITSImage;
    548   entry->magick=(IsImageFormatHandler *) IsFITS;
    549   entry->flags^=CoderAdjoinFlag;
    550   entry->flags|=CoderSeekableStreamFlag;
    551   (void) RegisterMagickInfo(entry);
    552   entry=AcquireMagickInfo("FITS","FTS","Flexible Image Transport System");
    553   entry->decoder=(DecodeImageHandler *) ReadFITSImage;
    554   entry->encoder=(EncodeImageHandler *) WriteFITSImage;
    555   entry->magick=(IsImageFormatHandler *) IsFITS;
    556   entry->flags^=CoderAdjoinFlag;
    557   entry->flags|=CoderSeekableStreamFlag;
    558   (void) RegisterMagickInfo(entry);
    559   return(MagickImageCoderSignature);
    560 }
    561 
    562 /*
    564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    565 %                                                                             %
    566 %                                                                             %
    567 %                                                                             %
    568 %   U n r e g i s t e r F I T S I m a g e                                     %
    569 %                                                                             %
    570 %                                                                             %
    571 %                                                                             %
    572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    573 %
    574 %  UnregisterFITSImage() removes format registrations made by the
    575 %  FITS module from the list of supported formats.
    576 %
    577 %  The format of the UnregisterFITSImage method is:
    578 %
    579 %      UnregisterFITSImage(void)
    580 %
    581 */
    582 ModuleExport void UnregisterFITSImage(void)
    583 {
    584   (void) UnregisterMagickInfo("FITS");
    585   (void) UnregisterMagickInfo("FTS");
    586 }
    587 
    588 /*
    590 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    591 %                                                                             %
    592 %                                                                             %
    593 %                                                                             %
    594 %   W r i t e F I T S I m a g e                                               %
    595 %                                                                             %
    596 %                                                                             %
    597 %                                                                             %
    598 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    599 %
    600 %  WriteFITSImage() writes a Flexible Image Transport System image to a
    601 %  file as gray scale intensities [0..255].
    602 %
    603 %  The format of the WriteFITSImage method is:
    604 %
    605 %      MagickBooleanType WriteFITSImage(const ImageInfo *image_info,
    606 %        Image *image,ExceptionInfo *exception)
    607 %
    608 %  A description of each parameter follows.
    609 %
    610 %    o image_info: the image info.
    611 %
    612 %    o image:  The image.
    613 %
    614 %    o exception: return any errors or warnings in this structure.
    615 %
    616 */
    617 static MagickBooleanType WriteFITSImage(const ImageInfo *image_info,
    618   Image *image,ExceptionInfo *exception)
    619 {
    620   char
    621     header[FITSBlocksize],
    622     *fits_info;
    623 
    624   MagickBooleanType
    625     status;
    626 
    627   QuantumInfo
    628     *quantum_info;
    629 
    630   register const Quantum
    631     *p;
    632 
    633   size_t
    634     length;
    635 
    636   ssize_t
    637     count,
    638     offset,
    639     y;
    640 
    641   unsigned char
    642     *pixels;
    643 
    644   /*
    645     Open output image file.
    646   */
    647   assert(image_info != (const ImageInfo *) NULL);
    648   assert(image_info->signature == MagickCoreSignature);
    649   assert(image != (Image *) NULL);
    650   assert(image->signature == MagickCoreSignature);
    651   if (image->debug != MagickFalse)
    652     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    653   assert(exception != (ExceptionInfo *) NULL);
    654   assert(exception->signature == MagickCoreSignature);
    655   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
    656   if (status == MagickFalse)
    657     return(status);
    658   (void) TransformImageColorspace(image,sRGBColorspace,exception);
    659   /*
    660     Allocate image memory.
    661   */
    662   fits_info=(char *) AcquireQuantumMemory(FITSBlocksize,sizeof(*fits_info));
    663   if (fits_info == (char *) NULL)
    664     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
    665   (void) ResetMagickMemory(fits_info,' ',FITSBlocksize*sizeof(*fits_info));
    666   /*
    667     Initialize image header.
    668   */
    669   image->depth=GetImageQuantumDepth(image,MagickFalse);
    670   image->endian=MSBEndian;
    671   quantum_info=AcquireQuantumInfo(image_info,image);
    672   if (quantum_info == (QuantumInfo *) NULL)
    673     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
    674   offset=0;
    675   (void) FormatLocaleString(header,FITSBlocksize,
    676     "SIMPLE  =                    T");
    677   (void) strncpy(fits_info+offset,header,strlen(header));
    678   offset+=80;
    679   (void) FormatLocaleString(header,FITSBlocksize,"BITPIX  =           %10ld",
    680     (long) ((quantum_info->format == FloatingPointQuantumFormat ? -1 : 1)*
    681     image->depth));
    682   (void) strncpy(fits_info+offset,header,strlen(header));
    683   offset+=80;
    684   (void) FormatLocaleString(header,FITSBlocksize,"NAXIS   =           %10lu",
    685     SetImageGray(image,exception) != MagickFalse ? 2UL : 3UL);
    686   (void) strncpy(fits_info+offset,header,strlen(header));
    687   offset+=80;
    688   (void) FormatLocaleString(header,FITSBlocksize,"NAXIS1  =           %10lu",
    689     (unsigned long) image->columns);
    690   (void) strncpy(fits_info+offset,header,strlen(header));
    691   offset+=80;
    692   (void) FormatLocaleString(header,FITSBlocksize,"NAXIS2  =           %10lu",
    693     (unsigned long) image->rows);
    694   (void) strncpy(fits_info+offset,header,strlen(header));
    695   offset+=80;
    696   if (SetImageGray(image,exception) == MagickFalse)
    697     {
    698       (void) FormatLocaleString(header,FITSBlocksize,
    699         "NAXIS3  =           %10lu",3UL);
    700       (void) strncpy(fits_info+offset,header,strlen(header));
    701       offset+=80;
    702     }
    703   (void) FormatLocaleString(header,FITSBlocksize,"BSCALE  =         %E",1.0);
    704   (void) strncpy(fits_info+offset,header,strlen(header));
    705   offset+=80;
    706   (void) FormatLocaleString(header,FITSBlocksize,"BZERO   =         %E",
    707     image->depth > 8 ? GetFITSPixelRange(image->depth)/2.0 : 0.0);
    708   (void) strncpy(fits_info+offset,header,strlen(header));
    709   offset+=80;
    710   (void) FormatLocaleString(header,FITSBlocksize,"DATAMAX =         %E",
    711     1.0*((MagickOffsetType) GetQuantumRange(image->depth)));
    712   (void) strncpy(fits_info+offset,header,strlen(header));
    713   offset+=80;
    714   (void) FormatLocaleString(header,FITSBlocksize,"DATAMIN =         %E",0.0);
    715   (void) strncpy(fits_info+offset,header,strlen(header));
    716   offset+=80;
    717   if (image->endian == LSBEndian)
    718     {
    719       (void) FormatLocaleString(header,FITSBlocksize,"XENDIAN = 'SMALL'");
    720       (void) strncpy(fits_info+offset,header,strlen(header));
    721       offset+=80;
    722     }
    723   (void) FormatLocaleString(header,FITSBlocksize,"HISTORY %.72s",
    724     GetMagickVersion((size_t *) NULL));
    725   (void) strncpy(fits_info+offset,header,strlen(header));
    726   offset+=80;
    727   (void) strncpy(header,"END",FITSBlocksize);
    728   (void) strncpy(fits_info+offset,header,strlen(header));
    729   offset+=80;
    730   (void) WriteBlob(image,FITSBlocksize,(unsigned char *) fits_info);
    731   /*
    732     Convert image to fits scale PseudoColor class.
    733   */
    734   pixels=(unsigned char *) GetQuantumPixels(quantum_info);
    735   if (SetImageGray(image,exception) != MagickFalse)
    736     {
    737       length=GetQuantumExtent(image,quantum_info,GrayQuantum);
    738       for (y=(ssize_t) image->rows-1; y >= 0; y--)
    739       {
    740         p=GetVirtualPixels(image,0,y,image->columns,1,exception);
    741         if (p == (const Quantum *) NULL)
    742           break;
    743         length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
    744           GrayQuantum,pixels,exception);
    745         if (image->depth == 16)
    746           SetFITSUnsignedPixels(image->columns,image->depth,image->endian,
    747             pixels);
    748         if (((image->depth == 32) || (image->depth == 64)) &&
    749             (quantum_info->format != FloatingPointQuantumFormat))
    750           SetFITSUnsignedPixels(image->columns,image->depth,image->endian,
    751             pixels);
    752         count=WriteBlob(image,length,pixels);
    753         if (count != (ssize_t) length)
    754           break;
    755         status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
    756           image->rows);
    757         if (status == MagickFalse)
    758           break;
    759       }
    760     }
    761   else
    762     {
    763       length=GetQuantumExtent(image,quantum_info,RedQuantum);
    764       for (y=(ssize_t) image->rows-1; y >= 0; y--)
    765       {
    766         p=GetVirtualPixels(image,0,y,image->columns,1,exception);
    767         if (p == (const Quantum *) NULL)
    768           break;
    769         length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
    770           RedQuantum,pixels,exception);
    771         if (image->depth == 16)
    772           SetFITSUnsignedPixels(image->columns,image->depth,image->endian,
    773             pixels);
    774         if (((image->depth == 32) || (image->depth == 64)) &&
    775             (quantum_info->format != FloatingPointQuantumFormat))
    776           SetFITSUnsignedPixels(image->columns,image->depth,image->endian,
    777             pixels);
    778         count=WriteBlob(image,length,pixels);
    779         if (count != (ssize_t) length)
    780           break;
    781         status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
    782           image->rows);
    783         if (status == MagickFalse)
    784           break;
    785       }
    786       length=GetQuantumExtent(image,quantum_info,GreenQuantum);
    787       for (y=(ssize_t) image->rows-1; y >= 0; y--)
    788       {
    789         p=GetVirtualPixels(image,0,y,image->columns,1,exception);
    790         if (p == (const Quantum *) NULL)
    791           break;
    792         length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
    793           GreenQuantum,pixels,exception);
    794         if (image->depth == 16)
    795           SetFITSUnsignedPixels(image->columns,image->depth,image->endian,
    796             pixels);
    797         if (((image->depth == 32) || (image->depth == 64)) &&
    798             (quantum_info->format != FloatingPointQuantumFormat))
    799           SetFITSUnsignedPixels(image->columns,image->depth,image->endian,
    800             pixels);
    801         count=WriteBlob(image,length,pixels);
    802         if (count != (ssize_t) length)
    803           break;
    804         status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
    805           image->rows);
    806         if (status == MagickFalse)
    807           break;
    808       }
    809       length=GetQuantumExtent(image,quantum_info,BlueQuantum);
    810       for (y=(ssize_t) image->rows-1; y >= 0; y--)
    811       {
    812         p=GetVirtualPixels(image,0,y,image->columns,1,exception);
    813         if (p == (const Quantum *) NULL)
    814           break;
    815         length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
    816           BlueQuantum,pixels,exception);
    817         if (image->depth == 16)
    818           SetFITSUnsignedPixels(image->columns,image->depth,image->endian,
    819             pixels);
    820         if (((image->depth == 32) || (image->depth == 64)) &&
    821             (quantum_info->format != FloatingPointQuantumFormat))
    822           SetFITSUnsignedPixels(image->columns,image->depth,image->endian,
    823             pixels);
    824         count=WriteBlob(image,length,pixels);
    825         if (count != (ssize_t) length)
    826           break;
    827         status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
    828           image->rows);
    829         if (status == MagickFalse)
    830           break;
    831       }
    832     }
    833   quantum_info=DestroyQuantumInfo(quantum_info);
    834   length=(size_t) (FITSBlocksize-TellBlob(image) % FITSBlocksize);
    835   if (length != 0)
    836     {
    837       (void) ResetMagickMemory(fits_info,0,length*sizeof(*fits_info));
    838       (void) WriteBlob(image,length,(unsigned char *) fits_info);
    839     }
    840   fits_info=DestroyString(fits_info);
    841   (void) CloseBlob(image);
    842   return(MagickTrue);
    843 }
    844