1 /* 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % % 4 % % 5 % % 6 % EEEEE X X RRRR % 7 % E X X R R % 8 % EEE X RRRR % 9 % E X X R R % 10 % EEEEE X X R R % 11 % % 12 % % 13 % Read/Write High Dynamic-Range (HDR) Image File Format % 14 % % 15 % Software Design % 16 % Cristy % 17 % April 2007 % 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/exception.h" 48 #include "MagickCore/exception-private.h" 49 #include "MagickCore/image.h" 50 #include "MagickCore/image-private.h" 51 #include "MagickCore/list.h" 52 #include "MagickCore/magick.h" 53 #include "MagickCore/memory_.h" 54 #include "MagickCore/option.h" 55 #include "MagickCore/pixel-accessor.h" 56 #include "MagickCore/property.h" 57 #include "MagickCore/quantum-private.h" 58 #include "MagickCore/static.h" 59 #include "MagickCore/string_.h" 60 #include "MagickCore/module.h" 61 #include "MagickCore/resource_.h" 62 #include "MagickCore/utility.h" 63 #if defined(MAGICKCORE_OPENEXR_DELEGATE) 64 #include <ImfCRgbaFile.h> 65 66 /* 68 Forward declarations. 69 */ 70 static MagickBooleanType 71 WriteEXRImage(const ImageInfo *,Image *,ExceptionInfo *); 72 #endif 73 74 /* 76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 77 % % 78 % % 79 % % 80 % I s E X R % 81 % % 82 % % 83 % % 84 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 85 % 86 % IsEXR() returns MagickTrue if the image format type, identified by the 87 % magick string, is EXR. 88 % 89 % The format of the IsEXR method is: 90 % 91 % MagickBooleanType IsEXR(const unsigned char *magick,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 IsEXR(const unsigned char *magick,const size_t length) 101 { 102 if (length < 4) 103 return(MagickFalse); 104 if (memcmp(magick,"\166\057\061\001",4) == 0) 105 return(MagickTrue); 106 return(MagickFalse); 107 } 108 109 #if defined(MAGICKCORE_OPENEXR_DELEGATE) 111 /* 112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 113 % % 114 % % 115 % % 116 % R e a d E X R I m a g e % 117 % % 118 % % 119 % % 120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 121 % 122 % ReadEXRImage reads an image in the high dynamic-range (HDR) file format 123 % developed by Industrial Light & Magic. It allocates the memory necessary 124 % for the new Image structure and returns a pointer to the new image. 125 % 126 % The format of the ReadEXRImage method is: 127 % 128 % Image *ReadEXRImage(const ImageInfo *image_info,ExceptionInfo *exception) 129 % 130 % A description of each parameter follows: 131 % 132 % o image_info: the image info. 133 % 134 % o exception: return any errors or warnings in this structure. 135 % 136 */ 137 static Image *ReadEXRImage(const ImageInfo *image_info,ExceptionInfo *exception) 138 { 139 const ImfHeader 140 *hdr_info; 141 142 Image 143 *image; 144 145 ImageInfo 146 *read_info; 147 148 ImfInputFile 149 *file; 150 151 ImfRgba 152 *scanline; 153 154 int 155 max_x, 156 max_y, 157 min_x, 158 min_y; 159 160 MagickBooleanType 161 status; 162 163 register ssize_t 164 x; 165 166 register Quantum 167 *q; 168 169 ssize_t 170 y; 171 172 /* 173 Open image. 174 */ 175 assert(image_info != (const ImageInfo *) NULL); 176 assert(image_info->signature == MagickCoreSignature); 177 if (image_info->debug != MagickFalse) 178 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 179 image_info->filename); 180 assert(exception != (ExceptionInfo *) NULL); 181 assert(exception->signature == MagickCoreSignature); 182 image=AcquireImage(image_info,exception); 183 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 184 if (status == MagickFalse) 185 { 186 image=DestroyImageList(image); 187 return((Image *) NULL); 188 } 189 read_info=CloneImageInfo(image_info); 190 if (IsPathAccessible(read_info->filename) == MagickFalse) 191 { 192 (void) AcquireUniqueFilename(read_info->filename); 193 (void) ImageToFile(image,read_info->filename,exception); 194 } 195 file=ImfOpenInputFile(read_info->filename); 196 if (file == (ImfInputFile *) NULL) 197 { 198 ThrowFileException(exception,BlobError,"UnableToOpenBlob", 199 ImfErrorMessage()); 200 if (LocaleCompare(image_info->filename,read_info->filename) != 0) 201 (void) RelinquishUniqueFileResource(read_info->filename); 202 read_info=DestroyImageInfo(read_info); 203 return((Image *) NULL); 204 } 205 hdr_info=ImfInputHeader(file); 206 ImfHeaderDisplayWindow(hdr_info,&min_x,&min_y,&max_x,&max_y); 207 image->columns=max_x-min_x+1UL; 208 image->rows=max_y-min_y+1UL; 209 image->alpha_trait=BlendPixelTrait; 210 SetImageColorspace(image,RGBColorspace,exception); 211 image->gamma=1.0; 212 if (image_info->ping != MagickFalse) 213 { 214 (void) ImfCloseInputFile(file); 215 if (LocaleCompare(image_info->filename,read_info->filename) != 0) 216 (void) RelinquishUniqueFileResource(read_info->filename); 217 read_info=DestroyImageInfo(read_info); 218 (void) CloseBlob(image); 219 return(GetFirstImageInList(image)); 220 } 221 status=SetImageExtent(image,image->columns,image->rows,exception); 222 if (status == MagickFalse) 223 return(DestroyImageList(image)); 224 scanline=(ImfRgba *) AcquireQuantumMemory(image->columns,sizeof(*scanline)); 225 if (scanline == (ImfRgba *) NULL) 226 { 227 (void) ImfCloseInputFile(file); 228 if (LocaleCompare(image_info->filename,read_info->filename) != 0) 229 (void) RelinquishUniqueFileResource(read_info->filename); 230 read_info=DestroyImageInfo(read_info); 231 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 232 } 233 for (y=0; y < (ssize_t) image->rows; y++) 234 { 235 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 236 if (q == (Quantum *) NULL) 237 break; 238 ResetMagickMemory(scanline,0,image->columns*sizeof(*scanline)); 239 ImfInputSetFrameBuffer(file,scanline-min_x-image->columns*(min_y+y),1, 240 image->columns); 241 ImfInputReadPixels(file,min_y+y,min_y+y); 242 for (x=0; x < (ssize_t) image->columns; x++) 243 { 244 SetPixelRed(image,ClampToQuantum(QuantumRange* 245 ImfHalfToFloat(scanline[x].r)),q); 246 SetPixelGreen(image,ClampToQuantum(QuantumRange* 247 ImfHalfToFloat(scanline[x].g)),q); 248 SetPixelBlue(image,ClampToQuantum(QuantumRange* 249 ImfHalfToFloat(scanline[x].b)),q); 250 SetPixelAlpha(image,ClampToQuantum(QuantumRange* 251 ImfHalfToFloat(scanline[x].a)),q); 252 q+=GetPixelChannels(image); 253 } 254 if (SyncAuthenticPixels(image,exception) == MagickFalse) 255 break; 256 } 257 scanline=(ImfRgba *) RelinquishMagickMemory(scanline); 258 (void) ImfCloseInputFile(file); 259 if (LocaleCompare(image_info->filename,read_info->filename) != 0) 260 (void) RelinquishUniqueFileResource(read_info->filename); 261 read_info=DestroyImageInfo(read_info); 262 (void) CloseBlob(image); 263 return(GetFirstImageInList(image)); 264 } 265 #endif 266 267 /* 269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 270 % % 271 % % 272 % % 273 % R e g i s t e r E X R I m a g e % 274 % % 275 % % 276 % % 277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 278 % 279 % RegisterEXRImage() adds properties for the EXR image format 280 % to the list of supported formats. The properties include the image format 281 % tag, a method to read and/or write the format, whether the format 282 % supports the saving of more than one frame to the same file or blob, 283 % whether the format supports native in-memory I/O, and a brief 284 % description of the format. 285 % 286 % The format of the RegisterEXRImage method is: 287 % 288 % size_t RegisterEXRImage(void) 289 % 290 */ 291 ModuleExport size_t RegisterEXRImage(void) 292 { 293 MagickInfo 294 *entry; 295 296 entry=AcquireMagickInfo("EXR","EXR","High Dynamic-range (HDR)"); 297 #if defined(MAGICKCORE_OPENEXR_DELEGATE) 298 entry->decoder=(DecodeImageHandler *) ReadEXRImage; 299 entry->encoder=(EncodeImageHandler *) WriteEXRImage; 300 #endif 301 entry->magick=(IsImageFormatHandler *) IsEXR; 302 entry->flags^=CoderAdjoinFlag; 303 entry->flags^=CoderBlobSupportFlag; 304 (void) RegisterMagickInfo(entry); 305 return(MagickImageCoderSignature); 306 } 307 308 /* 310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 311 % % 312 % % 313 % % 314 % U n r e g i s t e r E X R I m a g e % 315 % % 316 % % 317 % % 318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 319 % 320 % UnregisterEXRImage() removes format registrations made by the 321 % EXR module from the list of supported formats. 322 % 323 % The format of the UnregisterEXRImage method is: 324 % 325 % UnregisterEXRImage(void) 326 % 327 */ 328 ModuleExport void UnregisterEXRImage(void) 329 { 330 (void) UnregisterMagickInfo("EXR"); 331 } 332 333 #if defined(MAGICKCORE_OPENEXR_DELEGATE) 335 /* 336 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 337 % % 338 % % 339 % % 340 % W r i t e E X R I m a g e % 341 % % 342 % % 343 % % 344 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 345 % 346 % WriteEXRImage() writes an image to a file the in the high dynamic-range 347 % (HDR) file format developed by Industrial Light & Magic. 348 % 349 % The format of the WriteEXRImage method is: 350 % 351 % MagickBooleanType WriteEXRImage(const ImageInfo *image_info, 352 % Image *image,ExceptionInfo *exception) 353 % 354 % A description of each parameter follows. 355 % 356 % o image_info: the image info. 357 % 358 % o image: The image. 359 % 360 % o exception: return any errors or warnings in this structure. 361 % 362 */ 363 static MagickBooleanType WriteEXRImage(const ImageInfo *image_info,Image *image, 364 ExceptionInfo *exception) 365 { 366 const char 367 *sampling_factor, 368 *value; 369 370 ImageInfo 371 *write_info; 372 373 ImfHalf 374 half_quantum; 375 376 ImfHeader 377 *hdr_info; 378 379 ImfOutputFile 380 *file; 381 382 ImfRgba 383 *scanline; 384 385 int 386 channels, 387 compression, 388 factors[3]; 389 390 MagickBooleanType 391 status; 392 393 register const Quantum 394 *p; 395 396 register ssize_t 397 x; 398 399 ssize_t 400 y; 401 402 /* 403 Open output image file. 404 */ 405 assert(image_info != (const ImageInfo *) NULL); 406 assert(image_info->signature == MagickCoreSignature); 407 assert(image != (Image *) NULL); 408 assert(image->signature == MagickCoreSignature); 409 if (image->debug != MagickFalse) 410 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 411 assert(exception != (ExceptionInfo *) NULL); 412 assert(exception->signature == MagickCoreSignature); 413 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 414 if (status == MagickFalse) 415 return(status); 416 (void) SetImageColorspace(image,RGBColorspace,exception); 417 write_info=CloneImageInfo(image_info); 418 (void) AcquireUniqueFilename(write_info->filename); 419 hdr_info=ImfNewHeader(); 420 ImfHeaderSetDataWindow(hdr_info,0,0,(int) image->columns-1,(int) 421 image->rows-1); 422 ImfHeaderSetDisplayWindow(hdr_info,0,0,(int) image->columns-1,(int) 423 image->rows-1); 424 compression=IMF_NO_COMPRESSION; 425 if (write_info->compression == ZipSCompression) 426 compression=IMF_ZIPS_COMPRESSION; 427 if (write_info->compression == ZipCompression) 428 compression=IMF_ZIP_COMPRESSION; 429 if (write_info->compression == PizCompression) 430 compression=IMF_PIZ_COMPRESSION; 431 if (write_info->compression == Pxr24Compression) 432 compression=IMF_PXR24_COMPRESSION; 433 #if defined(B44Compression) 434 if (write_info->compression == B44Compression) 435 compression=IMF_B44_COMPRESSION; 436 #endif 437 #if defined(B44ACompression) 438 if (write_info->compression == B44ACompression) 439 compression=IMF_B44A_COMPRESSION; 440 #endif 441 channels=0; 442 value=GetImageOption(image_info,"exr:color-type"); 443 if (value != (const char *) NULL) 444 { 445 if (LocaleCompare(value,"RGB") == 0) 446 channels=IMF_WRITE_RGB; 447 else if (LocaleCompare(value,"RGBA") == 0) 448 channels=IMF_WRITE_RGBA; 449 else if (LocaleCompare(value,"YC") == 0) 450 channels=IMF_WRITE_YC; 451 else if (LocaleCompare(value,"YCA") == 0) 452 channels=IMF_WRITE_YCA; 453 else if (LocaleCompare(value,"Y") == 0) 454 channels=IMF_WRITE_Y; 455 else if (LocaleCompare(value,"YA") == 0) 456 channels=IMF_WRITE_YA; 457 else if (LocaleCompare(value,"R") == 0) 458 channels=IMF_WRITE_R; 459 else if (LocaleCompare(value,"G") == 0) 460 channels=IMF_WRITE_G; 461 else if (LocaleCompare(value,"B") == 0) 462 channels=IMF_WRITE_B; 463 else if (LocaleCompare(value,"A") == 0) 464 channels=IMF_WRITE_A; 465 else 466 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning, 467 "ignoring invalid defined exr:color-type","=%s",value); 468 } 469 sampling_factor=(const char *) NULL; 470 factors[0]=0; 471 if (image_info->sampling_factor != (char *) NULL) 472 sampling_factor=image_info->sampling_factor; 473 if (sampling_factor != NULL) 474 { 475 /* 476 Sampling factors, valid values are 1x1 or 2x2. 477 */ 478 if (sscanf(sampling_factor,"%d:%d:%d",factors,factors+1,factors+2) == 3) 479 { 480 if ((factors[0] == factors[1]) && (factors[1] == factors[2])) 481 factors[0]=1; 482 else 483 if ((factors[0] == (2*factors[1])) && (factors[2] == 0)) 484 factors[0]=2; 485 } 486 else 487 if (sscanf(sampling_factor,"%dx%d",factors,factors+1) == 2) 488 { 489 if (factors[0] != factors[1]) 490 factors[0]=0; 491 } 492 if ((factors[0] != 1) && (factors[0] != 2)) 493 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning, 494 "ignoring sampling-factor","=%s",sampling_factor); 495 else if (channels != 0) 496 { 497 /* 498 Cross check given color type and subsampling. 499 */ 500 factors[1]=((channels == IMF_WRITE_YCA) || 501 (channels == IMF_WRITE_YC)) ? 2 : 1; 502 if (factors[0] != factors[1]) 503 (void) ThrowMagickException(exception,GetMagickModule(), 504 CoderWarning,"sampling-factor and color type mismatch","=%s", 505 sampling_factor); 506 } 507 } 508 if (channels == 0) 509 { 510 /* 511 If no color type given, select it now. 512 */ 513 if (factors[0] == 2) 514 channels=image->alpha_trait != UndefinedPixelTrait ? IMF_WRITE_YCA : 515 IMF_WRITE_YC; 516 else 517 channels=image->alpha_trait != UndefinedPixelTrait ? IMF_WRITE_RGBA : 518 IMF_WRITE_RGB; 519 } 520 ImfHeaderSetCompression(hdr_info,compression); 521 ImfHeaderSetLineOrder(hdr_info,IMF_INCREASING_Y); 522 file=ImfOpenOutputFile(write_info->filename,hdr_info,channels); 523 ImfDeleteHeader(hdr_info); 524 if (file == (ImfOutputFile *) NULL) 525 { 526 (void) RelinquishUniqueFileResource(write_info->filename); 527 write_info=DestroyImageInfo(write_info); 528 ThrowFileException(exception,BlobError,"UnableToOpenBlob", 529 ImfErrorMessage()); 530 return(MagickFalse); 531 } 532 scanline=(ImfRgba *) AcquireQuantumMemory(image->columns,sizeof(*scanline)); 533 if (scanline == (ImfRgba *) NULL) 534 { 535 (void) ImfCloseOutputFile(file); 536 (void) RelinquishUniqueFileResource(write_info->filename); 537 write_info=DestroyImageInfo(write_info); 538 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 539 } 540 ResetMagickMemory(scanline,0,image->columns*sizeof(*scanline)); 541 for (y=0; y < (ssize_t) image->rows; y++) 542 { 543 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 544 if (p == (const Quantum *) NULL) 545 break; 546 for (x=0; x < (ssize_t) image->columns; x++) 547 { 548 ImfFloatToHalf(QuantumScale*GetPixelRed(image,p),&half_quantum); 549 scanline[x].r=half_quantum; 550 ImfFloatToHalf(QuantumScale*GetPixelGreen(image,p),&half_quantum); 551 scanline[x].g=half_quantum; 552 ImfFloatToHalf(QuantumScale*GetPixelBlue(image,p),&half_quantum); 553 scanline[x].b=half_quantum; 554 if (image->alpha_trait == UndefinedPixelTrait) 555 ImfFloatToHalf(1.0,&half_quantum); 556 else 557 ImfFloatToHalf(QuantumScale*GetPixelAlpha(image,p),&half_quantum); 558 scanline[x].a=half_quantum; 559 p+=GetPixelChannels(image); 560 } 561 ImfOutputSetFrameBuffer(file,scanline-(y*image->columns),1,image->columns); 562 ImfOutputWritePixels(file,1); 563 } 564 (void) ImfCloseOutputFile(file); 565 scanline=(ImfRgba *) RelinquishMagickMemory(scanline); 566 (void) FileToImage(image,write_info->filename,exception); 567 (void) RelinquishUniqueFileResource(write_info->filename); 568 write_info=DestroyImageInfo(write_info); 569 (void) CloseBlob(image); 570 return(MagickTrue); 571 } 572 #endif 573