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