1 /* 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % % 4 % % 5 % % 6 % H H DDDD RRRR % 7 % H H D D R R % 8 % HHHHH D D RRRR % 9 % H H D D R R % 10 % H H DDDD R R % 11 % % 12 % % 13 % Read/Write Radiance RGBE Image Format % 14 % % 15 % Software Design % 16 % Cristy % 17 % July 1992 % 18 % % 19 % % 20 % Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization % 21 % dedicated to making software imaging solutions freely available. % 22 % % 23 % You may not use this file except in compliance with the License. You may % 24 % obtain a copy of the License at % 25 % % 26 % http://www.imagemagick.org/script/license.php % 27 % % 28 % Unless required by applicable law or agreed to in writing, software % 29 % distributed under the License is distributed on an "AS IS" BASIS, % 30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 31 % See the License for the specific language governing permissions and % 32 % limitations under the License. % 33 % % 34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35 % 36 % 37 */ 38 39 /* 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 for (y=0; y < (ssize_t) image->rows; y++) 411 { 412 if (image->compression != RLECompression) 413 { 414 count=ReadBlob(image,4*image->columns*sizeof(*pixels),pixels); 415 if (count != (ssize_t) (4*image->columns*sizeof(*pixels))) 416 break; 417 } 418 else 419 { 420 count=ReadBlob(image,4*sizeof(*pixel),pixel); 421 if (count != 4) 422 break; 423 if ((size_t) ((((size_t) pixel[2]) << 8) | pixel[3]) != image->columns) 424 { 425 (void) memcpy(pixels,pixel,4*sizeof(*pixel)); 426 count=ReadBlob(image,4*(image->columns-1)*sizeof(*pixels),pixels+4); 427 image->compression=NoCompression; 428 } 429 else 430 { 431 p=pixels; 432 for (i=0; i < 4; i++) 433 { 434 end=&pixels[(i+1)*image->columns]; 435 while (p < end) 436 { 437 count=ReadBlob(image,2*sizeof(*pixel),pixel); 438 if (count < 1) 439 break; 440 if (pixel[0] > 128) 441 { 442 count=(ssize_t) pixel[0]-128; 443 if ((count == 0) || (count > (ssize_t) (end-p))) 444 break; 445 while (count-- > 0) 446 *p++=pixel[1]; 447 } 448 else 449 { 450 count=(ssize_t) pixel[0]; 451 if ((count == 0) || (count > (ssize_t) (end-p))) 452 break; 453 *p++=pixel[1]; 454 if (--count > 0) 455 { 456 count=ReadBlob(image,(size_t) count*sizeof(*p),p); 457 if (count < 1) 458 break; 459 p+=count; 460 } 461 } 462 } 463 } 464 } 465 } 466 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 467 if (q == (Quantum *) NULL) 468 break; 469 i=0; 470 for (x=0; x < (ssize_t) image->columns; x++) 471 { 472 if (image->compression == RLECompression) 473 { 474 pixel[0]=pixels[x]; 475 pixel[1]=pixels[x+image->columns]; 476 pixel[2]=pixels[x+2*image->columns]; 477 pixel[3]=pixels[x+3*image->columns]; 478 } 479 else 480 { 481 pixel[0]=pixels[i++]; 482 pixel[1]=pixels[i++]; 483 pixel[2]=pixels[i++]; 484 pixel[3]=pixels[i++]; 485 } 486 SetPixelRed(image,0,q); 487 SetPixelGreen(image,0,q); 488 SetPixelBlue(image,0,q); 489 if (pixel[3] != 0) 490 { 491 gamma=pow(2.0,pixel[3]-(128.0+8.0)); 492 SetPixelRed(image,ClampToQuantum(QuantumRange*gamma*pixel[0]),q); 493 SetPixelGreen(image,ClampToQuantum(QuantumRange*gamma*pixel[1]),q); 494 SetPixelBlue(image,ClampToQuantum(QuantumRange*gamma*pixel[2]),q); 495 } 496 q+=GetPixelChannels(image); 497 } 498 if (SyncAuthenticPixels(image,exception) == MagickFalse) 499 break; 500 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, 501 image->rows); 502 if (status == MagickFalse) 503 break; 504 } 505 pixels=(unsigned char *) RelinquishMagickMemory(pixels); 506 if (EOFBlob(image) != MagickFalse) 507 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile", 508 image->filename); 509 (void) CloseBlob(image); 510 return(GetFirstImageInList(image)); 511 } 512 513 /* 515 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 516 % % 517 % % 518 % % 519 % R e g i s t e r H D R I m a g e % 520 % % 521 % % 522 % % 523 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 524 % 525 % RegisterHDRImage() adds attributes for the Radiance RGBE image format to the 526 % list of supported formats. The attributes include the image format tag, a 527 % method to read and/or write the format, whether the format supports the 528 % saving of more than one frame to the same file or blob, whether the format 529 % supports native in-memory I/O, and a brief description of the format. 530 % 531 % The format of the RegisterHDRImage method is: 532 % 533 % size_t RegisterHDRImage(void) 534 % 535 */ 536 ModuleExport size_t RegisterHDRImage(void) 537 { 538 MagickInfo 539 *entry; 540 541 entry=AcquireMagickInfo("HDR","HDR","Radiance RGBE image format"); 542 entry->decoder=(DecodeImageHandler *) ReadHDRImage; 543 entry->encoder=(EncodeImageHandler *) WriteHDRImage; 544 entry->magick=(IsImageFormatHandler *) IsHDR; 545 (void) RegisterMagickInfo(entry); 546 return(MagickImageCoderSignature); 547 } 548 549 /* 551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 552 % % 553 % % 554 % % 555 % U n r e g i s t e r H D R I m a g e % 556 % % 557 % % 558 % % 559 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 560 % 561 % UnregisterHDRImage() removes format registrations made by the 562 % HDR module from the list of supported formats. 563 % 564 % The format of the UnregisterHDRImage method is: 565 % 566 % UnregisterHDRImage(void) 567 % 568 */ 569 ModuleExport void UnregisterHDRImage(void) 570 { 571 (void) UnregisterMagickInfo("HDR"); 572 } 573 574 /* 576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 577 % % 578 % % 579 % % 580 % W r i t e H D R I m a g e % 581 % % 582 % % 583 % % 584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 585 % 586 % WriteHDRImage() writes an image in the Radience RGBE image format. 587 % 588 % The format of the WriteHDRImage method is: 589 % 590 % MagickBooleanType WriteHDRImage(const ImageInfo *image_info, 591 % Image *image,ExceptionInfo *exception) 592 % 593 % A description of each parameter follows. 594 % 595 % o image_info: the image info. 596 % 597 % o image: The image. 598 % 599 */ 600 601 static size_t HDRWriteRunlengthPixels(Image *image,unsigned char *pixels) 602 { 603 #define MinimumRunlength 4 604 605 register size_t 606 p, 607 q; 608 609 size_t 610 runlength; 611 612 ssize_t 613 count, 614 previous_count; 615 616 unsigned char 617 pixel[2]; 618 619 for (p=0; p < image->columns; ) 620 { 621 q=p; 622 runlength=0; 623 previous_count=0; 624 while ((runlength < MinimumRunlength) && (q < image->columns)) 625 { 626 q+=runlength; 627 previous_count=(ssize_t) runlength; 628 runlength=1; 629 while ((pixels[q] == pixels[q+runlength]) && 630 ((q+runlength) < image->columns) && (runlength < 127)) 631 runlength++; 632 } 633 if ((previous_count > 1) && (previous_count == (ssize_t) (q-p))) 634 { 635 pixel[0]=(unsigned char) (128+previous_count); 636 pixel[1]=pixels[p]; 637 if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1) 638 break; 639 p=q; 640 } 641 while (p < q) 642 { 643 count=(ssize_t) (q-p); 644 if (count > 128) 645 count=128; 646 pixel[0]=(unsigned char) count; 647 if (WriteBlob(image,sizeof(*pixel),pixel) < 1) 648 break; 649 if (WriteBlob(image,(size_t) count*sizeof(*pixel),&pixels[p]) < 1) 650 break; 651 p+=count; 652 } 653 if (runlength >= MinimumRunlength) 654 { 655 pixel[0]=(unsigned char) (128+runlength); 656 pixel[1]=pixels[q]; 657 if (WriteBlob(image,2*sizeof(*pixel),pixel) < 1) 658 break; 659 p+=runlength; 660 } 661 } 662 return(p); 663 } 664 665 static MagickBooleanType WriteHDRImage(const ImageInfo *image_info,Image *image, 666 ExceptionInfo *exception) 667 { 668 char 669 header[MagickPathExtent]; 670 671 const char 672 *property; 673 674 MagickBooleanType 675 status; 676 677 register const Quantum 678 *p; 679 680 register ssize_t 681 i, 682 x; 683 684 size_t 685 length; 686 687 ssize_t 688 count, 689 y; 690 691 unsigned char 692 pixel[4], 693 *pixels; 694 695 /* 696 Open output image file. 697 */ 698 assert(image_info != (const ImageInfo *) NULL); 699 assert(image_info->signature == MagickCoreSignature); 700 assert(image != (Image *) NULL); 701 assert(image->signature == MagickCoreSignature); 702 if (image->debug != MagickFalse) 703 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 704 assert(exception != (ExceptionInfo *) NULL); 705 assert(exception->signature == MagickCoreSignature); 706 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 707 if (status == MagickFalse) 708 return(status); 709 if (IsRGBColorspace(image->colorspace) == MagickFalse) 710 (void) TransformImageColorspace(image,RGBColorspace,exception); 711 /* 712 Write header. 713 */ 714 (void) ResetMagickMemory(header,' ',MagickPathExtent); 715 length=CopyMagickString(header,"#?RGBE\n",MagickPathExtent); 716 (void) WriteBlob(image,length,(unsigned char *) header); 717 property=GetImageProperty(image,"comment",exception); 718 if ((property != (const char *) NULL) && 719 (strchr(property,'\n') == (char *) NULL)) 720 { 721 count=FormatLocaleString(header,MagickPathExtent,"#%s\n",property); 722 (void) WriteBlob(image,(size_t) count,(unsigned char *) header); 723 } 724 property=GetImageProperty(image,"hdr:exposure",exception); 725 if (property != (const char *) NULL) 726 { 727 count=FormatLocaleString(header,MagickPathExtent,"EXPOSURE=%g\n", 728 strtod(property,(char **) NULL)); 729 (void) WriteBlob(image,(size_t) count,(unsigned char *) header); 730 } 731 if (image->gamma != 0.0) 732 { 733 count=FormatLocaleString(header,MagickPathExtent,"GAMMA=%g\n", 734 image->gamma); 735 (void) WriteBlob(image,(size_t) count,(unsigned char *) header); 736 } 737 count=FormatLocaleString(header,MagickPathExtent, 738 "PRIMARIES=%g %g %g %g %g %g %g %g\n", 739 image->chromaticity.red_primary.x,image->chromaticity.red_primary.y, 740 image->chromaticity.green_primary.x,image->chromaticity.green_primary.y, 741 image->chromaticity.blue_primary.x,image->chromaticity.blue_primary.y, 742 image->chromaticity.white_point.x,image->chromaticity.white_point.y); 743 (void) WriteBlob(image,(size_t) count,(unsigned char *) header); 744 length=CopyMagickString(header,"FORMAT=32-bit_rle_rgbe\n\n",MagickPathExtent); 745 (void) WriteBlob(image,length,(unsigned char *) header); 746 count=FormatLocaleString(header,MagickPathExtent,"-Y %.20g +X %.20g\n", 747 (double) image->rows,(double) image->columns); 748 (void) WriteBlob(image,(size_t) count,(unsigned char *) header); 749 /* 750 Write HDR pixels. 751 */ 752 pixels=(unsigned char *) AcquireQuantumMemory(image->columns+128,4* 753 sizeof(*pixels)); 754 if (pixels == (unsigned char *) NULL) 755 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 756 (void) ResetMagickMemory(pixels,0,4*(image->columns+128)*sizeof(*pixels)); 757 for (y=0; y < (ssize_t) image->rows; y++) 758 { 759 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 760 if (p == (const Quantum *) NULL) 761 break; 762 if ((image->columns >= 8) && (image->columns <= 0x7ffff)) 763 { 764 pixel[0]=2; 765 pixel[1]=2; 766 pixel[2]=(unsigned char) (image->columns >> 8); 767 pixel[3]=(unsigned char) (image->columns & 0xff); 768 count=WriteBlob(image,4*sizeof(*pixel),pixel); 769 if (count != (ssize_t) (4*sizeof(*pixel))) 770 break; 771 } 772 i=0; 773 for (x=0; x < (ssize_t) image->columns; x++) 774 { 775 double 776 gamma; 777 778 pixel[0]=0; 779 pixel[1]=0; 780 pixel[2]=0; 781 pixel[3]=0; 782 gamma=QuantumScale*GetPixelRed(image,p); 783 if ((QuantumScale*GetPixelGreen(image,p)) > gamma) 784 gamma=QuantumScale*GetPixelGreen(image,p); 785 if ((QuantumScale*GetPixelBlue(image,p)) > gamma) 786 gamma=QuantumScale*GetPixelBlue(image,p); 787 if (gamma > MagickEpsilon) 788 { 789 int 790 exponent; 791 792 gamma=frexp(gamma,&exponent)*256.0/gamma; 793 pixel[0]=(unsigned char) (gamma*QuantumScale*GetPixelRed(image,p)); 794 pixel[1]=(unsigned char) (gamma*QuantumScale*GetPixelGreen(image,p)); 795 pixel[2]=(unsigned char) (gamma*QuantumScale*GetPixelBlue(image,p)); 796 pixel[3]=(unsigned char) (exponent+128); 797 } 798 if ((image->columns >= 8) && (image->columns <= 0x7ffff)) 799 { 800 pixels[x]=pixel[0]; 801 pixels[x+image->columns]=pixel[1]; 802 pixels[x+2*image->columns]=pixel[2]; 803 pixels[x+3*image->columns]=pixel[3]; 804 } 805 else 806 { 807 pixels[i++]=pixel[0]; 808 pixels[i++]=pixel[1]; 809 pixels[i++]=pixel[2]; 810 pixels[i++]=pixel[3]; 811 } 812 p+=GetPixelChannels(image); 813 } 814 if ((image->columns >= 8) && (image->columns <= 0x7ffff)) 815 { 816 for (i=0; i < 4; i++) 817 length=HDRWriteRunlengthPixels(image,&pixels[i*image->columns]); 818 } 819 else 820 { 821 count=WriteBlob(image,4*image->columns*sizeof(*pixels),pixels); 822 if (count != (ssize_t) (4*image->columns*sizeof(*pixels))) 823 break; 824 } 825 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, 826 image->rows); 827 if (status == MagickFalse) 828 break; 829 } 830 pixels=(unsigned char *) RelinquishMagickMemory(pixels); 831 (void) CloseBlob(image); 832 return(MagickTrue); 833 } 834