exr.c revision e85007d004da78a4e1dc7738d894a7074cce596d
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%                                John Cristy                                  %
17%                                 April 2007                                  %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2010 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 "magick/studio.h"
43#include "magick/blob.h"
44#include "magick/blob-private.h"
45#include "magick/cache.h"
46#include "magick/exception.h"
47#include "magick/exception-private.h"
48#include "magick/image.h"
49#include "magick/image-private.h"
50#include "magick/list.h"
51#include "magick/magick.h"
52#include "magick/memory_.h"
53#include "magick/property.h"
54#include "magick/quantum-private.h"
55#include "magick/static.h"
56#include "magick/string_.h"
57#include "magick/module.h"
58#include "magick/resource_.h"
59#include "magick/utility.h"
60#if defined(MAGICKCORE_OPENEXR_DELEGATE)
61#include <ImfCRgbaFile.h>
62
63/*
64  Forward declarations.
65*/
66static MagickBooleanType
67  WriteEXRImage(const ImageInfo *,Image *);
68#endif
69
70/*
71%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72%                                                                             %
73%                                                                             %
74%                                                                             %
75%   I s E X R                                                                 %
76%                                                                             %
77%                                                                             %
78%                                                                             %
79%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80%
81%  IsEXR() returns MagickTrue if the image format type, identified by the
82%  magick string, is EXR.
83%
84%  The format of the IsEXR method is:
85%
86%      MagickBooleanType IsEXR(const unsigned char *magick,const size_t length)
87%
88%  A description of each parameter follows:
89%
90%    o magick: compare image format pattern against these bytes.
91%
92%    o length: Specifies the length of the magick string.
93%
94*/
95static MagickBooleanType IsEXR(const unsigned char *magick,const size_t length)
96{
97  if (length < 4)
98    return(MagickFalse);
99  if (memcmp(magick,"\166\057\061\001",4) == 0)
100    return(MagickTrue);
101  return(MagickFalse);
102}
103
104#if defined(MAGICKCORE_OPENEXR_DELEGATE)
105/*
106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
107%                                                                             %
108%                                                                             %
109%                                                                             %
110%   R e a d E X R I m a g e                                                   %
111%                                                                             %
112%                                                                             %
113%                                                                             %
114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115%
116%  ReadEXRImage reads an image in the high dynamic-range (HDR) file format
117%  developed by Industrial Light & Magic.  It allocates the memory necessary
118%  for the new Image structure and returns a pointer to the new image.
119%
120%  The format of the ReadEXRImage method is:
121%
122%      Image *ReadEXRImage(const ImageInfo *image_info,ExceptionInfo *exception)
123%
124%  A description of each parameter follows:
125%
126%    o image_info: the image info.
127%
128%    o exception: return any errors or warnings in this structure.
129%
130*/
131static Image *ReadEXRImage(const ImageInfo *image_info,ExceptionInfo *exception)
132{
133  const ImfHeader
134    *hdr_info;
135
136  Image
137    *image;
138
139  ImageInfo
140    *read_info;
141
142  ImfInputFile
143    *file;
144
145  ImfRgba
146    *scanline;
147
148  int
149    max_x,
150    max_y,
151    min_x,
152    min_y;
153
154  ssize_t
155    y;
156
157  register ssize_t
158    x;
159
160  register PixelPacket
161    *q;
162
163  MagickBooleanType
164    status;
165
166  /*
167    Open image.
168  */
169  assert(image_info != (const ImageInfo *) NULL);
170  assert(image_info->signature == MagickSignature);
171  if (image_info->debug != MagickFalse)
172    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
173      image_info->filename);
174  assert(exception != (ExceptionInfo *) NULL);
175  assert(exception->signature == MagickSignature);
176  image=AcquireImage(image_info);
177  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
178  if (status == MagickFalse)
179    {
180      image=DestroyImageList(image);
181      return((Image *) NULL);
182    }
183  read_info=CloneImageInfo(image_info);
184  if (IsPathAccessible(read_info->filename) == MagickFalse)
185    {
186      (void) AcquireUniqueFilename(read_info->filename);
187      (void) ImageToFile(image,read_info->filename,exception);
188    }
189  file=ImfOpenInputFile(read_info->filename);
190  if (file == (ImfInputFile *) NULL)
191    {
192      ThrowFileException(exception,BlobError,"UnableToOpenBlob",
193        ImfErrorMessage());
194      read_info=DestroyImageInfo(read_info);
195      return((Image *) NULL);
196    }
197  hdr_info=ImfInputHeader(file);
198  ImfHeaderDataWindow(hdr_info,&min_x,&min_y,&max_x,&max_y);
199  image->columns=max_x-min_x+1UL;
200  image->rows=max_y-min_y+1UL;
201  image->matte=MagickTrue;
202  if (image_info->ping != MagickFalse)
203    {
204      (void) ImfCloseInputFile(file);
205      if (LocaleCompare(image_info->filename,read_info->filename) != 0)
206        (void) RelinquishUniqueFileResource(read_info->filename);
207      read_info=DestroyImageInfo(read_info);
208      (void) CloseBlob(image);
209      return(GetFirstImageInList(image));
210    }
211  scanline=(ImfRgba *) AcquireQuantumMemory(image->columns,sizeof(*scanline));
212  if (scanline == (ImfRgba *) NULL)
213    {
214      (void) ImfCloseInputFile(file);
215      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
216    }
217  for (y=0; y < (ssize_t) image->rows; y++)
218  {
219    q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
220    if (q == (PixelPacket *) NULL)
221      break;
222    ImfInputSetFrameBuffer(file,scanline-min_x-image->columns*(min_y+y),1,
223      image->columns);
224    ImfInputReadPixels(file,min_y+y,min_y+y);
225    for (x=0; x < (ssize_t) image->columns; x++)
226    {
227      q->red=ClampToQuantum((MagickRealType) QuantumRange*ImfHalfToFloat(
228        scanline[x].r));
229      q->green=ClampToQuantum((MagickRealType) QuantumRange*ImfHalfToFloat(
230        scanline[x].g));
231      q->blue=ClampToQuantum((MagickRealType) QuantumRange*ImfHalfToFloat(
232        scanline[x].b));
233      q->opacity=ClampToQuantum((MagickRealType) QuantumRange-QuantumRange*
234        ImfHalfToFloat(scanline[x].a));
235      q++;
236    }
237    if (SyncAuthenticPixels(image,exception) == MagickFalse)
238      break;
239  }
240  scanline=(ImfRgba *) RelinquishMagickMemory(scanline);
241  (void) ImfCloseInputFile(file);
242  if (LocaleCompare(image_info->filename,read_info->filename) != 0)
243    (void) RelinquishUniqueFileResource(read_info->filename);
244  read_info=DestroyImageInfo(read_info);
245  (void) CloseBlob(image);
246  return(GetFirstImageInList(image));
247}
248#endif
249
250/*
251%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
252%                                                                             %
253%                                                                             %
254%                                                                             %
255%   R e g i s t e r E X R I m a g e                                           %
256%                                                                             %
257%                                                                             %
258%                                                                             %
259%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
260%
261%  RegisterEXRImage() adds properties for the EXR image format
262%  to the list of supported formats.  The properties include the image format
263%  tag, a method to read and/or write the format, whether the format
264%  supports the saving of more than one frame to the same file or blob,
265%  whether the format supports native in-memory I/O, and a brief
266%  description of the format.
267%
268%  The format of the RegisterEXRImage method is:
269%
270%      size_t RegisterEXRImage(void)
271%
272*/
273ModuleExport size_t RegisterEXRImage(void)
274{
275  MagickInfo
276    *entry;
277
278  entry=SetMagickInfo("EXR");
279#if defined(MAGICKCORE_OPENEXR_DELEGATE)
280  entry->decoder=(DecodeImageHandler *) ReadEXRImage;
281  entry->encoder=(EncodeImageHandler *) WriteEXRImage;
282#endif
283  entry->magick=(IsImageFormatHandler *) IsEXR;
284  entry->adjoin=MagickFalse;
285  entry->description=ConstantString("High Dynamic-range (HDR)");
286  entry->blob_support=MagickFalse;
287  entry->module=ConstantString("EXR");
288  (void) RegisterMagickInfo(entry);
289  return(MagickImageCoderSignature);
290}
291
292/*
293%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
294%                                                                             %
295%                                                                             %
296%                                                                             %
297%   U n r e g i s t e r E X R I m a g e                                       %
298%                                                                             %
299%                                                                             %
300%                                                                             %
301%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
302%
303%  UnregisterEXRImage() removes format registrations made by the
304%  EXR module from the list of supported formats.
305%
306%  The format of the UnregisterEXRImage method is:
307%
308%      UnregisterEXRImage(void)
309%
310*/
311ModuleExport void UnregisterEXRImage(void)
312{
313  (void) UnregisterMagickInfo("EXR");
314}
315
316#if defined(MAGICKCORE_OPENEXR_DELEGATE)
317/*
318%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
319%                                                                             %
320%                                                                             %
321%                                                                             %
322%   W r i t e E X R I m a g e                                                 %
323%                                                                             %
324%                                                                             %
325%                                                                             %
326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327%
328%  WriteEXRImage() writes an image to a file the in the high dynamic-range
329%  (HDR) file format developed by Industrial Light & Magic.
330%
331%  The format of the WriteEXRImage method is:
332%
333%      MagickBooleanType WriteEXRImage(const ImageInfo *image_info,Image *image)
334%
335%  A description of each parameter follows.
336%
337%    o image_info: the image info.
338%
339%    o image:  The image.
340%
341*/
342static MagickBooleanType WriteEXRImage(const ImageInfo *image_info,Image *image)
343{
344  ImageInfo
345    *write_info;
346
347  ImfHalf
348    half_quantum;
349
350  ImfHeader
351    *hdr_info;
352
353  ImfOutputFile
354    *file;
355
356  ImfRgba
357    *scanline;
358
359  int
360    compression;
361
362  ssize_t
363    y;
364
365  MagickBooleanType
366    status;
367
368  register const PixelPacket
369    *p;
370
371  register ssize_t
372    x;
373
374  /*
375    Open output image file.
376  */
377  assert(image_info != (const ImageInfo *) NULL);
378  assert(image_info->signature == MagickSignature);
379  assert(image != (Image *) NULL);
380  assert(image->signature == MagickSignature);
381  if (image->debug != MagickFalse)
382    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
383  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
384  if (status == MagickFalse)
385    return(status);
386  write_info=CloneImageInfo(image_info);
387  (void) AcquireUniqueFilename(write_info->filename);
388  hdr_info=ImfNewHeader();
389  ImfHeaderSetDataWindow(hdr_info,0,0,(int) image->columns-1,(int)
390    image->rows-1);
391  ImfHeaderSetDisplayWindow(hdr_info,0,0,(int) image->columns-1,(int)
392    image->rows-1);
393  compression=IMF_NO_COMPRESSION;
394  if (write_info->compression == ZipSCompression)
395    compression=IMF_ZIPS_COMPRESSION;
396  if (write_info->compression == ZipCompression)
397    compression=IMF_ZIP_COMPRESSION;
398  if (write_info->compression == PizCompression)
399    compression=IMF_PIZ_COMPRESSION;
400  if (write_info->compression == Pxr24Compression)
401    compression=IMF_PXR24_COMPRESSION;
402#if defined(B44Compression)
403  if (write_info->compression == B44Compression)
404    compression=IMF_B44_COMPRESSION;
405#endif
406#if defined(B44ACompression)
407  if (write_info->compression == B44ACompression)
408    compression=IMF_B44A_COMPRESSION;
409#endif
410  ImfHeaderSetCompression(hdr_info,compression);
411  ImfHeaderSetLineOrder(hdr_info,IMF_INCREASING_Y);
412  file=ImfOpenOutputFile(write_info->filename,hdr_info,IMF_WRITE_RGBA);
413  ImfDeleteHeader(hdr_info);
414  if (file == (ImfOutputFile *) NULL)
415    {
416      ThrowFileException(&image->exception,BlobError,"UnableToOpenBlob",
417        ImfErrorMessage());
418      write_info=DestroyImageInfo(write_info);
419      return(MagickFalse);
420    }
421  scanline=(ImfRgba *) AcquireQuantumMemory(image->columns,sizeof(*scanline));
422  if (scanline == (ImfRgba *) NULL)
423    {
424      (void) ImfCloseOutputFile(file);
425      ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
426    }
427  for (y=0; y < (ssize_t) image->rows; y++)
428  {
429    p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
430    if (p == (const PixelPacket *) NULL)
431      break;
432    for (x=0; x < (ssize_t) image->columns; x++)
433    {
434      ImfFloatToHalf(QuantumScale*p->red,&half_quantum);
435      scanline[x].r=half_quantum;
436      ImfFloatToHalf(QuantumScale*p->green,&half_quantum);
437      scanline[x].g=half_quantum;
438      ImfFloatToHalf(QuantumScale*p->blue,&half_quantum);
439      scanline[x].b=half_quantum;
440      if (image->matte == MagickFalse)
441        ImfFloatToHalf(1.0,&half_quantum);
442      else
443        ImfFloatToHalf(1.0-QuantumScale*p->opacity,&half_quantum);
444      scanline[x].a=half_quantum;
445      p++;
446    }
447    ImfOutputSetFrameBuffer(file,scanline-(y*image->columns),1,image->columns);
448    ImfOutputWritePixels(file,1);
449  }
450  (void) ImfCloseOutputFile(file);
451  scanline=(ImfRgba *) RelinquishMagickMemory(scanline);
452  (void) FileToImage(image,write_info->filename);
453  (void) RelinquishUniqueFileResource(write_info->filename);
454  write_info=DestroyImageInfo(write_info);
455  (void) CloseBlob(image);
456  return(MagickTrue);
457}
458#endif
459