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