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