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