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