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