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-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/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   for (y=0; y < (ssize_t) image->rows; y++)
    411   {
    412     if (image->compression != RLECompression)
    413       {
    414         count=ReadBlob(image,4*image->columns*sizeof(*pixels),pixels);
    415         if (count != (ssize_t) (4*image->columns*sizeof(*pixels)))
    416           break;
    417       }
    418     else
    419       {
    420         count=ReadBlob(image,4*sizeof(*pixel),pixel);
    421         if (count != 4)
    422           break;
    423         if ((size_t) ((((size_t) pixel[2]) << 8) | pixel[3]) != image->columns)
    424           {
    425             (void) memcpy(pixels,pixel,4*sizeof(*pixel));
    426             count=ReadBlob(image,4*(image->columns-1)*sizeof(*pixels),pixels+4);
    427             image->compression=NoCompression;
    428           }
    429         else
    430           {
    431             p=pixels;
    432             for (i=0; i < 4; i++)
    433             {
    434               end=&pixels[(i+1)*image->columns];
    435               while (p < end)
    436               {
    437                 count=ReadBlob(image,2*sizeof(*pixel),pixel);
    438                 if (count < 1)
    439                   break;
    440                 if (pixel[0] > 128)
    441                   {
    442                     count=(ssize_t) pixel[0]-128;
    443                     if ((count == 0) || (count > (ssize_t) (end-p)))
    444                       break;
    445                     while (count-- > 0)
    446                       *p++=pixel[1];
    447                   }
    448                 else
    449                   {
    450                     count=(ssize_t) pixel[0];
    451                     if ((count == 0) || (count > (ssize_t) (end-p)))
    452                       break;
    453                     *p++=pixel[1];
    454                     if (--count > 0)
    455                       {
    456                         count=ReadBlob(image,(size_t) count*sizeof(*p),p);
    457                         if (count < 1)
    458                           break;
    459                         p+=count;
    460                       }
    461                   }
    462               }
    463             }
    464           }
    465       }
    466     q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
    467     if (q == (Quantum *) NULL)
    468       break;
    469     i=0;
    470     for (x=0; x < (ssize_t) image->columns; x++)
    471     {
    472       if (image->compression == RLECompression)
    473         {
    474           pixel[0]=pixels[x];
    475           pixel[1]=pixels[x+image->columns];
    476           pixel[2]=pixels[x+2*image->columns];
    477           pixel[3]=pixels[x+3*image->columns];
    478         }
    479       else
    480         {
    481           pixel[0]=pixels[i++];
    482           pixel[1]=pixels[i++];
    483           pixel[2]=pixels[i++];
    484           pixel[3]=pixels[i++];
    485         }
    486       SetPixelRed(image,0,q);
    487       SetPixelGreen(image,0,q);
    488       SetPixelBlue(image,0,q);
    489       if (pixel[3] != 0)
    490         {
    491           gamma=pow(2.0,pixel[3]-(128.0+8.0));
    492           SetPixelRed(image,ClampToQuantum(QuantumRange*gamma*pixel[0]),q);
    493           SetPixelGreen(image,ClampToQuantum(QuantumRange*gamma*pixel[1]),q);
    494           SetPixelBlue(image,ClampToQuantum(QuantumRange*gamma*pixel[2]),q);
    495         }
    496       q+=GetPixelChannels(image);
    497     }
    498     if (SyncAuthenticPixels(image,exception) == MagickFalse)
    499       break;
    500     status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
    501       image->rows);
    502     if (status == MagickFalse)
    503       break;
    504   }
    505   pixels=(unsigned char *) RelinquishMagickMemory(pixels);
    506   if (EOFBlob(image) != MagickFalse)
    507     ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
    508       image->filename);
    509   (void) CloseBlob(image);
    510   return(GetFirstImageInList(image));
    511 }
    512 
    513 /*
    515 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    516 %                                                                             %
    517 %                                                                             %
    518 %                                                                             %
    519 %   R e g i s t e r H D R I m a g e                                           %
    520 %                                                                             %
    521 %                                                                             %
    522 %                                                                             %
    523 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    524 %
    525 %  RegisterHDRImage() adds attributes for the Radiance RGBE image format to the
    526 %  list of supported formats.  The attributes include the image format tag, a
    527 %  method to read and/or write the format, whether the format supports the
    528 %  saving of more than one frame to the same file or blob, whether the format
    529 %  supports native in-memory I/O, and a brief description of the format.
    530 %
    531 %  The format of the RegisterHDRImage method is:
    532 %
    533 %      size_t RegisterHDRImage(void)
    534 %
    535 */
    536 ModuleExport size_t RegisterHDRImage(void)
    537 {
    538   MagickInfo
    539     *entry;
    540 
    541   entry=AcquireMagickInfo("HDR","HDR","Radiance RGBE image format");
    542   entry->decoder=(DecodeImageHandler *) ReadHDRImage;
    543   entry->encoder=(EncodeImageHandler *) WriteHDRImage;
    544   entry->magick=(IsImageFormatHandler *) IsHDR;
    545   (void) RegisterMagickInfo(entry);
    546   return(MagickImageCoderSignature);
    547 }
    548 
    549 /*
    551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    552 %                                                                             %
    553 %                                                                             %
    554 %                                                                             %
    555 %   U n r e g i s t e r H D R I m a g e                                       %
    556 %                                                                             %
    557 %                                                                             %
    558 %                                                                             %
    559 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    560 %
    561 %  UnregisterHDRImage() removes format registrations made by the
    562 %  HDR module from the list of supported formats.
    563 %
    564 %  The format of the UnregisterHDRImage method is:
    565 %
    566 %      UnregisterHDRImage(void)
    567 %
    568 */
    569 ModuleExport void UnregisterHDRImage(void)
    570 {
    571   (void) UnregisterMagickInfo("HDR");
    572 }
    573 
    574 /*
    576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    577 %                                                                             %
    578 %                                                                             %
    579 %                                                                             %
    580 %   W r i t e H D R I m a g e                                                 %
    581 %                                                                             %
    582 %                                                                             %
    583 %                                                                             %
    584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    585 %
    586 %  WriteHDRImage() writes an image in the Radience RGBE image format.
    587 %
    588 %  The format of the WriteHDRImage method is:
    589 %
    590 %      MagickBooleanType WriteHDRImage(const ImageInfo *image_info,
    591 %        Image *image,ExceptionInfo *exception)
    592 %
    593 %  A description of each parameter follows.
    594 %
    595 %    o image_info: the image info.
    596 %
    597 %    o image:  The image.
    598 %
    599 */
    600 
    601 static size_t HDRWriteRunlengthPixels(Image *image,unsigned char *pixels)
    602 {
    603 #define MinimumRunlength 4
    604 
    605   register size_t
    606     p,
    607     q;
    608 
    609   size_t
    610     runlength;
    611 
    612   ssize_t
    613     count,
    614     previous_count;
    615 
    616   unsigned char
    617     pixel[2];
    618 
    619   for (p=0; p < image->columns; )
    620   {
    621     q=p;
    622     runlength=0;
    623     previous_count=0;
    624     while ((runlength < MinimumRunlength) && (q < image->columns))
    625     {
    626       q+=runlength;
    627       previous_count=(ssize_t) runlength;
    628       runlength=1;
    629       while ((pixels[q] == pixels[q+runlength]) &&
    630              ((q+runlength) < image->columns) && (runlength < 127))
    631        runlength++;
    632     }
    633     if ((previous_count > 1) && (previous_count == (ssize_t) (q-p)))
    634       {
    635         pixel[0]=(unsigned char) (128+previous_count);
    636         pixel[1]=pixels[p];
    637         if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1)
    638           break;
    639         p=q;
    640       }
    641     while (p < q)
    642     {
    643       count=(ssize_t) (q-p);
    644       if (count > 128)
    645         count=128;
    646       pixel[0]=(unsigned char) count;
    647       if (WriteBlob(image,sizeof(*pixel),pixel) < 1)
    648         break;
    649       if (WriteBlob(image,(size_t) count*sizeof(*pixel),&pixels[p]) < 1)
    650         break;
    651       p+=count;
    652     }
    653     if (runlength >= MinimumRunlength)
    654       {
    655         pixel[0]=(unsigned char) (128+runlength);
    656         pixel[1]=pixels[q];
    657         if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1)
    658           break;
    659         p+=runlength;
    660       }
    661   }
    662   return(p);
    663 }
    664 
    665 static MagickBooleanType WriteHDRImage(const ImageInfo *image_info,Image *image,
    666   ExceptionInfo *exception)
    667 {
    668   char
    669     header[MagickPathExtent];
    670 
    671   const char
    672     *property;
    673 
    674   MagickBooleanType
    675     status;
    676 
    677   register const Quantum
    678     *p;
    679 
    680   register ssize_t
    681     i,
    682     x;
    683 
    684   size_t
    685     length;
    686 
    687   ssize_t
    688     count,
    689     y;
    690 
    691   unsigned char
    692     pixel[4],
    693     *pixels;
    694 
    695   /*
    696     Open output image file.
    697   */
    698   assert(image_info != (const ImageInfo *) NULL);
    699   assert(image_info->signature == MagickCoreSignature);
    700   assert(image != (Image *) NULL);
    701   assert(image->signature == MagickCoreSignature);
    702   if (image->debug != MagickFalse)
    703     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    704   assert(exception != (ExceptionInfo *) NULL);
    705   assert(exception->signature == MagickCoreSignature);
    706   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
    707   if (status == MagickFalse)
    708     return(status);
    709   if (IsRGBColorspace(image->colorspace) == MagickFalse)
    710     (void) TransformImageColorspace(image,RGBColorspace,exception);
    711   /*
    712     Write header.
    713   */
    714   (void) ResetMagickMemory(header,' ',MagickPathExtent);
    715   length=CopyMagickString(header,"#?RGBE\n",MagickPathExtent);
    716   (void) WriteBlob(image,length,(unsigned char *) header);
    717   property=GetImageProperty(image,"comment",exception);
    718   if ((property != (const char *) NULL) &&
    719       (strchr(property,'\n') == (char *) NULL))
    720     {
    721       count=FormatLocaleString(header,MagickPathExtent,"#%s\n",property);
    722       (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
    723     }
    724   property=GetImageProperty(image,"hdr:exposure",exception);
    725   if (property != (const char *) NULL)
    726     {
    727       count=FormatLocaleString(header,MagickPathExtent,"EXPOSURE=%g\n",
    728         strtod(property,(char **) NULL));
    729       (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
    730     }
    731   if (image->gamma != 0.0)
    732     {
    733       count=FormatLocaleString(header,MagickPathExtent,"GAMMA=%g\n",
    734         image->gamma);
    735       (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
    736     }
    737   count=FormatLocaleString(header,MagickPathExtent,
    738     "PRIMARIES=%g %g %g %g %g %g %g %g\n",
    739     image->chromaticity.red_primary.x,image->chromaticity.red_primary.y,
    740     image->chromaticity.green_primary.x,image->chromaticity.green_primary.y,
    741     image->chromaticity.blue_primary.x,image->chromaticity.blue_primary.y,
    742     image->chromaticity.white_point.x,image->chromaticity.white_point.y);
    743   (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
    744   length=CopyMagickString(header,"FORMAT=32-bit_rle_rgbe\n\n",MagickPathExtent);
    745   (void) WriteBlob(image,length,(unsigned char *) header);
    746   count=FormatLocaleString(header,MagickPathExtent,"-Y %.20g +X %.20g\n",
    747     (double) image->rows,(double) image->columns);
    748   (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
    749   /*
    750     Write HDR pixels.
    751   */
    752   pixels=(unsigned char *) AcquireQuantumMemory(image->columns+128,4*
    753     sizeof(*pixels));
    754   if (pixels == (unsigned char *) NULL)
    755     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
    756   (void) ResetMagickMemory(pixels,0,4*(image->columns+128)*sizeof(*pixels));
    757   for (y=0; y < (ssize_t) image->rows; y++)
    758   {
    759     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
    760     if (p == (const Quantum *) NULL)
    761       break;
    762     if ((image->columns >= 8) && (image->columns <= 0x7ffff))
    763       {
    764         pixel[0]=2;
    765         pixel[1]=2;
    766         pixel[2]=(unsigned char) (image->columns >> 8);
    767         pixel[3]=(unsigned char) (image->columns & 0xff);
    768         count=WriteBlob(image,4*sizeof(*pixel),pixel);
    769         if (count != (ssize_t) (4*sizeof(*pixel)))
    770           break;
    771       }
    772     i=0;
    773     for (x=0; x < (ssize_t) image->columns; x++)
    774     {
    775       double
    776         gamma;
    777 
    778       pixel[0]=0;
    779       pixel[1]=0;
    780       pixel[2]=0;
    781       pixel[3]=0;
    782       gamma=QuantumScale*GetPixelRed(image,p);
    783       if ((QuantumScale*GetPixelGreen(image,p)) > gamma)
    784         gamma=QuantumScale*GetPixelGreen(image,p);
    785       if ((QuantumScale*GetPixelBlue(image,p)) > gamma)
    786         gamma=QuantumScale*GetPixelBlue(image,p);
    787       if (gamma > MagickEpsilon)
    788         {
    789           int
    790             exponent;
    791 
    792           gamma=frexp(gamma,&exponent)*256.0/gamma;
    793           pixel[0]=(unsigned char) (gamma*QuantumScale*GetPixelRed(image,p));
    794           pixel[1]=(unsigned char) (gamma*QuantumScale*GetPixelGreen(image,p));
    795           pixel[2]=(unsigned char) (gamma*QuantumScale*GetPixelBlue(image,p));
    796           pixel[3]=(unsigned char) (exponent+128);
    797         }
    798       if ((image->columns >= 8) && (image->columns <= 0x7ffff))
    799         {
    800           pixels[x]=pixel[0];
    801           pixels[x+image->columns]=pixel[1];
    802           pixels[x+2*image->columns]=pixel[2];
    803           pixels[x+3*image->columns]=pixel[3];
    804         }
    805       else
    806         {
    807           pixels[i++]=pixel[0];
    808           pixels[i++]=pixel[1];
    809           pixels[i++]=pixel[2];
    810           pixels[i++]=pixel[3];
    811         }
    812       p+=GetPixelChannels(image);
    813     }
    814     if ((image->columns >= 8) && (image->columns <= 0x7ffff))
    815       {
    816         for (i=0; i < 4; i++)
    817           length=HDRWriteRunlengthPixels(image,&pixels[i*image->columns]);
    818       }
    819     else
    820       {
    821         count=WriteBlob(image,4*image->columns*sizeof(*pixels),pixels);
    822         if (count != (ssize_t) (4*image->columns*sizeof(*pixels)))
    823           break;
    824       }
    825     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
    826       image->rows);
    827     if (status == MagickFalse)
    828       break;
    829   }
    830   pixels=(unsigned char *) RelinquishMagickMemory(pixels);
    831   (void) CloseBlob(image);
    832   return(MagickTrue);
    833 }
    834