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