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