]> granicus.if.org Git - imagemagick/blob - coders/viff.c
(no commit message)
[imagemagick] / coders / viff.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                        V   V  IIIII  FFFFF  FFFFF                           %
7 %                        V   V    I    F      F                               %
8 %                        V   V    I    FFF    FFF                             %
9 %                         V V     I    F      F                               %
10 %                          V    IIIII  F      F                               %
11 %                                                                             %
12 %                                                                             %
13 %                Read/Write Khoros Visualization Image Format                 %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2014 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 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/attribute.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/blob-private.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colormap.h"
50 #include "MagickCore/colorspace.h"
51 #include "MagickCore/colorspace-private.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/image.h"
55 #include "MagickCore/image-private.h"
56 #include "MagickCore/list.h"
57 #include "MagickCore/magick.h"
58 #include "MagickCore/memory_.h"
59 #include "MagickCore/monitor.h"
60 #include "MagickCore/monitor-private.h"
61 #include "MagickCore/pixel-accessor.h"
62 #include "MagickCore/property.h"
63 #include "MagickCore/quantum-private.h"
64 #include "MagickCore/static.h"
65 #include "MagickCore/string_.h"
66 #include "MagickCore/module.h"
67 \f
68 /*
69   Forward declarations.
70 */
71 static MagickBooleanType
72   WriteVIFFImage(const ImageInfo *,Image *,ExceptionInfo *);
73 \f
74 /*
75 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76 %                                                                             %
77 %                                                                             %
78 %                                                                             %
79 %   I s V I F F                                                               %
80 %                                                                             %
81 %                                                                             %
82 %                                                                             %
83 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84 %
85 %  IsVIFF() returns MagickTrue if the image format type, identified by the
86 %  magick string, is VIFF.
87 %
88 %  The format of the IsVIFF method is:
89 %
90 %      MagickBooleanType IsVIFF(const unsigned char *magick,const size_t length)
91 %
92 %  A description of each parameter follows:
93 %
94 %    o magick: compare image format pattern against these bytes.
95 %
96 %    o length: Specifies the length of the magick string.
97 %
98 */
99 static MagickBooleanType IsVIFF(const unsigned char *magick,const size_t length)
100 {
101   if (length < 2)
102     return(MagickFalse);
103   if (memcmp(magick,"\253\001",2) == 0)
104     return(MagickTrue);
105   return(MagickFalse);
106 }
107 \f
108 /*
109 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
110 %                                                                             %
111 %                                                                             %
112 %                                                                             %
113 %   R e a d V I F F I m a g e                                                 %
114 %                                                                             %
115 %                                                                             %
116 %                                                                             %
117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
118 %
119 %  ReadVIFFImage() reads a Khoros Visualization image file and returns
120 %  it.  It allocates the memory necessary for the new Image structure and
121 %  returns a pointer to the new image.
122 %
123 %  The format of the ReadVIFFImage method is:
124 %
125 %      Image *ReadVIFFImage(const ImageInfo *image_info,
126 %        ExceptionInfo *exception)
127 %
128 %  A description of each parameter follows:
129 %
130 %    o image: Method ReadVIFFImage returns a pointer to the image after
131 %      reading.  A null image is returned if there is a memory shortage or if
132 %      the image cannot be read.
133 %
134 %    o image_info: the image info.
135 %
136 %    o exception: return any errors or warnings in this structure.
137 %
138 */
139 static Image *ReadVIFFImage(const ImageInfo *image_info,
140   ExceptionInfo *exception)
141 {
142 #define VFF_CM_genericRGB  15
143 #define VFF_CM_ntscRGB  1
144 #define VFF_CM_NONE  0
145 #define VFF_DEP_DECORDER  0x4
146 #define VFF_DEP_NSORDER  0x8
147 #define VFF_DES_RAW  0
148 #define VFF_LOC_IMPLICIT  1
149 #define VFF_MAPTYP_NONE  0
150 #define VFF_MAPTYP_1_BYTE  1
151 #define VFF_MAPTYP_2_BYTE  2
152 #define VFF_MAPTYP_4_BYTE  4
153 #define VFF_MAPTYP_FLOAT  5
154 #define VFF_MAPTYP_DOUBLE  7
155 #define VFF_MS_NONE  0
156 #define VFF_MS_ONEPERBAND  1
157 #define VFF_MS_SHARED  3
158 #define VFF_TYP_BIT  0
159 #define VFF_TYP_1_BYTE  1
160 #define VFF_TYP_2_BYTE  2
161 #define VFF_TYP_4_BYTE  4
162 #define VFF_TYP_FLOAT  5
163 #define VFF_TYP_DOUBLE  9
164
165   typedef struct _ViffInfo
166   {
167     unsigned char
168       identifier,
169       file_type,
170       release,
171       version,
172       machine_dependency,
173       reserve[3];
174
175     char
176       comment[512];
177
178     unsigned int
179       rows,
180       columns,
181       subrows;
182
183     int
184       x_offset,
185       y_offset;
186
187     float
188       x_bits_per_pixel,
189       y_bits_per_pixel;
190
191     unsigned int
192       location_type,
193       location_dimension,
194       number_of_images,
195       number_data_bands,
196       data_storage_type,
197       data_encode_scheme,
198       map_scheme,
199       map_storage_type,
200       map_rows,
201       map_columns,
202       map_subrows,
203       map_enable,
204       maps_per_cycle,
205       color_space_model;
206   } ViffInfo;
207
208   double
209     min_value,
210     scale_factor,
211     value;
212
213   Image
214     *image;
215
216   int
217     bit;
218
219   MagickBooleanType
220     status;
221
222   MagickSizeType
223     number_pixels;
224
225   register ssize_t
226     x;
227
228   register Quantum
229     *q;
230
231   register ssize_t
232     i;
233
234   register unsigned char
235     *p;
236
237   size_t
238     bytes_per_pixel,
239     lsb_first,
240     max_packets,
241     quantum;
242
243   ssize_t
244     count,
245     y;
246
247   unsigned char
248     buffer[7],
249     *pixels;
250
251   ViffInfo
252     viff_info;
253
254   /*
255     Open image file.
256   */
257   assert(image_info != (const ImageInfo *) NULL);
258   assert(image_info->signature == MagickSignature);
259   if (image_info->debug != MagickFalse)
260     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
261       image_info->filename);
262   assert(exception != (ExceptionInfo *) NULL);
263   assert(exception->signature == MagickSignature);
264   image=AcquireImage(image_info,exception);
265   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
266   if (status == MagickFalse)
267     {
268       image=DestroyImageList(image);
269       return((Image *) NULL);
270     }
271   /*
272     Read VIFF header (1024 bytes).
273   */
274   count=ReadBlob(image,1,&viff_info.identifier);
275   do
276   {
277     /*
278       Verify VIFF identifier.
279     */
280     if ((count == 0) || ((unsigned char) viff_info.identifier != 0xab))
281       ThrowReaderException(CorruptImageError,"NotAVIFFImage");
282     /*
283       Initialize VIFF image.
284     */
285     count=ReadBlob(image,7,buffer);
286     viff_info.file_type=buffer[0];
287     viff_info.release=buffer[1];
288     viff_info.version=buffer[2];
289     viff_info.machine_dependency=buffer[3];
290     count=ReadBlob(image,512,(unsigned char *) viff_info.comment);
291     viff_info.comment[511]='\0';
292     if (strlen(viff_info.comment) > 4)
293       (void) SetImageProperty(image,"comment",viff_info.comment,exception);
294     if ((viff_info.machine_dependency == VFF_DEP_DECORDER) ||
295         (viff_info.machine_dependency == VFF_DEP_NSORDER))
296       {
297         viff_info.rows=ReadBlobLSBLong(image);
298         viff_info.columns=ReadBlobLSBLong(image);
299         viff_info.subrows=ReadBlobLSBLong(image);
300         viff_info.x_offset=(int) ReadBlobLSBLong(image);
301         viff_info.y_offset=(int) ReadBlobLSBLong(image);
302         viff_info.x_bits_per_pixel=(float) ReadBlobLSBLong(image);
303         viff_info.y_bits_per_pixel=(float) ReadBlobLSBLong(image);
304         viff_info.location_type=ReadBlobLSBLong(image);
305         viff_info.location_dimension=ReadBlobLSBLong(image);
306         viff_info.number_of_images=ReadBlobLSBLong(image);
307         viff_info.number_data_bands=ReadBlobLSBLong(image);
308         viff_info.data_storage_type=ReadBlobLSBLong(image);
309         viff_info.data_encode_scheme=ReadBlobLSBLong(image);
310         viff_info.map_scheme=ReadBlobLSBLong(image);
311         viff_info.map_storage_type=ReadBlobLSBLong(image);
312         viff_info.map_rows=ReadBlobLSBLong(image);
313         viff_info.map_columns=ReadBlobLSBLong(image);
314         viff_info.map_subrows=ReadBlobLSBLong(image);
315         viff_info.map_enable=ReadBlobLSBLong(image);
316         viff_info.maps_per_cycle=ReadBlobLSBLong(image);
317         viff_info.color_space_model=ReadBlobLSBLong(image);
318       }
319     else
320       {
321         viff_info.rows=ReadBlobMSBLong(image);
322         viff_info.columns=ReadBlobMSBLong(image);
323         viff_info.subrows=ReadBlobMSBLong(image);
324         viff_info.x_offset=(int) ReadBlobMSBLong(image);
325         viff_info.y_offset=(int) ReadBlobMSBLong(image);
326         viff_info.x_bits_per_pixel=(float) ReadBlobMSBLong(image);
327         viff_info.y_bits_per_pixel=(float) ReadBlobMSBLong(image);
328         viff_info.location_type=ReadBlobMSBLong(image);
329         viff_info.location_dimension=ReadBlobMSBLong(image);
330         viff_info.number_of_images=ReadBlobMSBLong(image);
331         viff_info.number_data_bands=ReadBlobMSBLong(image);
332         viff_info.data_storage_type=ReadBlobMSBLong(image);
333         viff_info.data_encode_scheme=ReadBlobMSBLong(image);
334         viff_info.map_scheme=ReadBlobMSBLong(image);
335         viff_info.map_storage_type=ReadBlobMSBLong(image);
336         viff_info.map_rows=ReadBlobMSBLong(image);
337         viff_info.map_columns=ReadBlobMSBLong(image);
338         viff_info.map_subrows=ReadBlobMSBLong(image);
339         viff_info.map_enable=ReadBlobMSBLong(image);
340         viff_info.maps_per_cycle=ReadBlobMSBLong(image);
341         viff_info.color_space_model=ReadBlobMSBLong(image);
342       }
343     for (i=0; i < 420; i++)
344       (void) ReadBlobByte(image);
345     image->columns=viff_info.rows;
346     image->rows=viff_info.columns;
347     image->depth=viff_info.x_bits_per_pixel <= 8 ? 8UL :
348       MAGICKCORE_QUANTUM_DEPTH;
349     /*
350       Verify that we can read this VIFF image.
351     */
352     number_pixels=(MagickSizeType) viff_info.columns*viff_info.rows;
353     if (number_pixels != (size_t) number_pixels)
354       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
355     if (number_pixels == 0)
356       ThrowReaderException(CoderError,"ImageColumnOrRowSizeIsNotSupported");
357     if ((viff_info.number_data_bands < 1) || (viff_info.number_data_bands > 4))
358       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
359     if ((viff_info.data_storage_type != VFF_TYP_BIT) &&
360         (viff_info.data_storage_type != VFF_TYP_1_BYTE) &&
361         (viff_info.data_storage_type != VFF_TYP_2_BYTE) &&
362         (viff_info.data_storage_type != VFF_TYP_4_BYTE) &&
363         (viff_info.data_storage_type != VFF_TYP_FLOAT) &&
364         (viff_info.data_storage_type != VFF_TYP_DOUBLE))
365       ThrowReaderException(CoderError,"DataStorageTypeIsNotSupported");
366     if (viff_info.data_encode_scheme != VFF_DES_RAW)
367       ThrowReaderException(CoderError,"DataEncodingSchemeIsNotSupported");
368     if ((viff_info.map_storage_type != VFF_MAPTYP_NONE) &&
369         (viff_info.map_storage_type != VFF_MAPTYP_1_BYTE) &&
370         (viff_info.map_storage_type != VFF_MAPTYP_2_BYTE) &&
371         (viff_info.map_storage_type != VFF_MAPTYP_4_BYTE) &&
372         (viff_info.map_storage_type != VFF_MAPTYP_FLOAT) &&
373         (viff_info.map_storage_type != VFF_MAPTYP_DOUBLE))
374       ThrowReaderException(CoderError,"MapStorageTypeIsNotSupported");
375     if ((viff_info.color_space_model != VFF_CM_NONE) &&
376         (viff_info.color_space_model != VFF_CM_ntscRGB) &&
377         (viff_info.color_space_model != VFF_CM_genericRGB))
378       ThrowReaderException(CoderError,"ColorspaceModelIsNotSupported");
379     if (viff_info.location_type != VFF_LOC_IMPLICIT)
380       ThrowReaderException(CoderError,"LocationTypeIsNotSupported");
381     if (viff_info.number_of_images != 1)
382       ThrowReaderException(CoderError,"NumberOfImagesIsNotSupported");
383     if (viff_info.map_rows == 0)
384       viff_info.map_scheme=VFF_MS_NONE;
385     switch ((int) viff_info.map_scheme)
386     {
387       case VFF_MS_NONE:
388       {
389         if (viff_info.number_data_bands < 3)
390           {
391             /*
392               Create linear color ramp.
393             */
394             if (viff_info.data_storage_type == VFF_TYP_BIT)
395               image->colors=2;
396             else if (viff_info.data_storage_type == VFF_MAPTYP_1_BYTE)
397               image->colors=256UL;
398             else
399               image->colors=image->depth <= 8 ? 256UL : 65536UL;
400             if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
401               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
402           }
403         break;
404       }
405       case VFF_MS_ONEPERBAND:
406       case VFF_MS_SHARED:
407       {
408         unsigned char
409           *viff_colormap;
410
411         /*
412           Allocate VIFF colormap.
413         */
414         switch ((int) viff_info.map_storage_type)
415         {
416           case VFF_MAPTYP_1_BYTE: bytes_per_pixel=1; break;
417           case VFF_MAPTYP_2_BYTE: bytes_per_pixel=2; break;
418           case VFF_MAPTYP_4_BYTE: bytes_per_pixel=4; break;
419           case VFF_MAPTYP_FLOAT: bytes_per_pixel=4; break;
420           case VFF_MAPTYP_DOUBLE: bytes_per_pixel=8; break;
421           default: bytes_per_pixel=1; break;
422         }
423         image->colors=viff_info.map_columns;
424         if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
425           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
426         viff_colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
427           viff_info.map_rows*bytes_per_pixel*sizeof(*viff_colormap));
428         if (viff_colormap == (unsigned char *) NULL)
429           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
430         /*
431           Read VIFF raster colormap.
432         */
433         count=ReadBlob(image,bytes_per_pixel*image->colors*viff_info.map_rows,
434           viff_colormap);
435         lsb_first=1;
436         if (*(char *) &lsb_first &&
437             ((viff_info.machine_dependency != VFF_DEP_DECORDER) &&
438              (viff_info.machine_dependency != VFF_DEP_NSORDER)))
439           switch ((int) viff_info.map_storage_type)
440           {
441             case VFF_MAPTYP_2_BYTE:
442             {
443               MSBOrderShort(viff_colormap,(bytes_per_pixel*image->colors*
444                 viff_info.map_rows));
445               break;
446             }
447             case VFF_MAPTYP_4_BYTE:
448             case VFF_MAPTYP_FLOAT:
449             {
450               MSBOrderLong(viff_colormap,(bytes_per_pixel*image->colors*
451                 viff_info.map_rows));
452               break;
453             }
454             default: break;
455           }
456         for (i=0; i < (ssize_t) (viff_info.map_rows*image->colors); i++)
457         {
458           switch ((int) viff_info.map_storage_type)
459           {
460             case VFF_MAPTYP_2_BYTE: value=1.0*((short *) viff_colormap)[i]; break;
461             case VFF_MAPTYP_4_BYTE: value=1.0*((int *) viff_colormap)[i]; break;
462             case VFF_MAPTYP_FLOAT: value=((float *) viff_colormap)[i]; break;
463             case VFF_MAPTYP_DOUBLE: value=((double *) viff_colormap)[i]; break;
464             default: value=1.0*viff_colormap[i]; break;
465           }
466           if (i < (ssize_t) image->colors)
467             {
468               image->colormap[i].red=ScaleCharToQuantum((unsigned char) value);
469               image->colormap[i].green=
470                 ScaleCharToQuantum((unsigned char) value);
471               image->colormap[i].blue=ScaleCharToQuantum((unsigned char) value);
472             }
473           else
474             if (i < (ssize_t) (2*image->colors))
475               image->colormap[i % image->colors].green=
476                 ScaleCharToQuantum((unsigned char) value);
477             else
478               if (i < (ssize_t) (3*image->colors))
479                 image->colormap[i % image->colors].blue=
480                   ScaleCharToQuantum((unsigned char) value);
481         }
482         viff_colormap=(unsigned char *) RelinquishMagickMemory(viff_colormap);
483         break;
484       }
485       default:
486         ThrowReaderException(CoderError,"ColormapTypeNotSupported");
487     }
488     /*
489       Initialize image structure.
490     */
491     image->alpha_trait=viff_info.number_data_bands == 4 ? BlendPixelTrait : 
492       UndefinedPixelTrait;
493     image->storage_class=(viff_info.number_data_bands < 3 ? PseudoClass :
494       DirectClass);
495     image->columns=viff_info.rows;
496     image->rows=viff_info.columns;
497     if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
498       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
499         break;
500     /*
501       Allocate VIFF pixels.
502     */
503     switch ((int) viff_info.data_storage_type)
504     {
505       case VFF_TYP_2_BYTE: bytes_per_pixel=2; break;
506       case VFF_TYP_4_BYTE: bytes_per_pixel=4; break;
507       case VFF_TYP_FLOAT: bytes_per_pixel=4; break;
508       case VFF_TYP_DOUBLE: bytes_per_pixel=8; break;
509       default: bytes_per_pixel=1; break;
510     }
511     if (viff_info.data_storage_type == VFF_TYP_BIT)
512       max_packets=((image->columns+7UL) >> 3UL)*image->rows;
513     else
514       max_packets=(size_t) (number_pixels*viff_info.number_data_bands);
515     pixels=(unsigned char *) AcquireQuantumMemory(max_packets,
516       bytes_per_pixel*sizeof(*pixels));
517     if (pixels == (unsigned char *) NULL)
518       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
519     count=ReadBlob(image,bytes_per_pixel*max_packets,pixels);
520     lsb_first=1;
521     if (*(char *) &lsb_first &&
522         ((viff_info.machine_dependency != VFF_DEP_DECORDER) &&
523          (viff_info.machine_dependency != VFF_DEP_NSORDER)))
524       switch ((int) viff_info.data_storage_type)
525       {
526         case VFF_TYP_2_BYTE:
527         {
528           MSBOrderShort(pixels,bytes_per_pixel*max_packets);
529           break;
530         }
531         case VFF_TYP_4_BYTE:
532         case VFF_TYP_FLOAT:
533         {
534           MSBOrderLong(pixels,bytes_per_pixel*max_packets);
535           break;
536         }
537         default: break;
538       }
539     min_value=0.0;
540     scale_factor=1.0;
541     if ((viff_info.data_storage_type != VFF_TYP_1_BYTE) &&
542         (viff_info.map_scheme == VFF_MS_NONE))
543       {
544         double
545           max_value;
546
547         /*
548           Determine scale factor.
549         */
550         switch ((int) viff_info.data_storage_type)
551         {
552           case VFF_TYP_2_BYTE: value=1.0*((short *) pixels)[0]; break;
553           case VFF_TYP_4_BYTE: value=1.0*((int *) pixels)[0]; break;
554           case VFF_TYP_FLOAT: value=((float *) pixels)[0]; break;
555           case VFF_TYP_DOUBLE: value=((double *) pixels)[0]; break;
556           default: value=1.0*pixels[0]; break;
557         }
558         max_value=value;
559         min_value=value;
560         for (i=0; i < (ssize_t) max_packets; i++)
561         {
562           switch ((int) viff_info.data_storage_type)
563           {
564             case VFF_TYP_2_BYTE: value=1.0*((short *) pixels)[i]; break;
565             case VFF_TYP_4_BYTE: value=1.0*((int *) pixels)[i]; break;
566             case VFF_TYP_FLOAT: value=((float *) pixels)[i]; break;
567             case VFF_TYP_DOUBLE: value=((double *) pixels)[i]; break;
568             default: value=1.0*pixels[i]; break;
569           }
570           if (value > max_value)
571             max_value=value;
572           else
573             if (value < min_value)
574               min_value=value;
575         }
576         if ((min_value == 0) && (max_value == 0))
577           scale_factor=0;
578         else
579           if (min_value == max_value)
580             {
581               scale_factor=(double) QuantumRange/min_value;
582               min_value=0;
583             }
584           else
585             scale_factor=(double) QuantumRange/(max_value-min_value);
586       }
587     /*
588       Convert pixels to Quantum size.
589     */
590     p=(unsigned char *) pixels;
591     for (i=0; i < (ssize_t) max_packets; i++)
592     {
593       switch ((int) viff_info.data_storage_type)
594       {
595         case VFF_TYP_2_BYTE: value=1.0*((short *) pixels)[i]; break;
596         case VFF_TYP_4_BYTE: value=1.0*((int *) pixels)[i]; break;
597         case VFF_TYP_FLOAT: value=((float *) pixels)[i]; break;
598         case VFF_TYP_DOUBLE: value=((double *) pixels)[i]; break;
599         default: value=1.0*pixels[i]; break;
600       }
601       if (viff_info.map_scheme == VFF_MS_NONE)
602         {
603           value=(value-min_value)*scale_factor;
604           if (value > QuantumRange)
605             value=QuantumRange;
606           else
607             if (value < 0)
608               value=0;
609         }
610       *p=(unsigned char) value;
611       p++;
612     }
613     /*
614       Convert VIFF raster image to pixel packets.
615     */
616     p=(unsigned char *) pixels;
617     if (viff_info.data_storage_type == VFF_TYP_BIT)
618       {
619         /*
620           Convert bitmap scanline.
621         */
622         (void) SetImageType(image,BilevelType,exception);
623         (void) SetImageType(image,PaletteType,exception);
624         for (y=0; y < (ssize_t) image->rows; y++)
625         {
626           q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
627           if (q == (Quantum *) NULL)
628             break;
629           for (x=0; x < (ssize_t) (image->columns-7); x+=8)
630           {
631             for (bit=0; bit < 8; bit++)
632             {
633               if (GetPixelLuma(image,q) < (QuantumRange/2.0))
634                 {
635                   quantum=(size_t) GetPixelIndex(image,q);
636                   quantum|=0x01;
637                   SetPixelIndex(image,quantum,q);
638                 }
639               q+=GetPixelChannels(image);
640             }
641             p++;
642           }
643           if ((image->columns % 8) != 0)
644             {
645               for (bit=0; bit < (ssize_t) (image->columns % 8); bit++)
646                 if (GetPixelLuma(image,q) < (QuantumRange/2.0))
647                   {
648                     quantum=(size_t) GetPixelIndex(image,q);
649                     quantum|=0x01;
650                     SetPixelIndex(image,quantum,q);
651                     q+=GetPixelChannels(image);
652                   }
653               p++;
654             }
655           if (SyncAuthenticPixels(image,exception) == MagickFalse)
656             break;
657           if (image->previous == (Image *) NULL)
658             {
659               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
660                 image->rows);
661               if (status == MagickFalse)
662                 break;
663             }
664         }
665       }
666     else
667       if (image->storage_class == PseudoClass)
668         for (y=0; y < (ssize_t) image->rows; y++)
669         {
670           q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
671           if (q == (Quantum *) NULL)
672             break;
673           for (x=0; x < (ssize_t) image->columns; x++)
674           {
675             SetPixelIndex(image,*p++,q);
676             q+=GetPixelChannels(image);
677           }
678           if (SyncAuthenticPixels(image,exception) == MagickFalse)
679             break;
680           if (image->previous == (Image *) NULL)
681             {
682               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
683                 image->rows);
684               if (status == MagickFalse)
685                 break;
686             }
687         }
688       else
689         {
690           /*
691             Convert DirectColor scanline.
692           */
693           number_pixels=(MagickSizeType) image->columns*image->rows;
694           for (y=0; y < (ssize_t) image->rows; y++)
695           {
696             q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
697             if (q == (Quantum *) NULL)
698               break;
699             for (x=0; x < (ssize_t) image->columns; x++)
700             {
701               SetPixelRed(image,ScaleCharToQuantum(*p),q);
702               SetPixelGreen(image,ScaleCharToQuantum(*(p+number_pixels)),q);
703               SetPixelBlue(image,ScaleCharToQuantum(*(p+2*number_pixels)),q);
704               if (image->colors != 0)
705                 {
706                   SetPixelRed(image,image->colormap[(ssize_t)
707                     GetPixelRed(image,q)].red,q);
708                   SetPixelGreen(image,image->colormap[(ssize_t)
709                     GetPixelGreen(image,q)].green,q);
710                   SetPixelBlue(image,image->colormap[(ssize_t)
711                     GetPixelBlue(image,q)].blue,q);
712                 }
713               SetPixelAlpha(image,image->alpha_trait == BlendPixelTrait ?
714                 ScaleCharToQuantum(*(p+number_pixels*3)) : OpaqueAlpha,q);
715               p++;
716               q+=GetPixelChannels(image);
717             }
718             if (SyncAuthenticPixels(image,exception) == MagickFalse)
719               break;
720             if (image->previous == (Image *) NULL)
721               {
722                 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
723                 image->rows);
724                 if (status == MagickFalse)
725                   break;
726               }
727           }
728         }
729     pixels=(unsigned char *) RelinquishMagickMemory(pixels);
730     if (image->storage_class == PseudoClass)
731       (void) SyncImage(image,exception);
732     if (EOFBlob(image) != MagickFalse)
733       {
734         ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
735           image->filename);
736         break;
737       }
738     /*
739       Proceed to next image.
740     */
741     if (image_info->number_scenes != 0)
742       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
743         break;
744     count=ReadBlob(image,1,&viff_info.identifier);
745     if ((count != 0) && (viff_info.identifier == 0xab))
746       {
747         /*
748           Allocate next image structure.
749         */
750         AcquireNextImage(image_info,image,exception);
751         if (GetNextImageInList(image) == (Image *) NULL)
752           {
753             image=DestroyImageList(image);
754             return((Image *) NULL);
755           }
756         image=SyncNextImageInList(image);
757         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
758           GetBlobSize(image));
759         if (status == MagickFalse)
760           break;
761       }
762   } while ((count != 0) && (viff_info.identifier == 0xab));
763   (void) CloseBlob(image);
764   return(GetFirstImageInList(image));
765 }
766 \f
767 /*
768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
769 %                                                                             %
770 %                                                                             %
771 %                                                                             %
772 %   R e g i s t e r V I F F I m a g e                                         %
773 %                                                                             %
774 %                                                                             %
775 %                                                                             %
776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
777 %
778 %  RegisterVIFFImage() adds properties for the VIFF image format to
779 %  the list of supported formats.  The properties include the image format
780 %  tag, a method to read and/or write the format, whether the format
781 %  supports the saving of more than one frame to the same file or blob,
782 %  whether the format supports native in-memory I/O, and a brief
783 %  description of the format.
784 %
785 %  The format of the RegisterVIFFImage method is:
786 %
787 %      size_t RegisterVIFFImage(void)
788 %
789 */
790 ModuleExport size_t RegisterVIFFImage(void)
791 {
792   MagickInfo
793     *entry;
794
795   entry=SetMagickInfo("VIFF");
796   entry->decoder=(DecodeImageHandler *) ReadVIFFImage;
797   entry->encoder=(EncodeImageHandler *) WriteVIFFImage;
798   entry->magick=(IsImageFormatHandler *) IsVIFF;
799   entry->description=ConstantString("Khoros Visualization image");
800   entry->module=ConstantString("VIFF");
801   (void) RegisterMagickInfo(entry);
802   entry=SetMagickInfo("XV");
803   entry->decoder=(DecodeImageHandler *) ReadVIFFImage;
804   entry->encoder=(EncodeImageHandler *) WriteVIFFImage;
805   entry->description=ConstantString("Khoros Visualization image");
806   entry->module=ConstantString("VIFF");
807   (void) RegisterMagickInfo(entry);
808   return(MagickImageCoderSignature);
809 }
810 \f
811 /*
812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813 %                                                                             %
814 %                                                                             %
815 %                                                                             %
816 %   U n r e g i s t e r V I F F I m a g e                                     %
817 %                                                                             %
818 %                                                                             %
819 %                                                                             %
820 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821 %
822 %  UnregisterVIFFImage() removes format registrations made by the
823 %  VIFF module from the list of supported formats.
824 %
825 %  The format of the UnregisterVIFFImage method is:
826 %
827 %      UnregisterVIFFImage(void)
828 %
829 */
830 ModuleExport void UnregisterVIFFImage(void)
831 {
832   (void) UnregisterMagickInfo("VIFF");
833   (void) UnregisterMagickInfo("XV");
834 }
835 \f
836 /*
837 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
838 %                                                                             %
839 %                                                                             %
840 %                                                                             %
841 %   W r i t e V I F F I m a g e                                               %
842 %                                                                             %
843 %                                                                             %
844 %                                                                             %
845 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
846 %
847 %  WriteVIFFImage() writes an image to a file in the VIFF image format.
848 %
849 %  The format of the WriteVIFFImage method is:
850 %
851 %      MagickBooleanType WriteVIFFImage(const ImageInfo *image_info,
852 %        Image *image,ExceptionInfo *exception)
853 %
854 %  A description of each parameter follows.
855 %
856 %    o image_info: the image info.
857 %
858 %    o image:  The image.
859 %
860 %    o exception: return any errors or warnings in this structure.
861 %
862 */
863
864 static inline size_t MagickMin(const size_t x,const size_t y)
865 {
866   if (x < y)
867     return(x);
868   return(y);
869 }
870
871 static MagickBooleanType WriteVIFFImage(const ImageInfo *image_info,
872   Image *image,ExceptionInfo *exception)
873 {
874 #define VFF_CM_genericRGB  15
875 #define VFF_CM_NONE  0
876 #define VFF_DEP_IEEEORDER  0x2
877 #define VFF_DES_RAW  0
878 #define VFF_LOC_IMPLICIT  1
879 #define VFF_MAPTYP_NONE  0
880 #define VFF_MAPTYP_1_BYTE  1
881 #define VFF_MS_NONE  0
882 #define VFF_MS_ONEPERBAND  1
883 #define VFF_TYP_BIT  0
884 #define VFF_TYP_1_BYTE  1
885
886   typedef struct _ViffInfo
887   {
888     char
889       identifier,
890       file_type,
891       release,
892       version,
893       machine_dependency,
894       reserve[3],
895       comment[512];
896
897     size_t
898       rows,
899       columns,
900       subrows;
901
902     int
903       x_offset,
904       y_offset;
905
906     unsigned int
907       x_bits_per_pixel,
908       y_bits_per_pixel,
909       location_type,
910       location_dimension,
911       number_of_images,
912       number_data_bands,
913       data_storage_type,
914       data_encode_scheme,
915       map_scheme,
916       map_storage_type,
917       map_rows,
918       map_columns,
919       map_subrows,
920       map_enable,
921       maps_per_cycle,
922       color_space_model;
923   } ViffInfo;
924
925   const char
926     *value;
927
928   MagickBooleanType
929     status;
930
931   MagickOffsetType
932     scene;
933
934   MagickSizeType
935     number_pixels,
936     packets;
937
938   MemoryInfo
939     *pixel_info;
940
941   register const Quantum
942     *p;
943
944   register ssize_t
945     x;
946
947   register ssize_t
948     i;
949
950   register unsigned char
951     *q;
952
953   ssize_t
954     y;
955
956   unsigned char
957     buffer[8],
958     *pixels;
959
960   ViffInfo
961     viff_info;
962
963   /*
964     Open output image file.
965   */
966   assert(image_info != (const ImageInfo *) NULL);
967   assert(image_info->signature == MagickSignature);
968   assert(image != (Image *) NULL);
969   assert(image->signature == MagickSignature);
970   if (image->debug != MagickFalse)
971     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
972   assert(exception != (ExceptionInfo *) NULL);
973   assert(exception->signature == MagickSignature);
974   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
975   if (status == MagickFalse)
976     return(status);
977   (void) ResetMagickMemory(&viff_info,0,sizeof(ViffInfo));
978   scene=0;
979   do
980   {
981     /*
982       Initialize VIFF image structure.
983     */
984     (void) TransformImageColorspace(image,sRGBColorspace,exception);
985 DisableMSCWarning(4310)
986     viff_info.identifier=(char) 0xab;
987 RestoreMSCWarning
988     viff_info.file_type=1;
989     viff_info.release=1;
990     viff_info.version=3;
991     viff_info.machine_dependency=VFF_DEP_IEEEORDER;  /* IEEE byte ordering */
992     *viff_info.comment='\0';
993     value=GetImageProperty(image,"comment",exception);
994     if (value != (const char *) NULL)
995       (void) CopyMagickString(viff_info.comment,value,MagickMin(strlen(value),
996         511)+1);
997     viff_info.rows=image->columns;
998     viff_info.columns=image->rows;
999     viff_info.subrows=0;
1000     viff_info.x_offset=(~0);
1001     viff_info.y_offset=(~0);
1002     viff_info.x_bits_per_pixel=0;
1003     viff_info.y_bits_per_pixel=0;
1004     viff_info.location_type=VFF_LOC_IMPLICIT;
1005     viff_info.location_dimension=0;
1006     viff_info.number_of_images=1;
1007     viff_info.data_encode_scheme=VFF_DES_RAW;
1008     viff_info.map_scheme=VFF_MS_NONE;
1009     viff_info.map_storage_type=VFF_MAPTYP_NONE;
1010     viff_info.map_rows=0;
1011     viff_info.map_columns=0;
1012     viff_info.map_subrows=0;
1013     viff_info.map_enable=1;  /* no colormap */
1014     viff_info.maps_per_cycle=0;
1015     number_pixels=(MagickSizeType) image->columns*image->rows;
1016     if (image->storage_class == DirectClass)
1017       {
1018         /*
1019           Full color VIFF raster.
1020         */
1021         viff_info.number_data_bands=image->alpha_trait ? 4UL : 3UL;
1022         viff_info.color_space_model=VFF_CM_genericRGB;
1023         viff_info.data_storage_type=VFF_TYP_1_BYTE;
1024         packets=viff_info.number_data_bands*number_pixels;
1025       }
1026     else
1027       {
1028         viff_info.number_data_bands=1;
1029         viff_info.color_space_model=VFF_CM_NONE;
1030         viff_info.data_storage_type=VFF_TYP_1_BYTE;
1031         packets=number_pixels;
1032         if (IsImageGray(image,exception) == MagickFalse)
1033           {
1034             /*
1035               Colormapped VIFF raster.
1036             */
1037             viff_info.map_scheme=VFF_MS_ONEPERBAND;
1038             viff_info.map_storage_type=VFF_MAPTYP_1_BYTE;
1039             viff_info.map_rows=3;
1040             viff_info.map_columns=(unsigned int) image->colors;
1041           }
1042         else
1043           if (image->colors <= 2)
1044             {
1045               /*
1046                 Monochrome VIFF raster.
1047               */
1048               viff_info.data_storage_type=VFF_TYP_BIT;
1049               packets=((image->columns+7) >> 3)*image->rows;
1050             }
1051       }
1052     /*
1053       Write VIFF image header (pad to 1024 bytes).
1054     */
1055     buffer[0]=(unsigned char) viff_info.identifier;
1056     buffer[1]=(unsigned char) viff_info.file_type;
1057     buffer[2]=(unsigned char) viff_info.release;
1058     buffer[3]=(unsigned char) viff_info.version;
1059     buffer[4]=(unsigned char) viff_info.machine_dependency;
1060     buffer[5]=(unsigned char) viff_info.reserve[0];
1061     buffer[6]=(unsigned char) viff_info.reserve[1];
1062     buffer[7]=(unsigned char) viff_info.reserve[2];
1063     (void) WriteBlob(image,8,buffer);
1064     (void) WriteBlob(image,512,(unsigned char *) viff_info.comment);
1065     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.rows);
1066     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.columns);
1067     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.subrows);
1068     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.x_offset);
1069     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.y_offset);
1070     viff_info.x_bits_per_pixel=1U*(63 << 24) | (128 << 16);
1071     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.x_bits_per_pixel);
1072     viff_info.y_bits_per_pixel=1U*(63 << 24) | (128 << 16);
1073     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.y_bits_per_pixel);
1074     (void) WriteBlobMSBLong(image,viff_info.location_type);
1075     (void) WriteBlobMSBLong(image,viff_info.location_dimension);
1076     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.number_of_images);
1077     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.number_data_bands);
1078     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.data_storage_type);
1079     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.data_encode_scheme);
1080     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.map_scheme);
1081     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.map_storage_type);
1082     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.map_rows);
1083     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.map_columns);
1084     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.map_subrows);
1085     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.map_enable);
1086     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.maps_per_cycle);
1087     (void) WriteBlobMSBLong(image,(unsigned int) viff_info.color_space_model);
1088     for (i=0; i < 420; i++)
1089       (void) WriteBlobByte(image,'\0');
1090     /*
1091       Convert MIFF to VIFF raster pixels.
1092     */
1093     pixel_info=AcquireVirtualMemory((size_t) packets,sizeof(*pixels));
1094     if (pixel_info == (MemoryInfo *) NULL)
1095       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1096     pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
1097     q=pixels;
1098     if (image->storage_class == DirectClass)
1099       {
1100         /*
1101           Convert DirectClass packet to VIFF RGB pixel.
1102         */
1103         number_pixels=(MagickSizeType) image->columns*image->rows;
1104         for (y=0; y < (ssize_t) image->rows; y++)
1105         {
1106           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1107           if (p == (const Quantum *) NULL)
1108             break;
1109           for (x=0; x < (ssize_t) image->columns; x++)
1110           {
1111             *q=ScaleQuantumToChar(GetPixelRed(image,p));
1112             *(q+number_pixels)=ScaleQuantumToChar(GetPixelGreen(image,p));
1113             *(q+number_pixels*2)=ScaleQuantumToChar(GetPixelBlue(image,p));
1114             if (image->alpha_trait == BlendPixelTrait)
1115               *(q+number_pixels*3)=ScaleQuantumToChar((Quantum)
1116                 (GetPixelAlpha(image,p)));
1117             p+=GetPixelChannels(image);
1118             q++;
1119           }
1120           if (image->previous == (Image *) NULL)
1121             {
1122               status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1123                 image->rows);
1124               if (status == MagickFalse)
1125                 break;
1126             }
1127         }
1128       }
1129     else
1130       if (IsImageGray(image,exception) == MagickFalse)
1131         {
1132           unsigned char
1133             *viff_colormap;
1134
1135           /*
1136             Dump colormap to file.
1137           */
1138           viff_colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
1139             3*sizeof(*viff_colormap));
1140           if (viff_colormap == (unsigned char *) NULL)
1141             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1142           q=viff_colormap;
1143           for (i=0; i < (ssize_t) image->colors; i++)
1144             *q++=ScaleQuantumToChar(image->colormap[i].red);
1145           for (i=0; i < (ssize_t) image->colors; i++)
1146             *q++=ScaleQuantumToChar(image->colormap[i].green);
1147           for (i=0; i < (ssize_t) image->colors; i++)
1148             *q++=ScaleQuantumToChar(image->colormap[i].blue);
1149           (void) WriteBlob(image,3*image->colors,viff_colormap);
1150           viff_colormap=(unsigned char *) RelinquishMagickMemory(viff_colormap);
1151           /*
1152             Convert PseudoClass packet to VIFF colormapped pixels.
1153           */
1154           q=pixels;
1155           for (y=0; y < (ssize_t) image->rows; y++)
1156           {
1157             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1158             if (p == (const Quantum *) NULL)
1159               break;
1160             for (x=0; x < (ssize_t) image->columns; x++)
1161             {
1162               *q++=(unsigned char) GetPixelIndex(image,p);
1163               p+=GetPixelChannels(image);
1164             }
1165             if (image->previous == (Image *) NULL)
1166               {
1167                 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1168                 image->rows);
1169                 if (status == MagickFalse)
1170                   break;
1171               }
1172           }
1173         }
1174       else
1175         if (image->colors <= 2)
1176           {
1177             ssize_t
1178               x,
1179               y;
1180
1181             register unsigned char
1182               bit,
1183               byte;
1184
1185             /*
1186               Convert PseudoClass image to a VIFF monochrome image.
1187             */
1188             (void) SetImageType(image,BilevelType,exception);
1189             for (y=0; y < (ssize_t) image->rows; y++)
1190             {
1191               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1192               if (p == (const Quantum *) NULL)
1193                 break;
1194               bit=0;
1195               byte=0;
1196               for (x=0; x < (ssize_t) image->columns; x++)
1197               {
1198                 byte>>=1;
1199                 if (GetPixelLuma(image,p) < (QuantumRange/2.0))
1200                   byte|=0x80;
1201                 bit++;
1202                 if (bit == 8)
1203                   {
1204                     *q++=byte;
1205                     bit=0;
1206                     byte=0;
1207                   }
1208                 p+=GetPixelChannels(image);
1209               }
1210               if (bit != 0)
1211                 *q++=byte >> (8-bit);
1212               if (image->previous == (Image *) NULL)
1213                 {
1214                   status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1215                     y,image->rows);
1216                   if (status == MagickFalse)
1217                     break;
1218                 }
1219             }
1220           }
1221         else
1222           {
1223             /*
1224               Convert PseudoClass packet to VIFF grayscale pixel.
1225             */
1226             for (y=0; y < (ssize_t) image->rows; y++)
1227             {
1228               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1229               if (p == (const Quantum *) NULL)
1230                 break;
1231               for (x=0; x < (ssize_t) image->columns; x++)
1232               {
1233                 *q++=(unsigned char) ClampToQuantum(GetPixelLuma(image,p));
1234                 p+=GetPixelChannels(image);
1235               }
1236               if (image->previous == (Image *) NULL)
1237                 {
1238                   status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1239                     y,image->rows);
1240                   if (status == MagickFalse)
1241                     break;
1242                 }
1243             }
1244           }
1245     (void) WriteBlob(image,(size_t) packets,pixels);
1246     pixel_info=RelinquishVirtualMemory(pixel_info);
1247     if (GetNextImageInList(image) == (Image *) NULL)
1248       break;
1249     image=SyncNextImageInList(image);
1250     status=SetImageProgress(image,SaveImagesTag,scene++,
1251       GetImageListLength(image));
1252     if (status == MagickFalse)
1253       break;
1254   } while (image_info->adjoin != MagickFalse);
1255   (void) CloseBlob(image);
1256   return(MagickTrue);
1257 }