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     return(DestroyImageList(image));
251   ImfHeaderDataWindow(hdr_info,&data_window.min_x,&data_window.min_y,
252     &data_window.max_x,&data_window.max_y);
253   columns=((size_t) data_window.max_x-data_window.min_x+1UL);
254   if ((display_window.min_x > data_window.max_x) ||
255       (display_window.min_x+(int) image->columns <= data_window.min_x))
256     scanline=(ImfRgba *) NULL;
257   else
258     {
259       scanline=(ImfRgba *) AcquireQuantumMemory(columns,sizeof(*scanline));
260       if (scanline == (ImfRgba *) NULL)
261         {
262           (void) ImfCloseInputFile(file);
263           image=DestroyImageList(image);
264           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
265         }
266     }
267   for (y=0; y < (ssize_t) image->rows; y++)
268   {
269     int
270       yy;
271 
272     ssize_t
273       x;
274 
275     q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
276     if (q == (Quantum *) NULL)
277       break;
278     yy=(int) (display_window.min_y+y);
279     if ((yy < data_window.min_y) || (yy > data_window.max_y) ||
280         (scanline == (ImfRgba *) NULL))
281       {
282         for (x=0; x < (ssize_t) image->columns; x++)
283         {
284           SetPixelViaPixelInfo(image,&image->background_color,q);
285           q+=GetPixelChannels(image);
286         }
287         if (SyncAuthenticPixels(image,exception) == MagickFalse)
288           break;
289         continue;
290       }
291     (void) memset(scanline,0,columns*sizeof(*scanline));
292     ImfInputSetFrameBuffer(file,scanline-data_window.min_x-columns*yy,1,
293       columns);
294     ImfInputReadPixels(file,yy,yy);
295     for (x=0; x < (ssize_t) image->columns; x++)
296     {
297       int
298         xx;
299 
300       xx=(int) (display_window.min_x+x-data_window.min_x);
301       if ((xx < 0) || (display_window.min_x+(int) x > data_window.max_x))
302         SetPixelViaPixelInfo(image,&image->background_color,q);
303       else
304         {
305           SetPixelRed(image,ClampToQuantum((MagickRealType) QuantumRange*
306             ImfHalfToFloat(scanline[xx].r)),q);
307           SetPixelGreen(image,ClampToQuantum((MagickRealType) QuantumRange*
308             ImfHalfToFloat(scanline[xx].g)),q);
309           SetPixelBlue(image,ClampToQuantum((MagickRealType) QuantumRange*
310             ImfHalfToFloat(scanline[xx].b)),q);
311           SetPixelAlpha(image,ClampToQuantum((MagickRealType) QuantumRange*
312             ImfHalfToFloat(scanline[xx].a)),q);
313         }
314       q+=GetPixelChannels(image);
315     }
316     if (SyncAuthenticPixels(image,exception) == MagickFalse)
317       break;
318   }
319   scanline=(ImfRgba *) RelinquishMagickMemory(scanline);
320   (void) ImfCloseInputFile(file);
321   (void) CloseBlob(image);
322   return(GetFirstImageInList(image));
323 }
324 #endif
325 
326 /*
327 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
328 %                                                                             %
329 %                                                                             %
330 %                                                                             %
331 %   R e g i s t e r E X R I m a g e                                           %
332 %                                                                             %
333 %                                                                             %
334 %                                                                             %
335 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
336 %
337 %  RegisterEXRImage() adds properties for the EXR image format
338 %  to the list of supported formats.  The properties include the image format
339 %  tag, a method to read and/or write the format, whether the format
340 %  supports the saving of more than one frame to the same file or blob,
341 %  whether the format supports native in-memory I/O, and a brief
342 %  description of the format.
343 %
344 %  The format of the RegisterEXRImage method is:
345 %
346 %      size_t RegisterEXRImage(void)
347 %
348 */
RegisterEXRImage(void)349 ModuleExport size_t RegisterEXRImage(void)
350 {
351   char
352     version[MagickPathExtent];
353 
354   MagickInfo
355     *entry;
356 
357   *version='\0';
358   entry=AcquireMagickInfo("EXR","EXR","High Dynamic-range (HDR)");
359 #if defined(MAGICKCORE_OPENEXR_DELEGATE)
360   entry->decoder=(DecodeImageHandler *) ReadEXRImage;
361   entry->encoder=(EncodeImageHandler *) WriteEXRImage;
362 #if defined( OPENEXR_PACKAGE_STRING)
363   (void) FormatLocaleString(version,MagickPathExtent,OPENEXR_PACKAGE_STRING);
364 #endif
365 #endif
366   entry->magick=(IsImageFormatHandler *) IsEXR;
367   if (*version != '\0')
368     entry->version=ConstantString(version);
369   entry->flags|=CoderDecoderSeekableStreamFlag;
370   entry->flags^=CoderAdjoinFlag;
371   entry->flags^=CoderBlobSupportFlag;
372   (void) RegisterMagickInfo(entry);
373   return(MagickImageCoderSignature);
374 }
375 
376 /*
377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
378 %                                                                             %
379 %                                                                             %
380 %                                                                             %
381 %   U n r e g i s t e r E X R I m a g e                                       %
382 %                                                                             %
383 %                                                                             %
384 %                                                                             %
385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
386 %
387 %  UnregisterEXRImage() removes format registrations made by the
388 %  EXR module from the list of supported formats.
389 %
390 %  The format of the UnregisterEXRImage method is:
391 %
392 %      UnregisterEXRImage(void)
393 %
394 */
UnregisterEXRImage(void)395 ModuleExport void UnregisterEXRImage(void)
396 {
397   (void) UnregisterMagickInfo("EXR");
398 }
399 
400 #if defined(MAGICKCORE_OPENEXR_DELEGATE)
401 /*
402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
403 %                                                                             %
404 %                                                                             %
405 %                                                                             %
406 %   W r i t e E X R I m a g e                                                 %
407 %                                                                             %
408 %                                                                             %
409 %                                                                             %
410 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
411 %
412 %  WriteEXRImage() writes an image to a file the in the high dynamic-range
413 %  (HDR) file format developed by Industrial Light & Magic.
414 %
415 %  The format of the WriteEXRImage method is:
416 %
417 %      MagickBooleanType WriteEXRImage(const ImageInfo *image_info,
418 %        Image *image,ExceptionInfo *exception)
419 %
420 %  A description of each parameter follows.
421 %
422 %    o image_info: the image info.
423 %
424 %    o image:  The image.
425 %
426 %    o exception: return any errors or warnings in this structure.
427 %
428 */
WriteEXRImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)429 static MagickBooleanType WriteEXRImage(const ImageInfo *image_info,Image *image,
430   ExceptionInfo *exception)
431 {
432   const char
433     *sampling_factor,
434     *value;
435 
436   ImageInfo
437     *write_info;
438 
439   ImfHalf
440     half_quantum;
441 
442   ImfHeader
443     *hdr_info;
444 
445   ImfOutputFile
446     *file;
447 
448   ImfRgba
449     *scanline;
450 
451   int
452     channels,
453     compression,
454     factors[3];
455 
456   MagickBooleanType
457     status;
458 
459   const Quantum
460     *p;
461 
462   ssize_t
463     x;
464 
465   ssize_t
466     y;
467 
468   /*
469     Open output image file.
470   */
471   assert(image_info != (const ImageInfo *) NULL);
472   assert(image_info->signature == MagickCoreSignature);
473   assert(image != (Image *) NULL);
474   assert(image->signature == MagickCoreSignature);
475   if (image->debug != MagickFalse)
476     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
477   assert(exception != (ExceptionInfo *) NULL);
478   assert(exception->signature == MagickCoreSignature);
479   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
480   if (status == MagickFalse)
481     return(status);
482   (void) SetImageColorspace(image,RGBColorspace,exception);
483   write_info=CloneImageInfo(image_info);
484   (void) AcquireUniqueFilename(write_info->filename);
485   hdr_info=ImfNewHeader();
486   ImfHeaderSetDataWindow(hdr_info,0,0,(int) image->columns-1,(int)
487     image->rows-1);
488   ImfHeaderSetDisplayWindow(hdr_info,0,0,(int) image->columns-1,(int)
489     image->rows-1);
490   compression=IMF_NO_COMPRESSION;
491   if (write_info->compression == RLECompression)
492     compression=IMF_RLE_COMPRESSION;
493   if (write_info->compression == ZipSCompression)
494     compression=IMF_ZIPS_COMPRESSION;
495   if (write_info->compression == ZipCompression)
496     compression=IMF_ZIP_COMPRESSION;
497   if (write_info->compression == PizCompression)
498     compression=IMF_PIZ_COMPRESSION;
499   if (write_info->compression == Pxr24Compression)
500     compression=IMF_PXR24_COMPRESSION;
501 #if defined(IMF_B44_COMPRESSION)
502   if (write_info->compression == B44Compression)
503     compression=IMF_B44_COMPRESSION;
504 #endif
505 #if defined(IMF_B44A_COMPRESSION)
506   if (write_info->compression == B44ACompression)
507     compression=IMF_B44A_COMPRESSION;
508 #endif
509 #if defined(IMF_DWAA_COMPRESSION)
510   if (write_info->compression == DWAACompression)
511     compression=IMF_DWAA_COMPRESSION;
512 #endif
513 #if defined(IMF_DWAB_COMPRESSION)
514   if (write_info->compression == DWABCompression)
515     compression=IMF_DWAB_COMPRESSION;
516 #endif
517   channels=0;
518   value=GetImageOption(image_info,"exr:color-type");
519   if (value != (const char *) NULL)
520     {
521       if (LocaleCompare(value,"RGB") == 0)
522         channels=IMF_WRITE_RGB;
523       else if (LocaleCompare(value,"RGBA") == 0)
524         channels=IMF_WRITE_RGBA;
525       else if (LocaleCompare(value,"YC") == 0)
526         channels=IMF_WRITE_YC;
527       else if (LocaleCompare(value,"YCA") == 0)
528         channels=IMF_WRITE_YCA;
529       else if (LocaleCompare(value,"Y") == 0)
530         channels=IMF_WRITE_Y;
531       else if (LocaleCompare(value,"YA") == 0)
532         channels=IMF_WRITE_YA;
533       else if (LocaleCompare(value,"R") == 0)
534         channels=IMF_WRITE_R;
535       else if (LocaleCompare(value,"G") == 0)
536         channels=IMF_WRITE_G;
537       else if (LocaleCompare(value,"B") == 0)
538         channels=IMF_WRITE_B;
539       else if (LocaleCompare(value,"A") == 0)
540         channels=IMF_WRITE_A;
541       else
542         (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
543           "ignoring invalid defined exr:color-type","=%s",value);
544    }
545   sampling_factor=(const char *) NULL;
546   factors[0]=0;
547   if (image_info->sampling_factor != (char *) NULL)
548     sampling_factor=image_info->sampling_factor;
549   if (sampling_factor != NULL)
550     {
551       /*
552         Sampling factors, valid values are 1x1 or 2x2.
553       */
554       if (sscanf(sampling_factor,"%d:%d:%d",factors,factors+1,factors+2) == 3)
555         {
556           if ((factors[0] == factors[1]) && (factors[1] == factors[2]))
557             factors[0]=1;
558           else
559             if ((factors[0] == (2*factors[1])) && (factors[2] == 0))
560               factors[0]=2;
561         }
562       else
563         if (sscanf(sampling_factor,"%dx%d",factors,factors+1) == 2)
564           {
565             if (factors[0] != factors[1])
566               factors[0]=0;
567           }
568       if ((factors[0] != 1) && (factors[0] != 2))
569         (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
570           "ignoring sampling-factor","=%s",sampling_factor);
571       else if (channels != 0)
572         {
573           /*
574             Cross check given color type and subsampling.
575           */
576           factors[1]=((channels == IMF_WRITE_YCA) ||
577             (channels == IMF_WRITE_YC)) ? 2 : 1;
578           if (factors[0] != factors[1])
579             (void) ThrowMagickException(exception,GetMagickModule(),
580               CoderWarning,"sampling-factor and color type mismatch","=%s",
581               sampling_factor);
582         }
583     }
584   if (channels == 0)
585     {
586       /*
587         If no color type given, select it now.
588       */
589       if (factors[0] == 2)
590         channels=image->alpha_trait != UndefinedPixelTrait ? IMF_WRITE_YCA :
591           IMF_WRITE_YC;
592       else
593         channels=image->alpha_trait != UndefinedPixelTrait ? IMF_WRITE_RGBA :
594           IMF_WRITE_RGB;
595     }
596   ImfHeaderSetCompression(hdr_info,compression);
597   ImfHeaderSetLineOrder(hdr_info,IMF_INCREASING_Y);
598   file=ImfOpenOutputFile(write_info->filename,hdr_info,channels);
599   ImfDeleteHeader(hdr_info);
600   if (file == (ImfOutputFile *) NULL)
601     {
602       (void) RelinquishUniqueFileResource(write_info->filename);
603       write_info=DestroyImageInfo(write_info);
604       ThrowFileException(exception,BlobError,"UnableToOpenBlob",
605         ImfErrorMessage());
606       return(MagickFalse);
607     }
608   scanline=(ImfRgba *) AcquireQuantumMemory(image->columns,sizeof(*scanline));
609   if (scanline == (ImfRgba *) NULL)
610     {
611       (void) ImfCloseOutputFile(file);
612       (void) RelinquishUniqueFileResource(write_info->filename);
613       write_info=DestroyImageInfo(write_info);
614       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
615     }
616   memset(scanline,0,image->columns*sizeof(*scanline));
617   for (y=0; y < (ssize_t) image->rows; y++)
618   {
619     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
620     if (p == (const Quantum *) NULL)
621       break;
622     for (x=0; x < (ssize_t) image->columns; x++)
623     {
624       ImfFloatToHalf(QuantumScale*GetPixelRed(image,p),&half_quantum);
625       scanline[x].r=half_quantum;
626       ImfFloatToHalf(QuantumScale*GetPixelGreen(image,p),&half_quantum);
627       scanline[x].g=half_quantum;
628       ImfFloatToHalf(QuantumScale*GetPixelBlue(image,p),&half_quantum);
629       scanline[x].b=half_quantum;
630       if (image->alpha_trait == UndefinedPixelTrait)
631         ImfFloatToHalf(1.0,&half_quantum);
632       else
633         ImfFloatToHalf(QuantumScale*GetPixelAlpha(image,p),&half_quantum);
634       scanline[x].a=half_quantum;
635       p+=GetPixelChannels(image);
636     }
637     ImfOutputSetFrameBuffer(file,scanline-(y*image->columns),1,image->columns);
638     ImfOutputWritePixels(file,1);
639   }
640   (void) ImfCloseOutputFile(file);
641   scanline=(ImfRgba *) RelinquishMagickMemory(scanline);
642   (void) FileToImage(image,write_info->filename,exception);
643   (void) RelinquishUniqueFileResource(write_info->filename);
644   write_info=DestroyImageInfo(write_info);
645   (void) CloseBlob(image);
646   return(MagickTrue);
647 }
648 #endif
649