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