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 */
68 static 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 */
IsEXR(const unsigned char * magick,const size_t length)97 static 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 */
ReadEXRImage(const ImageInfo * image_info,ExceptionInfo * exception)133 static 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 */
RegisterEXRImage(void)286 ModuleExport 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 */
UnregisterEXRImage(void)322 ModuleExport 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 */
WriteEXRImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)356 static 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