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