2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Radiance RGBE Image Format %
20 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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"
66 static MagickBooleanType
67 WriteHDRImage(const ImageInfo *,Image *);
70 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80 % IsHDR() returns MagickTrue if the image format type, identified by the
81 % magick string, is Radiance RGBE image format.
83 % The format of the IsHDR method is:
85 % MagickBooleanType IsHDR(const unsigned char *magick,
86 % const size_t length)
88 % A description of each parameter follows:
90 % o magick: compare image format pattern against these bytes.
92 % o length: Specifies the length of the magick string.
95 static MagickBooleanType IsHDR(const unsigned char *magick,
100 if (LocaleNCompare((const char *) magick,"#?RADIANCE",10) == 0)
102 if (LocaleNCompare((const char *) magick,"#?RGBE",6) == 0)
108 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112 % R e a d H D R I m a g e %
116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
122 % The format of the ReadHDRImage method is:
124 % Image *ReadHDRImage(const ImageInfo *image_info,ExceptionInfo *exception)
126 % A description of each parameter follows:
128 % o image_info: the image info.
130 % o exception: return any errors or warnings in this structure.
133 static Image *ReadHDRImage(const ImageInfo *image_info,ExceptionInfo *exception)
136 format[MaxTextExtent],
137 keyword[MaxTextExtent],
139 value[MaxTextExtent];
157 register unsigned char
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)
187 image=DestroyImageList(image);
188 return((Image *) NULL);
196 c=ReadBlobByte(image);
199 image=DestroyImage(image);
200 return((Image *) NULL);
202 while (isgraph(c) && (image->columns == 0) && (image->rows == 0))
216 Read comment-- any text between # and end-of-line.
218 length=MaxTextExtent;
219 comment=AcquireString((char *) NULL);
220 for (p=comment; comment != (char *) NULL; p++)
222 c=ReadBlobByte(image);
223 if ((c == EOF) || (c == (int) '\n'))
225 if ((size_t) (p-comment+1) >= length)
229 comment=(char *) ResizeQuantumMemory(comment,length+
230 MaxTextExtent,sizeof(*comment));
231 if (comment == (char *) NULL)
233 p=comment+strlen(comment);
237 if (comment == (char *) NULL)
238 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
240 (void) SetImageProperty(image,"comment",comment);
241 comment=DestroyString(comment);
242 c=ReadBlobByte(image);
245 if (isalnum(c) == MagickFalse)
246 c=ReadBlobByte(image);
253 Determine a keyword and its value.
258 if ((size_t) (p-keyword) < (MaxTextExtent-1))
260 c=ReadBlobByte(image);
261 } while (isalnum(c) || (c == '_'));
263 value_expected=MagickFalse;
264 while ((isspace((int) ((unsigned char) c)) != 0) || (c == '='))
267 value_expected=MagickTrue;
268 c=ReadBlobByte(image);
270 if (LocaleCompare(keyword,"Y") == 0)
271 value_expected=MagickTrue;
272 if (value_expected == MagickFalse)
275 while ((c != '\n') && (c != '\0'))
277 if ((size_t) (p-value) < (MaxTextExtent-1))
279 c=ReadBlobByte(image);
283 Assign a value to the specified keyword.
290 if (LocaleCompare(keyword,"format") == 0)
292 (void) CopyMagickString(format,value,MaxTextExtent);
295 (void) FormatMagickString(tag,MaxTextExtent,"hdr:%s",keyword);
296 (void) SetImageProperty(image,tag,value);
302 if (LocaleCompare(keyword,"gamma") == 0)
304 image->gamma=StringToDouble(value);
307 (void) FormatMagickString(tag,MaxTextExtent,"hdr:%s",keyword);
308 (void) SetImageProperty(image,tag,value);
314 if (LocaleCompare(keyword,"primaries") == 0)
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];
334 (void) FormatMagickString(tag,MaxTextExtent,"hdr:%s",keyword);
335 (void) SetImageProperty(image,tag,value);
341 if (strcmp(keyword,"Y") == 0)
347 (void) sscanf(value,"%d +X %d",&height,&width);
348 image->columns=(size_t) width;
349 image->rows=(size_t) height;
352 (void) FormatMagickString(tag,MaxTextExtent,"hdr:%s",keyword);
353 (void) SetImageProperty(image,tag,value);
358 (void) FormatMagickString(tag,MaxTextExtent,"hdr:%s",keyword);
359 (void) SetImageProperty(image,tag,value);
364 if ((image->columns == 0) && (image->rows == 0))
365 while (isspace((int) ((unsigned char) c)) != 0)
366 c=ReadBlobByte(image);
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)
379 (void) CloseBlob(image);
380 return(GetFirstImageInList(image));
383 Read RGBE (red+green+blue+exponent) pixels.
385 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,4*
387 if (pixels == (unsigned char *) NULL)
388 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
389 for (y=0; y < (ssize_t) image->rows; y++)
391 if (image->compression != RLECompression)
393 count=ReadBlob(image,4*image->columns*sizeof(*pixels),pixels);
394 if (count != (ssize_t) (4*image->columns*sizeof(*pixels)))
399 count=ReadBlob(image,4*sizeof(*pixel),pixel);
402 if ((size_t) ((((size_t) pixel[2]) << 8) | pixel[3]) != image->columns)
404 (void) memcpy(pixels,pixel,4*sizeof(*pixel));
405 count=ReadBlob(image,4*(image->columns-1)*sizeof(*pixels),pixels+4);
406 image->compression=NoCompression;
411 for (i=0; i < 4; i++)
413 end=&pixels[(i+1)*image->columns];
416 count=ReadBlob(image,2*sizeof(*pixel),pixel);
421 count=(ssize_t) pixel[0]-128;
422 if ((count == 0) || (count > (ssize_t) (end-p)))
429 count=(ssize_t) pixel[0];
430 if ((count == 0) || (count > (ssize_t) (end-p)))
435 count=ReadBlob(image,(size_t) count*sizeof(*p),p);
445 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
446 if (q == (PixelPacket *) NULL)
449 for (x=0; x < (ssize_t) image->columns; x++)
451 if (image->compression == RLECompression)
454 pixel[1]=pixels[x+image->columns];
455 pixel[2]=pixels[x+2*image->columns];
456 pixel[3]=pixels[x+3*image->columns];
460 pixel[0]=pixels[i++];
461 pixel[1]=pixels[i++];
462 pixel[2]=pixels[i++];
463 pixel[3]=pixels[i++];
465 SetRedPixelComponent(q,0);
466 SetGreenPixelComponent(q,0);
467 SetBluePixelComponent(q,0);
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]));
477 if (SyncAuthenticPixels(image,exception) == MagickFalse)
479 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
481 if (status == MagickFalse)
484 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
485 if (EOFBlob(image) != MagickFalse)
486 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
488 (void) CloseBlob(image);
489 return(GetFirstImageInList(image));
493 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497 % R e g i s t e r H D R I m a g e %
501 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
509 % The format of the RegisterHDRImage method is:
511 % size_t RegisterHDRImage(void)
514 ModuleExport size_t RegisterHDRImage(void)
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);
530 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
534 % U n r e g i s t e r H D R I m a g e %
538 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
540 % UnregisterHDRImage() removes format registrations made by the
541 % HDR module from the list of supported formats.
543 % The format of the UnregisterHDRImage method is:
545 % UnregisterHDRImage(void)
548 ModuleExport void UnregisterHDRImage(void)
550 (void) UnregisterMagickInfo("HDR");
554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
558 % W r i t e H D R I m a g e %
562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
564 % WriteHDRImage() writes an image in the Radience RGBE image format.
566 % The format of the WriteHDRImage method is:
568 % MagickBooleanType WriteHDRImage(const ImageInfo *image_info,
571 % A description of each parameter follows.
573 % o image_info: the image info.
575 % o image: The image.
579 static size_t HDRWriteRunlengthPixels(Image *image,unsigned char *pixels)
581 #define MinimumRunlength 4
597 for (p=0; p < image->columns; )
602 while ((runlength < MinimumRunlength) && (q < image->columns))
605 previous_count=(ssize_t) runlength;
607 while ((pixels[q] == pixels[q+runlength]) &&
608 ((q+runlength) < image->columns) && (runlength < 127))
611 if ((previous_count > 1) && (previous_count == (ssize_t) (q-p)))
613 pixel[0]=(unsigned char) (128+previous_count);
615 if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1)
621 count=(ssize_t) (q-p);
624 pixel[0]=(unsigned char) count;
625 if (WriteBlob(image,sizeof(*pixel),pixel) < 1)
627 if (WriteBlob(image,(size_t) count*sizeof(*pixel),&pixels[p]) < 1)
631 if (runlength >= MinimumRunlength)
633 pixel[0]=(unsigned char) (128+runlength);
635 if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1)
643 static MagickBooleanType WriteHDRImage(const ImageInfo *image_info,Image *image)
646 header[MaxTextExtent];
654 register const PixelPacket
673 Open output image file.
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)
684 if (image->colorspace != RGBColorspace)
685 (void) TransformImageColorspace(image,RGBColorspace);
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))
696 count=FormatMagickString(header,MaxTextExtent,"#%s\n",property);
697 (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
699 property=GetImageProperty(image,"hdr:exposure");
700 if (property != (const char *) NULL)
702 count=FormatMagickString(header,MaxTextExtent,"EXPOSURE=%g\n",
704 (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
706 if (image->gamma != 0.0)
708 count=FormatMagickString(header,MaxTextExtent,"GAMMA=%g\n",image->gamma);
709 (void) WriteBlob(image,(size_t) count,(unsigned char *) header);
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);
726 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,4*
728 if (pixels == (unsigned char *) NULL)
729 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
730 for (y=0; y < (ssize_t) image->rows; y++)
732 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
733 if (p == (const PixelPacket *) NULL)
735 if ((image->columns >= 8) && (image->columns <= 0x7ffff))
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)))
746 for (x=0; x < (ssize_t) image->columns; x++)
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)
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);
773 if ((image->columns >= 8) && (image->columns <= 0x7ffff))
776 pixels[x+image->columns]=pixel[1];
777 pixels[x+2*image->columns]=pixel[2];
778 pixels[x+3*image->columns]=pixel[3];
782 pixels[i++]=pixel[0];
783 pixels[i++]=pixel[1];
784 pixels[i++]=pixel[2];
785 pixels[i++]=pixel[3];
789 if ((image->columns >= 8) && (image->columns <= 0x7ffff))
791 for (i=0; i < 4; i++)
792 length=HDRWriteRunlengthPixels(image,&pixels[i*image->columns]);
796 count=WriteBlob(image,4*image->columns*sizeof(*pixel),pixel);
797 if (count != (ssize_t) (4*image->columns*sizeof(*pixel)))
800 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
802 if (status == MagickFalse)
805 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
806 (void) CloseBlob(image);