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