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*/
73static 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*/
101static 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*/
141static 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*/
795ModuleExport 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*/
831ModuleExport 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*/
864static 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);
977DisableMSCWarning(4310)
978    viff_info.identifier=(char) 0xab;
979RestoreMSCWarning
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