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