Home | History | Annotate | Download | only in coders
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %                            H   H  DDDD   RRRR                               %
      7 %                            H   H  D   D  R   R                              %
      8 %                            HHHHH  D   D  RRRR                               %
      9 %                            H   H  D   D  R R                                %
     10 %                            H   H  DDDD   R  R                               %
     11 %                                                                             %
     12 %                                                                             %
     13 %                   Read/Write Radiance RGBE Image Format                     %
     14 %                                                                             %
     15 %                              Software Design                                %
     16 %                                   Cristy                                    %
     17 %                                 July 1992                                   %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2019 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 %    https://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/blob.h"
     45 #include "MagickCore/blob-private.h"
     46 #include "MagickCore/cache.h"
     47 #include "MagickCore/colorspace.h"
     48 #include "MagickCore/colorspace-private.h"
     49 #include "MagickCore/exception.h"
     50 #include "MagickCore/exception-private.h"
     51 #include "MagickCore/image.h"
     52 #include "MagickCore/image-private.h"
     53 #include "MagickCore/list.h"
     54 #include "MagickCore/magick.h"
     55 #include "MagickCore/memory_.h"
     56 #include "MagickCore/monitor.h"
     57 #include "MagickCore/monitor-private.h"
     58 #include "MagickCore/pixel-accessor.h"
     59 #include "MagickCore/property.h"
     60 #include "MagickCore/quantum-private.h"
     61 #include "MagickCore/static.h"
     62 #include "MagickCore/string_.h"
     63 #include "MagickCore/string-private.h"
     64 #include "MagickCore/module.h"
     65 
     66 /*
     68   Forward declarations.
     69 */
     70 static MagickBooleanType
     71   WriteHDRImage(const ImageInfo *,Image *,ExceptionInfo *);
     72 
     73 /*
     75 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     76 %                                                                             %
     77 %                                                                             %
     78 %                                                                             %
     79 %   I s H D R                                                                 %
     80 %                                                                             %
     81 %                                                                             %
     82 %                                                                             %
     83 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     84 %
     85 %  IsHDR() returns MagickTrue if the image format type, identified by the
     86 %  magick string, is Radiance RGBE image format.
     87 %
     88 %  The format of the IsHDR method is:
     89 %
     90 %      MagickBooleanType IsHDR(const unsigned char *magick,
     91 %        const size_t length)
     92 %
     93 %  A description of each parameter follows:
     94 %
     95 %    o magick: compare image format pattern against these bytes.
     96 %
     97 %    o length: Specifies the length of the magick string.
     98 %
     99 */
    100 static MagickBooleanType IsHDR(const unsigned char *magick,
    101   const size_t length)
    102 {
    103   if (length < 10)
    104     return(MagickFalse);
    105   if (LocaleNCompare((const char *) magick,"#?RADIANCE",10) == 0)
    106     return(MagickTrue);
    107   if (LocaleNCompare((const char *) magick,"#?RGBE",6) == 0)
    108     return(MagickTrue);
    109   return(MagickFalse);
    110 }
    111 
    112 /*
    114 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    115 %                                                                             %
    116 %                                                                             %
    117 %                                                                             %
    118 %   R e a d H D R I m a g e                                                   %
    119 %                                                                             %
    120 %                                                                             %
    121 %                                                                             %
    122 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    123 %
    124 %  ReadHDRImage() reads the Radiance RGBE image format and returns it.  It
    125 %  allocates the memory necessary for the new Image structure and returns a
    126 %  pointer to the new image.
    127 %
    128 %  The format of the ReadHDRImage method is:
    129 %
    130 %      Image *ReadHDRImage(const ImageInfo *image_info,ExceptionInfo *exception)
    131 %
    132 %  A description of each parameter follows:
    133 %
    134 %    o image_info: the image info.
    135 %
    136 %    o exception: return any errors or warnings in this structure.
    137 %
    138 */
    139 static Image *ReadHDRImage(const ImageInfo *image_info,ExceptionInfo *exception)
    140 {
    141   char
    142     format[MagickPathExtent],
    143     keyword[MagickPathExtent],
    144     tag[MagickPathExtent],
    145     value[MagickPathExtent];
    146 
    147   double
    148     gamma;
    149 
    150   Image
    151     *image;
    152 
    153   int
    154     c;
    155 
    156   MagickBooleanType
    157     status,
    158     value_expected;
    159 
    160   register Quantum
    161     *q;
    162 
    163   register ssize_t
    164     i,
    165     x;
    166 
    167   register unsigned char
    168     *p;
    169 
    170   ssize_t
    171     count,
    172     y;
    173 
    174   unsigned char
    175     *end,
    176     pixel[4],
    177     *pixels;
    178 
    179   /*
    180     Open image file.
    181   */
    182   assert(image_info != (const ImageInfo *) NULL);
    183   assert(image_info->signature == MagickCoreSignature);
    184   if (image_info->debug != MagickFalse)
    185     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
    186       image_info->filename);
    187   assert(exception != (ExceptionInfo *) NULL);
    188   assert(exception->signature == MagickCoreSignature);
    189   image=AcquireImage(image_info,exception);
    190   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
    191   if (status == MagickFalse)
    192     {
    193       image=DestroyImageList(image);
    194       return((Image *) NULL);
    195     }
    196   /*
    197     Decode image header.
    198   */
    199   image->columns=0;
    200   image->rows=0;
    201   *format='\0';
    202   c=ReadBlobByte(image);
    203   if (c == EOF)
    204     {
    205       image=DestroyImage(image);
    206       return((Image *) NULL);
    207     }
    208   while (isgraph(c) && (image->columns == 0) && (image->rows == 0))
    209   {
    210     if (c == (int) '#')
    211       {
    212         char
    213           *comment;
    214 
    215         register char
    216           *p;
    217 
    218         size_t
    219           length;
    220 
    221         /*
    222           Read comment-- any text between # and end-of-line.
    223         */
    224         length=MagickPathExtent;
    225         comment=AcquireString((char *) NULL);
    226         for (p=comment; comment != (char *) NULL; p++)
    227         {
    228           c=ReadBlobByte(image);
    229           if ((c == EOF) || (c == (int) '\n'))
    230             break;
    231           if ((size_t) (p-comment+1) >= length)
    232             {
    233               *p='\0';
    234               length<<=1;
    235               comment=(char *) ResizeQuantumMemory(comment,length+
    236                 MagickPathExtent,sizeof(*comment));
    237               if (comment == (char *) NULL)
    238                 break;
    239               p=comment+strlen(comment);
    240             }
    241           *p=(char) c;
    242         }
    243         if (comment == (char *) NULL)
    244           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    245         *p='\0';
    246         (void) SetImageProperty(image,"comment",comment,exception);
    247         comment=DestroyString(comment);
    248         c=ReadBlobByte(image);
    249       }
    250     else
    251       if (isalnum(c) == MagickFalse)
    252         c=ReadBlobByte(image);
    253       else
    254         {
    255           register char
    256             *p;
    257 
    258           /*
    259             Determine a keyword and its value.
    260           */
    261           p=keyword;
    262           do
    263           {
    264             if ((size_t) (p-keyword) < (MagickPathExtent-1))
    265               *p++=c;
    266             c=ReadBlobByte(image);
    267           } while (isalnum(c) || (c == '_'));
    268           *p='\0';
    269           value_expected=MagickFalse;
    270           while ((isspace((int) ((unsigned char) c)) != 0) || (c == '='))
    271           {
    272             if (c == '=')
    273               value_expected=MagickTrue;
    274             c=ReadBlobByte(image);
    275           }
    276           if (LocaleCompare(keyword,"Y") == 0)
    277             value_expected=MagickTrue;
    278           if (value_expected == MagickFalse)
    279             continue;
    280           p=value;
    281           while ((c != '\n') && (c != '\0') && (c != EOF))
    282           {
    283             if ((size_t) (p-value) < (MagickPathExtent-1))
    284               *p++=c;
    285             c=ReadBlobByte(image);
    286           }
    287           *p='\0';
    288           /*
    289             Assign a value to the specified keyword.
    290           */
    291           switch (*keyword)
    292           {
    293             case 'F':
    294             case 'f':
    295             {
    296               if (LocaleCompare(keyword,"format") == 0)
    297                 {
    298                   (void) CopyMagickString(format,value,MagickPathExtent);
    299                   break;
    300                 }
    301               (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
    302               (void) SetImageProperty(image,tag,value,exception);
    303               break;
    304             }
    305             case 'G':
    306             case 'g':
    307             {
    308               if (LocaleCompare(keyword,"gamma") == 0)
    309                 {
    310                   image->gamma=StringToDouble(value,(char **) NULL);
    311                   break;
    312                 }
    313               (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
    314               (void) SetImageProperty(image,tag,value,exception);
    315               break;
    316             }
    317             case 'P':
    318             case 'p':
    319             {
    320               if (LocaleCompare(keyword,"primaries") == 0)
    321                 {
    322                   float
    323                     chromaticity[6],
    324                     white_point[2];
    325 
    326                   int
    327                     count;
    328 
    329                   count=sscanf(value,"%g %g %g %g %g %g %g %g",&chromaticity[0],
    330                     &chromaticity[1],&chromaticity[2],&chromaticity[3],
    331                     &chromaticity[4],&chromaticity[5],&white_point[0],
    332                     &white_point[1]);
    333                   if (count == 8)
    334                     {
    335                       image->chromaticity.red_primary.x=chromaticity[0];
    336                       image->chromaticity.red_primary.y=chromaticity[1];
    337                       image->chromaticity.green_primary.x=chromaticity[2];
    338                       image->chromaticity.green_primary.y=chromaticity[3];
    339                       image->chromaticity.blue_primary.x=chromaticity[4];
    340                       image->chromaticity.blue_primary.y=chromaticity[5];
    341                       image->chromaticity.white_point.x=white_point[0],
    342                       image->chromaticity.white_point.y=white_point[1];
    343                     }
    344                   break;
    345                 }
    346               (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
    347               (void) SetImageProperty(image,tag,value,exception);
    348               break;
    349             }
    350             case 'Y':
    351             case 'y':
    352             {
    353               char
    354                 target[] = "Y";
    355 
    356               if (strcmp(keyword,target) == 0)
    357                 {
    358                   int
    359                     height,
    360                     width;
    361 
    362                   if (sscanf(value,"%d +X %d",&height,&width) == 2)
    363                     {
    364                       image->columns=(size_t) width;
    365                       image->rows=(size_t) height;
    366                     }
    367                   break;
    368                 }
    369               (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
    370               (void) SetImageProperty(image,tag,value,exception);
    371               break;
    372             }
    373             default:
    374             {
    375               (void) FormatLocaleString(tag,MagickPathExtent,"hdr:%s",keyword);
    376               (void) SetImageProperty(image,tag,value,exception);
    377               break;
    378             }
    379           }
    380         }
    381     if ((image->columns == 0) && (image->rows == 0))
    382       while (isspace((int) ((unsigned char) c)) != 0)
    383         c=ReadBlobByte(image);
    384   }
    385   if ((LocaleCompare(format,"32-bit_rle_rgbe") != 0) &&
    386       (LocaleCompare(format,"32-bit_rle_xyze") != 0))
    387     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    388   if ((image->columns == 0) || (image->rows == 0))
    389     ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
    390   (void) SetImageColorspace(image,RGBColorspace,exception);
    391   if (LocaleCompare(format,"32-bit_rle_xyze") == 0)
    392     (void) SetImageColorspace(image,XYZColorspace,exception);
    393   image->compression=(image->columns < 8) || (image->columns > 0x7ffff) ?
    394     NoCompression : RLECompression;
    395   if (image_info->ping != MagickFalse)
    396     {
    397       (void) CloseBlob(image);
    398       return(GetFirstImageInList(image));
    399     }
    400   status=SetImageExtent(image,image->columns,image->rows,exception);
    401   if (status == MagickFalse)
    402     return(DestroyImageList(image));
    403   /*
    404     Read RGBE (red+green+blue+exponent) pixels.
    405   */
    406   pixels=(unsigned char *) AcquireQuantumMemory(image->columns,4*
    407     sizeof(*pixels));
    408   if (pixels == (unsigned char *) NULL)
    409     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    410   (void) memset(pixels,0,4*image->columns*sizeof(*pixels));
    411   for (y=0; y < (ssize_t) image->rows; y++)
    412   {
    413     if (image->compression != RLECompression)
    414       {
    415         count=ReadBlob(image,4*image->columns*sizeof(*pixels),pixels);
    416         if (count != (ssize_t) (4*image->columns*sizeof(*pixels)))
    417           break;
    418       }
    419     else
    420       {
    421         count=ReadBlob(image,4*sizeof(*pixel),pixel);
    422         if (count != 4)
    423           break;
    424         if ((size_t) ((((size_t) pixel[2]) << 8) | pixel[3]) != image->columns)
    425           {
    426             (void) memcpy(pixels,pixel,4*sizeof(*pixel));
    427             count=ReadBlob(image,4*(image->columns-1)*sizeof(*pixels),pixels+4);
    428             image->compression=NoCompression;
    429           }
    430         else
    431           {
    432             p=pixels;
    433             for (i=0; i < 4; i++)
    434             {
    435               end=&pixels[(i+1)*image->columns];
    436               while (p < end)
    437               {
    438                 count=ReadBlob(image,2*sizeof(*pixel),pixel);
    439                 if (count < 1)
    440                   break;
    441                 if (pixel[0] > 128)
    442                   {
    443                     count=(ssize_t) pixel[0]-128;
    444                     if ((count == 0) || (count > (ssize_t) (end-p)))
    445                       break;
    446                     while (count-- > 0)
    447                       *p++=pixel[1];
    448                   }
    449                 else
    450                   {
    451                     count=(ssize_t) pixel[0];
    452                     if ((count == 0) || (count > (ssize_t) (end-p)))
    453                       break;
    454                     *p++=pixel[1];
    455                     if (--count > 0)
    456                       {
    457                         count=ReadBlob(image,(size_t) count*sizeof(*p),p);
    458                         if (count < 1)
    459                           break;
    460                         p+=count;
    461                       }
    462                   }
    463               }
    464             }
    465           }
    466       }
    467     q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
    468     if (q == (Quantum *) NULL)
    469       break;
    470     i=0;
    471     for (x=0; x < (ssize_t) image->columns; x++)
    472     {
    473       if (image->compression == RLECompression)
    474         {
    475           pixel[0]=pixels[x];
    476           pixel[1]=pixels[x+image->columns];
    477           pixel[2]=pixels[x+2*image->columns];
    478           pixel[3]=pixels[x+3*image->columns];
    479         }
    480       else
    481         {
    482           pixel[0]=pixels[i++];
    483           pixel[1]=pixels[i++];
    484           pixel[2]=pixels[i++];
    485           pixel[3]=pixels[i++];
    486         }
    487       SetPixelRed(image,0,q);
    488       SetPixelGreen(image,0,q);
    489       SetPixelBlue(image,0,q);
    490       if (pixel[3] != 0)
    491         {
    492           gamma=pow(2.0,pixel[3]-(128.0+8.0));
    493           SetPixelRed(image,ClampToQuantum(QuantumRange*gamma*pixel[0]),q);
    494           SetPixelGreen(image,ClampToQuantum(QuantumRange*gamma*pixel[1]),q);
    495           SetPixelBlue(image,ClampToQuantum(QuantumRange*gamma*pixel[2]),q);
    496         }
    497       q+=GetPixelChannels(image);
    498     }
    499     if (SyncAuthenticPixels(image,exception) == MagickFalse)
    500       break;
    501     status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
    502       image->rows);
    503     if (status == MagickFalse)
    504       break;
    505   }
    506   pixels=(unsigned char *) RelinquishMagickMemory(pixels);
    507   if (EOFBlob(image) != MagickFalse)
    508     ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
    509       image->filename);
    510   (void) CloseBlob(image);
    511   return(GetFirstImageInList(image));
    512 }
    513 
    514 /*
    516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    517 %                                                                             %
    518 %                                                                             %
    519 %                                                                             %
    520 %   R e g i s t e r H D R I m a g e                                           %
    521 %                                                                             %
    522 %                                                                             %
    523 %                                                                             %
    524 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    525 %
    526 %  RegisterHDRImage() adds attributes for the Radiance RGBE image format to the
    527 %  list of supported formats.  The attributes include the image format tag, a
    528 %  method to read and/or write the format, whether the format supports the
    529 %  saving of more than one frame to the same file or blob, whether the format
    530 %  supports native in-memory I/O, and a brief description of the format.
    531 %
    532 %  The format of the RegisterHDRImage method is:
    533 %
    534 %      size_t RegisterHDRImage(void)
    535 %
    536 */
    537 ModuleExport size_t RegisterHDRImage(void)
    538 {
    539   MagickInfo
    540     *entry;
    541 
    542   entry=AcquireMagickInfo("HDR","HDR","Radiance RGBE image format");
    543   entry->decoder=(DecodeImageHandler *) ReadHDRImage;
    544   entry->encoder=(EncodeImageHandler *) WriteHDRImage;
    545   entry->magick=(IsImageFormatHandler *) IsHDR;
    546   (void) RegisterMagickInfo(entry);
    547   return(MagickImageCoderSignature);
    548 }
    549 
    550 /*
    552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    553 %                                                                             %
    554 %                                                                             %
    555 %                                                                             %
    556 %   U n r e g i s t e r H D R I m a g e                                       %
    557 %                                                                             %
    558 %                                                                             %
    559 %                                                                             %
    560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    561 %
    562 %  UnregisterHDRImage() removes format registrations made by the
    563 %  HDR module from the list of supported formats.
    564 %
    565 %  The format of the UnregisterHDRImage method is:
    566 %
    567 %      UnregisterHDRImage(void)
    568 %
    569 */
    570 ModuleExport void UnregisterHDRImage(void)
    571 {
    572   (void) UnregisterMagickInfo("HDR");
    573 }
    574 
    575 /*
    577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    578 %                                                                             %
    579 %                                                                             %
    580 %                                                                             %
    581 %   W r i t e H D R I m a g e                                                 %
    582 %                                                                             %
    583 %                                                                             %
    584 %                                                                             %
    585 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    586 %
    587 %  WriteHDRImage() writes an image in the Radience RGBE image format.
    588 %
    589 %  The format of the WriteHDRImage method is:
    590 %
    591 %      MagickBooleanType WriteHDRImage(const ImageInfo *image_info,
    592 %        Image *image,ExceptionInfo *exception)
    593 %
    594 %  A description of each parameter follows.
    595 %
    596 %    o image_info: the image info.
    597 %
    598 %    o image:  The image.
    599 %
    600 */
    601 
    602 static size_t HDRWriteRunlengthPixels(Image *image,unsigned char *pixels)
    603 {
    604 #define MinimumRunlength 4
    605 
    606   register size_t
    607     p,
    608     q;
    609 
    610   size_t
    611     runlength;
    612 
    613   ssize_t
    614     count,
    615     previous_count;
    616 
    617   unsigned char
    618     pixel[2];
    619 
    620   for (p=0; p < image->columns; )
    621   {
    622     q=p;
    623     runlength=0;
    624     previous_count=0;
    625     while ((runlength < MinimumRunlength) && (q < image->columns))
    626     {
    627       q+=runlength;
    628       previous_count=(ssize_t) runlength;
    629       runlength=1;
    630       while ((pixels[q] == pixels[q+runlength]) &&
    631              ((q+runlength) < image->columns) && (runlength < 127))
    632        runlength++;
    633     }
    634     if ((previous_count > 1) && (previous_count == (ssize_t) (q-p)))
    635       {
    636         pixel[0]=(unsigned char) (128+previous_count);
    637         pixel[1]=pixels[p];
    638         if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1)
    639           break;
    640         p=q;
    641       }
    642     while (p < q)
    643     {
    644       count=(ssize_t) (q-p);
    645       if (count > 128)
    646         count=128;
    647       pixel[0]=(unsigned char) count;
    648       if (WriteBlob(image,sizeof(*pixel),pixel) < 1)
    649         break;
    650       if (WriteBlob(image,(size_t) count*sizeof(*pixel),&pixels[p]) < 1)
    651         break;
    652       p+=count;
    653     }
    654     if (runlength >= MinimumRunlength)
    655       {
    656         pixel[0]=(unsigned char) (128+runlength);
    657         pixel[1]=pixels[q];
    658         if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1)
    659           break;
    660         p+=runlength;
    661       }
    662   }
    663   return(p);
    664 }
    665 
    666 static MagickBooleanType WriteHDRImage(const ImageInfo *image_info,Image *image,
    667   ExceptionInfo *exception)
    668 {
    669   char
    670     header[MagickPathExtent];
    671 
    672   const char
    673     *property;
    674 
    675   MagickBooleanType
    676     status;
    677 
    678   register const Quantum
    679     *p;
    680 
    681   register ssize_t
    682     i,
    683     x;
    684 
    685   size_t
    686     length;
    687 
    688   ssize_t
    689     count,
    690     y;
    691 
    692   unsigned char
    693     pixel[4],
    694     *pixels;
    695 
    696   /*
    697     Open output image file.
    698   */
    699   assert(image_info != (const ImageInfo *) NULL);
    700   assert(image_info->signature == MagickCoreSignature);
    701   assert(image != (Image *) NULL);
    702   assert(image->signature == MagickCoreSignature);
    703   if (image->debug != MagickFalse)
    704     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    705   assert(exception != (ExceptionInfo *) NULL);
    706   assert(exception->signature == MagickCoreSignature);
    707   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
    708   if (status == MagickFalse)
    709     return(status);
    710   if (IsRGBColorspace(image->colorspace) == MagickFalse)
    711     (void) TransformImageColorspace(image,RGBColorspace,exception);
    712   /*
    713     Write header.
    714   */
    715   (void) memset(header,' ',MagickPathExtent);
    716   length=CopyMagickString(header,"#?RADIANCE\n",MagickPathExtent);
    717   (void) WriteBlob(image,length,(unsigned char *) header);
    718   property=GetImageProperty(image,"comment",exception);
    719   if ((property != (const char *) NULL) &&
    720       (strchr(property,'\n') == (char *) NULL))
    721     {
    722       count=FormatLocaleString(header,MagickPathExtent,"#%.*s\n",
    723         MagickPathExtent-3,property);
    724       (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
    725     }
    726   property=GetImageProperty(image,"hdr:exposure",exception);
    727   if (property != (const char *) NULL)
    728     {
    729       count=FormatLocaleString(header,MagickPathExtent,"EXPOSURE=%g\n",
    730         strtod(property,(char **) NULL));
    731       (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
    732     }
    733   if (image->gamma != 0.0)
    734     {
    735       count=FormatLocaleString(header,MagickPathExtent,"GAMMA=%g\n",
    736         image->gamma);
    737       (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
    738     }
    739   count=FormatLocaleString(header,MagickPathExtent,
    740     "PRIMARIES=%g %g %g %g %g %g %g %g\n",
    741     image->chromaticity.red_primary.x,image->chromaticity.red_primary.y,
    742     image->chromaticity.green_primary.x,image->chromaticity.green_primary.y,
    743     image->chromaticity.blue_primary.x,image->chromaticity.blue_primary.y,
    744     image->chromaticity.white_point.x,image->chromaticity.white_point.y);
    745   (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
    746   length=CopyMagickString(header,"FORMAT=32-bit_rle_rgbe\n\n",MagickPathExtent);
    747   (void) WriteBlob(image,length,(unsigned char *) header);
    748   count=FormatLocaleString(header,MagickPathExtent,"-Y %.20g +X %.20g\n",
    749     (double) image->rows,(double) image->columns);
    750   (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
    751   /*
    752     Write HDR pixels.
    753   */
    754   pixels=(unsigned char *) AcquireQuantumMemory(image->columns+128,4*
    755     sizeof(*pixels));
    756   if (pixels == (unsigned char *) NULL)
    757     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
    758   (void) memset(pixels,0,4*(image->columns+128)*sizeof(*pixels));
    759   for (y=0; y < (ssize_t) image->rows; y++)
    760   {
    761     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
    762     if (p == (const Quantum *) NULL)
    763       break;
    764     if ((image->columns >= 8) && (image->columns <= 0x7ffff))
    765       {
    766         pixel[0]=2;
    767         pixel[1]=2;
    768         pixel[2]=(unsigned char) (image->columns >> 8);
    769         pixel[3]=(unsigned char) (image->columns & 0xff);
    770         count=WriteBlob(image,4*sizeof(*pixel),pixel);
    771         if (count != (ssize_t) (4*sizeof(*pixel)))
    772           break;
    773       }
    774     i=0;
    775     for (x=0; x < (ssize_t) image->columns; x++)
    776     {
    777       double
    778         gamma;
    779 
    780       pixel[0]=0;
    781       pixel[1]=0;
    782       pixel[2]=0;
    783       pixel[3]=0;
    784       gamma=QuantumScale*GetPixelRed(image,p);
    785       if ((QuantumScale*GetPixelGreen(image,p)) > gamma)
    786         gamma=QuantumScale*GetPixelGreen(image,p);
    787       if ((QuantumScale*GetPixelBlue(image,p)) > gamma)
    788         gamma=QuantumScale*GetPixelBlue(image,p);
    789       if (gamma > MagickEpsilon)
    790         {
    791           int
    792             exponent;
    793 
    794           gamma=frexp(gamma,&exponent)*256.0/gamma;
    795           pixel[0]=(unsigned char) (gamma*QuantumScale*GetPixelRed(image,p));
    796           pixel[1]=(unsigned char) (gamma*QuantumScale*GetPixelGreen(image,p));
    797           pixel[2]=(unsigned char) (gamma*QuantumScale*GetPixelBlue(image,p));
    798           pixel[3]=(unsigned char) (exponent+128);
    799         }
    800       if ((image->columns >= 8) && (image->columns <= 0x7ffff))
    801         {
    802           pixels[x]=pixel[0];
    803           pixels[x+image->columns]=pixel[1];
    804           pixels[x+2*image->columns]=pixel[2];
    805           pixels[x+3*image->columns]=pixel[3];
    806         }
    807       else
    808         {
    809           pixels[i++]=pixel[0];
    810           pixels[i++]=pixel[1];
    811           pixels[i++]=pixel[2];
    812           pixels[i++]=pixel[3];
    813         }
    814       p+=GetPixelChannels(image);
    815     }
    816     if ((image->columns >= 8) && (image->columns <= 0x7ffff))
    817       {
    818         for (i=0; i < 4; i++)
    819           length=HDRWriteRunlengthPixels(image,&pixels[i*image->columns]);
    820       }
    821     else
    822       {
    823         count=WriteBlob(image,4*image->columns*sizeof(*pixels),pixels);
    824         if (count != (ssize_t) (4*image->columns*sizeof(*pixels)))
    825           break;
    826       }
    827     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
    828       image->rows);
    829     if (status == MagickFalse)
    830       break;
    831   }
    832   pixels=(unsigned char *) RelinquishMagickMemory(pixels);
    833   (void) CloseBlob(image);
    834   return(MagickTrue);
    835 }
    836