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