viff.c revision 22e1306dc54f838231b30fa22b032c7a4a1d85ca
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=SetMagickInfo("VIFF");
787  entry->decoder=(DecodeImageHandler *) ReadVIFFImage;
788  entry->encoder=(EncodeImageHandler *) WriteVIFFImage;
789  entry->magick=(IsImageFormatHandler *) IsVIFF;
790  entry->description=ConstantString("Khoros Visualization image");
791  entry->module=ConstantString("VIFF");
792  (void) RegisterMagickInfo(entry);
793  entry=SetMagickInfo("XV");
794  entry->decoder=(DecodeImageHandler *) ReadVIFFImage;
795  entry->encoder=(EncodeImageHandler *) WriteVIFFImage;
796  entry->description=ConstantString("Khoros Visualization image");
797  entry->module=ConstantString("VIFF");
798  (void) RegisterMagickInfo(entry);
799  return(MagickImageCoderSignature);
800}
801
802/*
803%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
804%                                                                             %
805%                                                                             %
806%                                                                             %
807%   U n r e g i s t e r V I F F I m a g e                                     %
808%                                                                             %
809%                                                                             %
810%                                                                             %
811%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
812%
813%  UnregisterVIFFImage() removes format registrations made by the
814%  VIFF module from the list of supported formats.
815%
816%  The format of the UnregisterVIFFImage method is:
817%
818%      UnregisterVIFFImage(void)
819%
820*/
821ModuleExport void UnregisterVIFFImage(void)
822{
823  (void) UnregisterMagickInfo("VIFF");
824  (void) UnregisterMagickInfo("XV");
825}
826
827/*
828%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
829%                                                                             %
830%                                                                             %
831%                                                                             %
832%   W r i t e V I F F I m a g e                                               %
833%                                                                             %
834%                                                                             %
835%                                                                             %
836%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
837%
838%  WriteVIFFImage() writes an image to a file in the VIFF image format.
839%
840%  The format of the WriteVIFFImage method is:
841%
842%      MagickBooleanType WriteVIFFImage(const ImageInfo *image_info,
843%        Image *image,ExceptionInfo *exception)
844%
845%  A description of each parameter follows.
846%
847%    o image_info: the image info.
848%
849%    o image:  The image.
850%
851%    o exception: return any errors or warnings in this structure.
852%
853*/
854static MagickBooleanType WriteVIFFImage(const ImageInfo *image_info,
855  Image *image,ExceptionInfo *exception)
856{
857#define VFF_CM_genericRGB  15
858#define VFF_CM_NONE  0
859#define VFF_DEP_IEEEORDER  0x2
860#define VFF_DES_RAW  0
861#define VFF_LOC_IMPLICIT  1
862#define VFF_MAPTYP_NONE  0
863#define VFF_MAPTYP_1_BYTE  1
864#define VFF_MS_NONE  0
865#define VFF_MS_ONEPERBAND  1
866#define VFF_TYP_BIT  0
867#define VFF_TYP_1_BYTE  1
868
869  typedef struct _ViffInfo
870  {
871    char
872      identifier,
873      file_type,
874      release,
875      version,
876      machine_dependency,
877      reserve[3],
878      comment[512];
879
880    size_t
881      rows,
882      columns,
883      subrows;
884
885    int
886      x_offset,
887      y_offset;
888
889    unsigned int
890      x_bits_per_pixel,
891      y_bits_per_pixel,
892      location_type,
893      location_dimension,
894      number_of_images,
895      number_data_bands,
896      data_storage_type,
897      data_encode_scheme,
898      map_scheme,
899      map_storage_type,
900      map_rows,
901      map_columns,
902      map_subrows,
903      map_enable,
904      maps_per_cycle,
905      color_space_model;
906  } ViffInfo;
907
908  const char
909    *value;
910
911  MagickBooleanType
912    status;
913
914  MagickOffsetType
915    scene;
916
917  MagickSizeType
918    number_pixels,
919    packets;
920
921  MemoryInfo
922    *pixel_info;
923
924  register const Quantum
925    *p;
926
927  register ssize_t
928    x;
929
930  register ssize_t
931    i;
932
933  register unsigned char
934    *q;
935
936  ssize_t
937    y;
938
939  unsigned char
940    *pixels;
941
942  ViffInfo
943    viff_info;
944
945  /*
946    Open output image file.
947  */
948  assert(image_info != (const ImageInfo *) NULL);
949  assert(image_info->signature == MagickSignature);
950  assert(image != (Image *) NULL);
951  assert(image->signature == MagickSignature);
952  if (image->debug != MagickFalse)
953    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
954  assert(exception != (ExceptionInfo *) NULL);
955  assert(exception->signature == MagickSignature);
956  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
957  if (status == MagickFalse)
958    return(status);
959  (void) ResetMagickMemory(&viff_info,0,sizeof(ViffInfo));
960  scene=0;
961  do
962  {
963    /*
964      Initialize VIFF image structure.
965    */
966    (void) TransformImageColorspace(image,sRGBColorspace,exception);
967DisableMSCWarning(4310)
968    viff_info.identifier=(char) 0xab;
969RestoreMSCWarning
970    viff_info.file_type=1;
971    viff_info.release=1;
972    viff_info.version=3;
973    viff_info.machine_dependency=VFF_DEP_IEEEORDER;  /* IEEE byte ordering */
974    *viff_info.comment='\0';
975    value=GetImageProperty(image,"comment",exception);
976    if (value != (const char *) NULL)
977      (void) CopyMagickString(viff_info.comment,value,MagickMin(strlen(value),
978        511)+1);
979    viff_info.rows=image->columns;
980    viff_info.columns=image->rows;
981    viff_info.subrows=0;
982    viff_info.x_offset=(~0);
983    viff_info.y_offset=(~0);
984    viff_info.x_bits_per_pixel=0;
985    viff_info.y_bits_per_pixel=0;
986    viff_info.location_type=VFF_LOC_IMPLICIT;
987    viff_info.location_dimension=0;
988    viff_info.number_of_images=1;
989    viff_info.data_encode_scheme=VFF_DES_RAW;
990    viff_info.map_scheme=VFF_MS_NONE;
991    viff_info.map_storage_type=VFF_MAPTYP_NONE;
992    viff_info.map_rows=0;
993    viff_info.map_columns=0;
994    viff_info.map_subrows=0;
995    viff_info.map_enable=1;  /* no colormap */
996    viff_info.maps_per_cycle=0;
997    number_pixels=(MagickSizeType) image->columns*image->rows;
998    if (image->storage_class == DirectClass)
999      {
1000        /*
1001          Full color VIFF raster.
1002        */
1003        viff_info.number_data_bands=image->alpha_trait ? 4U : 3U;
1004        viff_info.color_space_model=VFF_CM_genericRGB;
1005        viff_info.data_storage_type=VFF_TYP_1_BYTE;
1006        packets=viff_info.number_data_bands*number_pixels;
1007      }
1008    else
1009      {
1010        viff_info.number_data_bands=1;
1011        viff_info.color_space_model=VFF_CM_NONE;
1012        viff_info.data_storage_type=VFF_TYP_1_BYTE;
1013        packets=number_pixels;
1014        if (IsImageGray(image,exception) == MagickFalse)
1015          {
1016            /*
1017              Colormapped VIFF raster.
1018            */
1019            viff_info.map_scheme=VFF_MS_ONEPERBAND;
1020            viff_info.map_storage_type=VFF_MAPTYP_1_BYTE;
1021            viff_info.map_rows=3;
1022            viff_info.map_columns=(unsigned int) image->colors;
1023          }
1024        else
1025          if (image->colors <= 2)
1026            {
1027              /*
1028                Monochrome VIFF raster.
1029              */
1030              viff_info.data_storage_type=VFF_TYP_BIT;
1031              packets=((image->columns+7) >> 3)*image->rows;
1032            }
1033      }
1034    /*
1035      Write VIFF image header (pad to 1024 bytes).
1036    */
1037    (void) WriteBlob(image,sizeof(viff_info.identifier),(unsigned char *)
1038      &viff_info.identifier);
1039    (void) WriteBlob(image,sizeof(viff_info.file_type),(unsigned char *)
1040      &viff_info.file_type);
1041    (void) WriteBlob(image,sizeof(viff_info.release),(unsigned char *)
1042      &viff_info.release);
1043    (void) WriteBlob(image,sizeof(viff_info.version),(unsigned char *)
1044      &viff_info.version);
1045    (void) WriteBlob(image,sizeof(viff_info.machine_dependency),
1046      (unsigned char *) &viff_info.machine_dependency);
1047    (void) WriteBlob(image,sizeof(viff_info.reserve),(unsigned char *)
1048      viff_info.reserve);
1049    (void) WriteBlob(image,512,(unsigned char *) viff_info.comment);
1050    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.rows);
1051    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.columns);
1052    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.subrows);
1053    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.x_offset);
1054    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.y_offset);
1055    viff_info.x_bits_per_pixel=(unsigned int) ((63 << 24) | (128 << 16));
1056    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.x_bits_per_pixel);
1057    viff_info.y_bits_per_pixel=(unsigned int) ((63 << 24) | (128 << 16));
1058    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.y_bits_per_pixel);
1059    (void) WriteBlobMSBLong(image,viff_info.location_type);
1060    (void) WriteBlobMSBLong(image,viff_info.location_dimension);
1061    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.number_of_images);
1062    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.number_data_bands);
1063    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.data_storage_type);
1064    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.data_encode_scheme);
1065    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.map_scheme);
1066    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.map_storage_type);
1067    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.map_rows);
1068    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.map_columns);
1069    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.map_subrows);
1070    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.map_enable);
1071    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.maps_per_cycle);
1072    (void) WriteBlobMSBLong(image,(unsigned int) viff_info.color_space_model);
1073    for (i=0; i < 420; i++)
1074      (void) WriteBlobByte(image,'\0');
1075    /*
1076      Convert MIFF to VIFF raster pixels.
1077    */
1078    pixel_info=AcquireVirtualMemory((size_t) packets,sizeof(*pixels));
1079    if (pixel_info == (MemoryInfo *) NULL)
1080      ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1081    pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
1082    q=pixels;
1083    if (image->storage_class == DirectClass)
1084      {
1085        /*
1086          Convert DirectClass packet to VIFF RGB pixel.
1087        */
1088        number_pixels=(MagickSizeType) image->columns*image->rows;
1089        for (y=0; y < (ssize_t) image->rows; y++)
1090        {
1091          p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1092          if (p == (const Quantum *) NULL)
1093            break;
1094          for (x=0; x < (ssize_t) image->columns; x++)
1095          {
1096            *q=ScaleQuantumToChar(GetPixelRed(image,p));
1097            *(q+number_pixels)=ScaleQuantumToChar(GetPixelGreen(image,p));
1098            *(q+number_pixels*2)=ScaleQuantumToChar(GetPixelBlue(image,p));
1099            if (image->alpha_trait != UndefinedPixelTrait)
1100              *(q+number_pixels*3)=ScaleQuantumToChar((Quantum)
1101                (GetPixelAlpha(image,p)));
1102            p+=GetPixelChannels(image);
1103            q++;
1104          }
1105          if (image->previous == (Image *) NULL)
1106            {
1107              status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1108                image->rows);
1109              if (status == MagickFalse)
1110                break;
1111            }
1112        }
1113      }
1114    else
1115      if (IsImageGray(image,exception) == MagickFalse)
1116        {
1117          unsigned char
1118            *viff_colormap;
1119
1120          /*
1121            Dump colormap to file.
1122          */
1123          viff_colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
1124            3*sizeof(*viff_colormap));
1125          if (viff_colormap == (unsigned char *) NULL)
1126            ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1127          q=viff_colormap;
1128          for (i=0; i < (ssize_t) image->colors; i++)
1129            *q++=ScaleQuantumToChar(image->colormap[i].red);
1130          for (i=0; i < (ssize_t) image->colors; i++)
1131            *q++=ScaleQuantumToChar(image->colormap[i].green);
1132          for (i=0; i < (ssize_t) image->colors; i++)
1133            *q++=ScaleQuantumToChar(image->colormap[i].blue);
1134          (void) WriteBlob(image,3*image->colors,viff_colormap);
1135          viff_colormap=(unsigned char *) RelinquishMagickMemory(viff_colormap);
1136          /*
1137            Convert PseudoClass packet to VIFF colormapped pixels.
1138          */
1139          q=pixels;
1140          for (y=0; y < (ssize_t) image->rows; y++)
1141          {
1142            p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1143            if (p == (const Quantum *) NULL)
1144              break;
1145            for (x=0; x < (ssize_t) image->columns; x++)
1146            {
1147              *q++=(unsigned char) GetPixelIndex(image,p);
1148              p+=GetPixelChannels(image);
1149            }
1150            if (image->previous == (Image *) NULL)
1151              {
1152                status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1153                image->rows);
1154                if (status == MagickFalse)
1155                  break;
1156              }
1157          }
1158        }
1159      else
1160        if (image->colors <= 2)
1161          {
1162            ssize_t
1163              x,
1164              y;
1165
1166            register unsigned char
1167              bit,
1168              byte;
1169
1170            /*
1171              Convert PseudoClass image to a VIFF monochrome image.
1172            */
1173            (void) SetImageType(image,BilevelType,exception);
1174            for (y=0; y < (ssize_t) image->rows; y++)
1175            {
1176              p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1177              if (p == (const Quantum *) NULL)
1178                break;
1179              bit=0;
1180              byte=0;
1181              for (x=0; x < (ssize_t) image->columns; x++)
1182              {
1183                byte>>=1;
1184                if (GetPixelLuma(image,p) < (QuantumRange/2.0))
1185                  byte|=0x80;
1186                bit++;
1187                if (bit == 8)
1188                  {
1189                    *q++=byte;
1190                    bit=0;
1191                    byte=0;
1192                  }
1193                p+=GetPixelChannels(image);
1194              }
1195              if (bit != 0)
1196                *q++=byte >> (8-bit);
1197              if (image->previous == (Image *) NULL)
1198                {
1199                  status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1200                    y,image->rows);
1201                  if (status == MagickFalse)
1202                    break;
1203                }
1204            }
1205          }
1206        else
1207          {
1208            /*
1209              Convert PseudoClass packet to VIFF grayscale pixel.
1210            */
1211            for (y=0; y < (ssize_t) image->rows; y++)
1212            {
1213              p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1214              if (p == (const Quantum *) NULL)
1215                break;
1216              for (x=0; x < (ssize_t) image->columns; x++)
1217              {
1218                *q++=(unsigned char) ClampToQuantum(GetPixelLuma(image,p));
1219                p+=GetPixelChannels(image);
1220              }
1221              if (image->previous == (Image *) NULL)
1222                {
1223                  status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1224                    y,image->rows);
1225                  if (status == MagickFalse)
1226                    break;
1227                }
1228            }
1229          }
1230    (void) WriteBlob(image,(size_t) packets,pixels);
1231    pixel_info=RelinquishVirtualMemory(pixel_info);
1232    if (GetNextImageInList(image) == (Image *) NULL)
1233      break;
1234    image=SyncNextImageInList(image);
1235    status=SetImageProgress(image,SaveImagesTag,scene++,
1236      GetImageListLength(image));
1237    if (status == MagickFalse)
1238      break;
1239  } while (image_info->adjoin != MagickFalse);
1240  (void) CloseBlob(image);
1241  return(MagickTrue);
1242}
1243