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