• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /*
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 
65 /*
66   Forward declarations.
67 */
68 static MagickBooleanType
69   WriteHDRImage(const ImageInfo *,Image *,ExceptionInfo *);
70 
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 */
IsHDR(const unsigned char * magick,const size_t length)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 
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 */
ReadHDRImage(const ImageInfo * image_info,ExceptionInfo * exception)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   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 
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 */
RegisterHDRImage(void)531 ModuleExport size_t RegisterHDRImage(void)
532 {
533   MagickInfo
534     *entry;
535 
536   entry=AcquireMagickInfo("HDR","HDR","Radiance RGBE image format");
537   entry->decoder=(DecodeImageHandler *) ReadHDRImage;
538   entry->encoder=(EncodeImageHandler *) WriteHDRImage;
539   entry->magick=(IsImageFormatHandler *) IsHDR;
540   (void) RegisterMagickInfo(entry);
541   return(MagickImageCoderSignature);
542 }
543 
544 /*
545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
546 %                                                                             %
547 %                                                                             %
548 %                                                                             %
549 %   U n r e g i s t e r H D R I m a g e                                       %
550 %                                                                             %
551 %                                                                             %
552 %                                                                             %
553 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
554 %
555 %  UnregisterHDRImage() removes format registrations made by the
556 %  HDR module from the list of supported formats.
557 %
558 %  The format of the UnregisterHDRImage method is:
559 %
560 %      UnregisterHDRImage(void)
561 %
562 */
UnregisterHDRImage(void)563 ModuleExport void UnregisterHDRImage(void)
564 {
565   (void) UnregisterMagickInfo("HDR");
566 }
567 
568 /*
569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
570 %                                                                             %
571 %                                                                             %
572 %                                                                             %
573 %   W r i t e H D R I m a g e                                                 %
574 %                                                                             %
575 %                                                                             %
576 %                                                                             %
577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
578 %
579 %  WriteHDRImage() writes an image in the Radience RGBE image format.
580 %
581 %  The format of the WriteHDRImage method is:
582 %
583 %      MagickBooleanType WriteHDRImage(const ImageInfo *image_info,
584 %        Image *image,ExceptionInfo *exception)
585 %
586 %  A description of each parameter follows.
587 %
588 %    o image_info: the image info.
589 %
590 %    o image:  The image.
591 %
592 */
593 
HDRWriteRunlengthPixels(Image * image,unsigned char * pixels)594 static size_t HDRWriteRunlengthPixels(Image *image,unsigned char *pixels)
595 {
596 #define MinimumRunlength 4
597 
598   register size_t
599     p,
600     q;
601 
602   size_t
603     runlength;
604 
605   ssize_t
606     count,
607     previous_count;
608 
609   unsigned char
610     pixel[2];
611 
612   for (p=0; p < image->columns; )
613   {
614     q=p;
615     runlength=0;
616     previous_count=0;
617     while ((runlength < MinimumRunlength) && (q < image->columns))
618     {
619       q+=runlength;
620       previous_count=(ssize_t) runlength;
621       runlength=1;
622       while ((pixels[q] == pixels[q+runlength]) &&
623              ((q+runlength) < image->columns) && (runlength < 127))
624        runlength++;
625     }
626     if ((previous_count > 1) && (previous_count == (ssize_t) (q-p)))
627       {
628         pixel[0]=(unsigned char) (128+previous_count);
629         pixel[1]=pixels[p];
630         if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1)
631           break;
632         p=q;
633       }
634     while (p < q)
635     {
636       count=(ssize_t) (q-p);
637       if (count > 128)
638         count=128;
639       pixel[0]=(unsigned char) count;
640       if (WriteBlob(image,sizeof(*pixel),pixel) < 1)
641         break;
642       if (WriteBlob(image,(size_t) count*sizeof(*pixel),&pixels[p]) < 1)
643         break;
644       p+=count;
645     }
646     if (runlength >= MinimumRunlength)
647       {
648         pixel[0]=(unsigned char) (128+runlength);
649         pixel[1]=pixels[q];
650         if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1)
651           break;
652         p+=runlength;
653       }
654   }
655   return(p);
656 }
657 
WriteHDRImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)658 static MagickBooleanType WriteHDRImage(const ImageInfo *image_info,Image *image,
659   ExceptionInfo *exception)
660 {
661   char
662     header[MagickPathExtent];
663 
664   const char
665     *property;
666 
667   MagickBooleanType
668     status;
669 
670   register const Quantum
671     *p;
672 
673   register ssize_t
674     i,
675     x;
676 
677   size_t
678     length;
679 
680   ssize_t
681     count,
682     y;
683 
684   unsigned char
685     pixel[4],
686     *pixels;
687 
688   /*
689     Open output image file.
690   */
691   assert(image_info != (const ImageInfo *) NULL);
692   assert(image_info->signature == MagickCoreSignature);
693   assert(image != (Image *) NULL);
694   assert(image->signature == MagickCoreSignature);
695   if (image->debug != MagickFalse)
696     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
697   assert(exception != (ExceptionInfo *) NULL);
698   assert(exception->signature == MagickCoreSignature);
699   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
700   if (status == MagickFalse)
701     return(status);
702   if (IsRGBColorspace(image->colorspace) == MagickFalse)
703     (void) TransformImageColorspace(image,RGBColorspace,exception);
704   /*
705     Write header.
706   */
707   (void) ResetMagickMemory(header,' ',MagickPathExtent);
708   length=CopyMagickString(header,"#?RGBE\n",MagickPathExtent);
709   (void) WriteBlob(image,length,(unsigned char *) header);
710   property=GetImageProperty(image,"comment",exception);
711   if ((property != (const char *) NULL) &&
712       (strchr(property,'\n') == (char *) NULL))
713     {
714       count=FormatLocaleString(header,MagickPathExtent,"#%s\n",property);
715       (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
716     }
717   property=GetImageProperty(image,"hdr:exposure",exception);
718   if (property != (const char *) NULL)
719     {
720       count=FormatLocaleString(header,MagickPathExtent,"EXPOSURE=%g\n",
721         strtod(property,(char **) NULL));
722       (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
723     }
724   if (image->gamma != 0.0)
725     {
726       count=FormatLocaleString(header,MagickPathExtent,"GAMMA=%g\n",
727         image->gamma);
728       (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
729     }
730   count=FormatLocaleString(header,MagickPathExtent,
731     "PRIMARIES=%g %g %g %g %g %g %g %g\n",
732     image->chromaticity.red_primary.x,image->chromaticity.red_primary.y,
733     image->chromaticity.green_primary.x,image->chromaticity.green_primary.y,
734     image->chromaticity.blue_primary.x,image->chromaticity.blue_primary.y,
735     image->chromaticity.white_point.x,image->chromaticity.white_point.y);
736   (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
737   length=CopyMagickString(header,"FORMAT=32-bit_rle_rgbe\n\n",MagickPathExtent);
738   (void) WriteBlob(image,length,(unsigned char *) header);
739   count=FormatLocaleString(header,MagickPathExtent,"-Y %.20g +X %.20g\n",
740     (double) image->rows,(double) image->columns);
741   (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
742   /*
743     Write HDR pixels.
744   */
745   pixels=(unsigned char *) AcquireQuantumMemory(image->columns+128,4*
746     sizeof(*pixels));
747   if (pixels == (unsigned char *) NULL)
748     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
749   (void) ResetMagickMemory(pixels,0,4*(image->columns+128)*sizeof(*pixels));
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 }
827