sgi.c revision 7d6d3d842e19316bdf35c69a032cd77a40f476aa
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                            SSSSS   GGGG  IIIII                              %
7%                            SS     G        I                                %
8%                             SSS   G  GG    I                                %
9%                               SS  G   G    I                                %
10%                            SSSSS   GGG   IIIII                              %
11%                                                                             %
12%                                                                             %
13%                      Read/Write Irix RGB Image Format                       %
14%                                                                             %
15%                              Software Design                                %
16%                                   Cristy                                    %
17%                                 July 1992                                   %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colormap.h"
50#include "MagickCore/colorspace.h"
51#include "MagickCore/colorspace-private.h"
52#include "MagickCore/exception.h"
53#include "MagickCore/exception-private.h"
54#include "MagickCore/image.h"
55#include "MagickCore/image-private.h"
56#include "MagickCore/list.h"
57#include "MagickCore/magick.h"
58#include "MagickCore/memory_.h"
59#include "MagickCore/monitor.h"
60#include "MagickCore/monitor-private.h"
61#include "MagickCore/pixel-accessor.h"
62#include "MagickCore/property.h"
63#include "MagickCore/quantum-private.h"
64#include "MagickCore/static.h"
65#include "MagickCore/string_.h"
66#include "MagickCore/module.h"
67
68/*
69  Typedef declaractions.
70*/
71typedef struct _SGIInfo
72{
73  unsigned short
74    magic;
75
76  unsigned char
77    storage,
78    bytes_per_pixel;
79
80  unsigned short
81    dimension,
82    columns,
83    rows,
84    depth;
85
86  size_t
87    minimum_value,
88    maximum_value,
89    sans;
90
91  char
92    name[80];
93
94  size_t
95    pixel_format;
96
97  unsigned char
98    filler[404];
99} SGIInfo;
100
101/*
102  Forward declarations.
103*/
104static MagickBooleanType
105  WriteSGIImage(const ImageInfo *,Image *,ExceptionInfo *);
106/*
107%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108%                                                                             %
109%                                                                             %
110%                                                                             %
111%   I s S G I                                                                 %
112%                                                                             %
113%                                                                             %
114%                                                                             %
115%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116%
117%  IsSGI() returns MagickTrue if the image format type, identified by the
118%  magick string, is SGI.
119%
120%  The format of the IsSGI method is:
121%
122%      MagickBooleanType IsSGI(const unsigned char *magick,const size_t length)
123%
124%  A description of each parameter follows:
125%
126%    o magick: compare image format pattern against these bytes.
127%
128%    o length: Specifies the length of the magick string.
129%
130*/
131static MagickBooleanType IsSGI(const unsigned char *magick,const size_t length)
132{
133  if (length < 2)
134    return(MagickFalse);
135  if (memcmp(magick,"\001\332",2) == 0)
136    return(MagickTrue);
137  return(MagickFalse);
138}
139
140/*
141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142%                                                                             %
143%                                                                             %
144%                                                                             %
145%   R e a d S G I I m a g e                                                   %
146%                                                                             %
147%                                                                             %
148%                                                                             %
149%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
150%
151%  ReadSGIImage() reads a SGI RGB image file and returns it.  It
152%  allocates the memory necessary for the new Image structure and returns a
153%  pointer to the new image.
154%
155%  The format of the ReadSGIImage method is:
156%
157%      Image *ReadSGIImage(const ImageInfo *image_info,ExceptionInfo *exception)
158%
159%  A description of each parameter follows:
160%
161%    o image_info: the image info.
162%
163%    o exception: return any errors or warnings in this structure.
164%
165*/
166
167static inline size_t MagickMin(const size_t x,const size_t y)
168{
169  if (x < y)
170    return(x);
171  return(y);
172}
173
174static MagickBooleanType SGIDecode(const size_t bytes_per_pixel,
175  ssize_t number_packets,unsigned char *packets,ssize_t number_pixels,
176  unsigned char *pixels)
177{
178  register unsigned char
179    *p,
180    *q;
181
182  size_t
183    pixel;
184
185  ssize_t
186    count;
187
188  p=packets;
189  q=pixels;
190  if (bytes_per_pixel == 2)
191    {
192      for ( ; number_pixels > 0; )
193      {
194        if (number_packets-- == 0)
195          return(MagickFalse);
196        pixel=(size_t) (*p++) << 8;
197        pixel|=(*p++);
198        count=(ssize_t) (pixel & 0x7f);
199        if (count == 0)
200          break;
201        if (count > (ssize_t) number_pixels)
202          return(MagickFalse);
203        number_pixels-=count;
204        if ((pixel & 0x80) != 0)
205          for ( ; count != 0; count--)
206          {
207            if (number_packets-- == 0)
208              return(MagickFalse);
209            *q=(*p++);
210            *(q+1)=(*p++);
211            q+=8;
212          }
213        else
214          {
215            pixel=(size_t) (*p++) << 8;
216            pixel|=(*p++);
217            for ( ; count != 0; count--)
218            {
219              if (number_packets-- == 0)
220                return(MagickFalse);
221              *q=(unsigned char) (pixel >> 8);
222              *(q+1)=(unsigned char) pixel;
223              q+=8;
224            }
225          }
226      }
227      return(MagickTrue);
228    }
229  for ( ; number_pixels > 0; )
230  {
231    if (number_packets-- == 0)
232      return(MagickFalse);
233    pixel=(size_t) (*p++);
234    count=(ssize_t) (pixel & 0x7f);
235    if (count == 0)
236      break;
237    if (count > (ssize_t) number_pixels)
238      return(MagickFalse);
239    number_pixels-=count;
240    if ((pixel & 0x80) != 0)
241      for ( ; count != 0; count--)
242      {
243        if (number_packets-- == 0)
244          return(MagickFalse);
245        *q=(*p++);
246        q+=4;
247      }
248    else
249      {
250        if (number_packets-- == 0)
251          return(MagickFalse);
252        pixel=(size_t) (*p++);
253        for ( ; count != 0; count--)
254        {
255          *q=(unsigned char) pixel;
256          q+=4;
257        }
258      }
259  }
260  return(MagickTrue);
261}
262
263static Image *ReadSGIImage(const ImageInfo *image_info,ExceptionInfo *exception)
264{
265  Image
266    *image;
267
268  MagickBooleanType
269    status;
270
271  MagickSizeType
272    number_pixels;
273
274  MemoryInfo
275    *pixel_info;
276
277  register Quantum
278    *q;
279
280  register ssize_t
281    i,
282    x;
283
284  register unsigned char
285    *p;
286
287  SGIInfo
288    iris_info;
289
290  size_t
291    bytes_per_pixel,
292    quantum;
293
294  ssize_t
295    count,
296    y,
297    z;
298
299  unsigned char
300    *pixels;
301
302  /*
303    Open image file.
304  */
305  assert(image_info != (const ImageInfo *) NULL);
306  assert(image_info->signature == MagickSignature);
307  if (image_info->debug != MagickFalse)
308    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
309      image_info->filename);
310  assert(exception != (ExceptionInfo *) NULL);
311  assert(exception->signature == MagickSignature);
312  image=AcquireImage(image_info,exception);
313  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
314  if (status == MagickFalse)
315    {
316      image=DestroyImageList(image);
317      return((Image *) NULL);
318    }
319  /*
320    Read SGI raster header.
321  */
322  iris_info.magic=ReadBlobMSBShort(image);
323  do
324  {
325    /*
326      Verify SGI identifier.
327    */
328    if (iris_info.magic != 0x01DA)
329      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
330    iris_info.storage=(unsigned char) ReadBlobByte(image);
331    switch (iris_info.storage)
332    {
333      case 0x00: image->compression=NoCompression; break;
334      case 0x01: image->compression=RLECompression; break;
335      default:
336        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
337    }
338    iris_info.bytes_per_pixel=(unsigned char) ReadBlobByte(image);
339    if ((iris_info.bytes_per_pixel == 0) || (iris_info.bytes_per_pixel > 2))
340      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
341    iris_info.dimension=ReadBlobMSBShort(image);
342    iris_info.columns=ReadBlobMSBShort(image);
343    iris_info.rows=ReadBlobMSBShort(image);
344    iris_info.depth=ReadBlobMSBShort(image);
345    if ((iris_info.depth == 0) || (iris_info.depth > 4))
346      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
347    iris_info.minimum_value=ReadBlobMSBLong(image);
348    iris_info.maximum_value=ReadBlobMSBLong(image);
349    iris_info.sans=ReadBlobMSBLong(image);
350    (void) ReadBlob(image,sizeof(iris_info.name),(unsigned char *)
351      iris_info.name);
352    iris_info.name[sizeof(iris_info.name)-1]='\0';
353    if (*iris_info.name != '\0')
354      (void) SetImageProperty(image,"label",iris_info.name,exception);
355    iris_info.pixel_format=ReadBlobMSBLong(image);
356    if (iris_info.pixel_format != 0)
357      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
358    count=ReadBlob(image,sizeof(iris_info.filler),iris_info.filler);
359    (void) count;
360    image->columns=iris_info.columns;
361    image->rows=iris_info.rows;
362    image->depth=(size_t) MagickMin(iris_info.depth,MAGICKCORE_QUANTUM_DEPTH);
363    if (iris_info.pixel_format == 0)
364      image->depth=(size_t) MagickMin((size_t) 8*
365        iris_info.bytes_per_pixel,MAGICKCORE_QUANTUM_DEPTH);
366    if (iris_info.depth < 3)
367      {
368        image->storage_class=PseudoClass;
369        image->colors=iris_info.bytes_per_pixel > 1 ? 65535 : 256;
370      }
371    if ((image_info->ping != MagickFalse)  && (image_info->number_scenes != 0))
372      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
373        break;
374    /*
375      Allocate SGI pixels.
376    */
377    bytes_per_pixel=(size_t) iris_info.bytes_per_pixel;
378    number_pixels=(MagickSizeType) iris_info.columns*iris_info.rows;
379    if ((4*bytes_per_pixel*number_pixels) != ((MagickSizeType) (size_t)
380        (4*bytes_per_pixel*number_pixels)))
381      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
382    pixel_info=AcquireVirtualMemory(iris_info.columns,iris_info.rows*4*
383      bytes_per_pixel*sizeof(*pixels));
384    if (pixel_info == (MemoryInfo *) NULL)
385      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
386    pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
387    if ((int) iris_info.storage != 0x01)
388      {
389        unsigned char
390          *scanline;
391
392        /*
393          Read standard image format.
394        */
395        scanline=(unsigned char *) AcquireQuantumMemory(iris_info.columns,
396          bytes_per_pixel*sizeof(*scanline));
397        if (scanline == (unsigned char *) NULL)
398          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
399        for (z=0; z < (ssize_t) iris_info.depth; z++)
400        {
401          p=pixels+bytes_per_pixel*z;
402          for (y=0; y < (ssize_t) iris_info.rows; y++)
403          {
404            count=ReadBlob(image,bytes_per_pixel*iris_info.columns,scanline);
405            if (EOFBlob(image) != MagickFalse)
406              break;
407            if (bytes_per_pixel == 2)
408              for (x=0; x < (ssize_t) iris_info.columns; x++)
409              {
410                *p=scanline[2*x];
411                *(p+1)=scanline[2*x+1];
412                p+=8;
413              }
414            else
415              for (x=0; x < (ssize_t) iris_info.columns; x++)
416              {
417                *p=scanline[x];
418                p+=4;
419              }
420          }
421        }
422        scanline=(unsigned char *) RelinquishMagickMemory(scanline);
423      }
424    else
425      {
426        MemoryInfo
427          *packet_info;
428
429        size_t
430          *runlength;
431
432        ssize_t
433          offset,
434          *offsets;
435
436        unsigned char
437          *packets;
438
439        unsigned int
440          data_order;
441
442        /*
443          Read runlength-encoded image format.
444        */
445        offsets=(ssize_t *) AcquireQuantumMemory((size_t) iris_info.rows,
446          iris_info.depth*sizeof(*offsets));
447        runlength=(size_t *) AcquireQuantumMemory(iris_info.rows,
448          iris_info.depth*sizeof(*runlength));
449        packet_info=AcquireVirtualMemory((size_t) iris_info.columns+10UL,4UL*
450          sizeof(*packets));
451        if ((offsets == (ssize_t *) NULL) ||
452            (runlength == (size_t *) NULL) ||
453            (packet_info == (MemoryInfo *) NULL))
454          {
455            if (offsets == (ssize_t *) NULL)
456              offsets=(ssize_t *) RelinquishMagickMemory(offsets);
457            if (runlength == (size_t *) NULL)
458              runlength=(size_t *) RelinquishMagickMemory(runlength);
459            if (packet_info == (MemoryInfo *) NULL)
460              packet_info=RelinquishVirtualMemory(packet_info);
461            ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
462          }
463        packets=(unsigned char *) GetVirtualMemoryBlob(packet_info);
464        for (i=0; i < (ssize_t) (iris_info.rows*iris_info.depth); i++)
465          offsets[i]=(int) ReadBlobMSBLong(image);
466        for (i=0; i < (ssize_t) (iris_info.rows*iris_info.depth); i++)
467        {
468          runlength[i]=ReadBlobMSBLong(image);
469          if (runlength[i] > (4*(size_t) iris_info.columns+10))
470            ThrowReaderException(CorruptImageError,"ImproperImageHeader");
471        }
472        /*
473          Check data order.
474        */
475        offset=0;
476        data_order=0;
477        for (y=0; ((y < (ssize_t) iris_info.rows) && (data_order == 0)); y++)
478          for (z=0; ((z < (ssize_t) iris_info.depth) && (data_order == 0)); z++)
479          {
480            if (offsets[y+z*iris_info.rows] < offset)
481              data_order=1;
482            offset=offsets[y+z*iris_info.rows];
483          }
484        offset=(ssize_t) TellBlob(image);
485        if (data_order == 1)
486          {
487            for (z=0; z < (ssize_t) iris_info.depth; z++)
488            {
489              p=pixels;
490              for (y=0; y < (ssize_t) iris_info.rows; y++)
491              {
492                if (offset != offsets[y+z*iris_info.rows])
493                  {
494                    offset=offsets[y+z*iris_info.rows];
495                    offset=(ssize_t) SeekBlob(image,(ssize_t) offset,SEEK_SET);
496                  }
497                count=ReadBlob(image,(size_t) runlength[y+z*iris_info.rows],
498                  packets);
499                if (EOFBlob(image) != MagickFalse)
500                  break;
501                offset+=(ssize_t) runlength[y+z*iris_info.rows];
502                status=SGIDecode(bytes_per_pixel,(ssize_t)
503                  (runlength[y+z*iris_info.rows]/bytes_per_pixel),packets,
504                  1L*iris_info.columns,p+bytes_per_pixel*z);
505                if (status == MagickFalse)
506                  ThrowReaderException(CorruptImageError,"ImproperImageHeader");
507                p+=(iris_info.columns*4*bytes_per_pixel);
508              }
509            }
510          }
511        else
512          {
513            MagickOffsetType
514              position;
515
516            position=TellBlob(image);
517            p=pixels;
518            for (y=0; y < (ssize_t) iris_info.rows; y++)
519            {
520              for (z=0; z < (ssize_t) iris_info.depth; z++)
521              {
522                if (offset != offsets[y+z*iris_info.rows])
523                  {
524                    offset=offsets[y+z*iris_info.rows];
525                    offset=(ssize_t) SeekBlob(image,(ssize_t) offset,SEEK_SET);
526                  }
527                count=ReadBlob(image,(size_t) runlength[y+z*iris_info.rows],
528                  packets);
529                if (EOFBlob(image) != MagickFalse)
530                  break;
531                offset+=(ssize_t) runlength[y+z*iris_info.rows];
532                status=SGIDecode(bytes_per_pixel,(ssize_t)
533                  (runlength[y+z*iris_info.rows]/bytes_per_pixel),packets,
534                  1L*iris_info.columns,p+bytes_per_pixel*z);
535                if (status == MagickFalse)
536                  ThrowReaderException(CorruptImageError,"ImproperImageHeader");
537              }
538              p+=(iris_info.columns*4*bytes_per_pixel);
539            }
540            offset=(ssize_t) SeekBlob(image,position,SEEK_SET);
541          }
542        packet_info=RelinquishVirtualMemory(packet_info);
543        runlength=(size_t *) RelinquishMagickMemory(runlength);
544        offsets=(ssize_t *) RelinquishMagickMemory(offsets);
545      }
546    /*
547      Initialize image structure.
548    */
549    image->alpha_trait=iris_info.depth == 4 ? BlendPixelTrait :
550      UndefinedPixelTrait;
551    image->columns=iris_info.columns;
552    image->rows=iris_info.rows;
553    /*
554      Convert SGI raster image to pixel packets.
555    */
556    if (image->storage_class == DirectClass)
557      {
558        /*
559          Convert SGI image to DirectClass pixel packets.
560        */
561        if (bytes_per_pixel == 2)
562          {
563            for (y=0; y < (ssize_t) image->rows; y++)
564            {
565              p=pixels+(image->rows-y-1)*8*image->columns;
566              q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
567              if (q == (Quantum *) NULL)
568                break;
569              for (x=0; x < (ssize_t) image->columns; x++)
570              {
571                SetPixelRed(image,ScaleShortToQuantum((unsigned short)
572                  ((*(p+0) << 8) | (*(p+1)))),q);
573                SetPixelGreen(image,ScaleShortToQuantum((unsigned short)
574                  ((*(p+2) << 8) | (*(p+3)))),q);
575                SetPixelBlue(image,ScaleShortToQuantum((unsigned short)
576                  ((*(p+4) << 8) | (*(p+5)))),q);
577                SetPixelAlpha(image,OpaqueAlpha,q);
578                if (image->alpha_trait != UndefinedPixelTrait)
579                  SetPixelAlpha(image,ScaleShortToQuantum((unsigned short)
580                    ((*(p+6) << 8) | (*(p+7)))),q);
581                p+=8;
582                q+=GetPixelChannels(image);
583              }
584              if (SyncAuthenticPixels(image,exception) == MagickFalse)
585                break;
586              if (image->previous == (Image *) NULL)
587                {
588                  status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
589                    y,image->rows);
590                  if (status == MagickFalse)
591                    break;
592                }
593            }
594          }
595        else
596          for (y=0; y < (ssize_t) image->rows; y++)
597          {
598            p=pixels+(image->rows-y-1)*4*image->columns;
599            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
600            if (q == (Quantum *) NULL)
601              break;
602            for (x=0; x < (ssize_t) image->columns; x++)
603            {
604              SetPixelRed(image,ScaleCharToQuantum(*p),q);
605              SetPixelGreen(image,ScaleCharToQuantum(*(p+1)),q);
606              SetPixelBlue(image,ScaleCharToQuantum(*(p+2)),q);
607              SetPixelAlpha(image,OpaqueAlpha,q);
608              if (image->alpha_trait != UndefinedPixelTrait)
609                SetPixelAlpha(image,ScaleCharToQuantum(*(p+3)),q);
610              p+=4;
611              q+=GetPixelChannels(image);
612            }
613            if (SyncAuthenticPixels(image,exception) == MagickFalse)
614              break;
615            if (image->previous == (Image *) NULL)
616              {
617                status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
618                  image->rows);
619                if (status == MagickFalse)
620                  break;
621              }
622          }
623      }
624    else
625      {
626        /*
627          Create grayscale map.
628        */
629        if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
630          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
631        /*
632          Convert SGI image to PseudoClass pixel packets.
633        */
634        if (bytes_per_pixel == 2)
635          {
636            for (y=0; y < (ssize_t) image->rows; y++)
637            {
638              p=pixels+(image->rows-y-1)*8*image->columns;
639              q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
640              if (q == (Quantum *) NULL)
641                break;
642              for (x=0; x < (ssize_t) image->columns; x++)
643              {
644                quantum=(*p << 8);
645                quantum|=(*(p+1));
646                SetPixelIndex(image,quantum,q);
647                p+=8;
648                q+=GetPixelChannels(image);
649              }
650              if (SyncAuthenticPixels(image,exception) == MagickFalse)
651                break;
652              if (image->previous == (Image *) NULL)
653                {
654                  status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
655                    y,image->rows);
656                  if (status == MagickFalse)
657                    break;
658                }
659            }
660          }
661        else
662          for (y=0; y < (ssize_t) image->rows; y++)
663          {
664            p=pixels+(image->rows-y-1)*4*image->columns;
665            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
666            if (q == (Quantum *) NULL)
667              break;
668            for (x=0; x < (ssize_t) image->columns; x++)
669            {
670              SetPixelIndex(image,*p,q);
671              p+=4;
672              q+=GetPixelChannels(image);
673            }
674            if (SyncAuthenticPixels(image,exception) == MagickFalse)
675              break;
676            if (image->previous == (Image *) NULL)
677              {
678                status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
679                image->rows);
680                if (status == MagickFalse)
681                  break;
682              }
683          }
684        (void) SyncImage(image,exception);
685      }
686    pixel_info=RelinquishVirtualMemory(pixel_info);
687    if (EOFBlob(image) != MagickFalse)
688      {
689        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
690          image->filename);
691        break;
692      }
693    /*
694      Proceed to next image.
695    */
696    if (image_info->number_scenes != 0)
697      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
698        break;
699    iris_info.magic=ReadBlobMSBShort(image);
700    if (iris_info.magic == 0x01DA)
701      {
702        /*
703          Allocate next image structure.
704        */
705        AcquireNextImage(image_info,image,exception);
706        if (GetNextImageInList(image) == (Image *) NULL)
707          {
708            image=DestroyImageList(image);
709            return((Image *) NULL);
710          }
711        image=SyncNextImageInList(image);
712        status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
713          GetBlobSize(image));
714        if (status == MagickFalse)
715          break;
716      }
717  } while (iris_info.magic == 0x01DA);
718  (void) CloseBlob(image);
719  return(GetFirstImageInList(image));
720}
721
722/*
723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
724%                                                                             %
725%                                                                             %
726%                                                                             %
727%   R e g i s t e r S G I I m a g e                                           %
728%                                                                             %
729%                                                                             %
730%                                                                             %
731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
732%
733%  RegisterSGIImage() adds properties for the SGI image format to
734%  the list of supported formats.  The properties include the image format
735%  tag, a method to read and/or write the format, whether the format
736%  supports the saving of more than one frame to the same file or blob,
737%  whether the format supports native in-memory I/O, and a brief
738%  description of the format.
739%
740%  The format of the RegisterSGIImage method is:
741%
742%      size_t RegisterSGIImage(void)
743%
744*/
745ModuleExport size_t RegisterSGIImage(void)
746{
747  MagickInfo
748    *entry;
749
750  entry=SetMagickInfo("SGI");
751  entry->decoder=(DecodeImageHandler *) ReadSGIImage;
752  entry->encoder=(EncodeImageHandler *) WriteSGIImage;
753  entry->magick=(IsImageFormatHandler *) IsSGI;
754  entry->description=ConstantString("Irix RGB image");
755  entry->module=ConstantString("SGI");
756  entry->seekable_stream=MagickTrue;
757  (void) RegisterMagickInfo(entry);
758  return(MagickImageCoderSignature);
759}
760
761/*
762%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
763%                                                                             %
764%                                                                             %
765%                                                                             %
766%   U n r e g i s t e r S G I I m a g e                                       %
767%                                                                             %
768%                                                                             %
769%                                                                             %
770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771%
772%  UnregisterSGIImage() removes format registrations made by the
773%  SGI module from the list of supported formats.
774%
775%  The format of the UnregisterSGIImage method is:
776%
777%      UnregisterSGIImage(void)
778%
779*/
780ModuleExport void UnregisterSGIImage(void)
781{
782  (void) UnregisterMagickInfo("SGI");
783}
784
785/*
786%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
787%                                                                             %
788%                                                                             %
789%                                                                             %
790%   W r i t e S G I I m a g e                                                 %
791%                                                                             %
792%                                                                             %
793%                                                                             %
794%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
795%
796%  WriteSGIImage() writes an image in SGI RGB encoded image format.
797%
798%  The format of the WriteSGIImage method is:
799%
800%      MagickBooleanType WriteSGIImage(const ImageInfo *image_info,
801%        Image *image,ExceptionInfo *exception)
802%
803%  A description of each parameter follows.
804%
805%    o image_info: the image info.
806%
807%    o image:  The image.
808%
809%    o exception: return any errors or warnings in this structure.
810%
811*/
812
813static size_t SGIEncode(unsigned char *pixels,size_t length,
814  unsigned char *packets)
815{
816  short
817    runlength;
818
819  register unsigned char
820    *p,
821    *q;
822
823  unsigned char
824    *limit,
825    *mark;
826
827  p=pixels;
828  limit=p+length*4;
829  q=packets;
830  while (p < limit)
831  {
832    mark=p;
833    p+=8;
834    while ((p < limit) && ((*(p-8) != *(p-4)) || (*(p-4) != *p)))
835      p+=4;
836    p-=8;
837    length=(size_t) (p-mark) >> 2;
838    while (length != 0)
839    {
840      runlength=(short) (length > 126 ? 126 : length);
841      length-=runlength;
842      *q++=(unsigned char) (0x80 | runlength);
843      for ( ; runlength > 0; runlength--)
844      {
845        *q++=(*mark);
846        mark+=4;
847      }
848    }
849    mark=p;
850    p+=4;
851    while ((p < limit) && (*p == *mark))
852      p+=4;
853    length=(size_t) (p-mark) >> 2;
854    while (length != 0)
855    {
856      runlength=(short) (length > 126 ? 126 : length);
857      length-=runlength;
858      *q++=(unsigned char) runlength;
859      *q++=(*mark);
860    }
861  }
862  *q++='\0';
863  return((size_t) (q-packets));
864}
865
866static MagickBooleanType WriteSGIImage(const ImageInfo *image_info,Image *image,
867  ExceptionInfo *exception)
868{
869  CompressionType
870    compression;
871
872  const char
873    *value;
874
875  MagickBooleanType
876    status;
877
878  MagickOffsetType
879    scene;
880
881  MagickSizeType
882    number_pixels;
883
884  MemoryInfo
885    *pixel_info;
886
887  SGIInfo
888    iris_info;
889
890  register const Quantum
891    *p;
892
893  register ssize_t
894    i,
895    x;
896
897  register unsigned char
898    *q;
899
900  ssize_t
901    y,
902    z;
903
904  unsigned char
905    *pixels,
906    *packets;
907
908  /*
909    Open output image file.
910  */
911  assert(image_info != (const ImageInfo *) NULL);
912  assert(image_info->signature == MagickSignature);
913  assert(image != (Image *) NULL);
914  assert(image->signature == MagickSignature);
915  if (image->debug != MagickFalse)
916    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
917  if ((image->columns > 65535UL) || (image->rows > 65535UL))
918    ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
919  assert(exception != (ExceptionInfo *) NULL);
920  assert(exception->signature == MagickSignature);
921  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
922  if (status == MagickFalse)
923    return(status);
924  scene=0;
925  do
926  {
927    /*
928      Initialize SGI raster file header.
929    */
930    (void) TransformImageColorspace(image,sRGBColorspace,exception);
931    (void) ResetMagickMemory(&iris_info,0,sizeof(iris_info));
932    iris_info.magic=0x01DA;
933    compression=image->compression;
934    if (image_info->compression != UndefinedCompression)
935      compression=image_info->compression;
936    if (image->depth > 8)
937      compression=NoCompression;
938    if (compression == NoCompression)
939      iris_info.storage=(unsigned char) 0x00;
940    else
941      iris_info.storage=(unsigned char) 0x01;
942    iris_info.bytes_per_pixel=(unsigned char) (image->depth > 8 ? 2 : 1);
943    iris_info.dimension=3;
944    iris_info.columns=(unsigned short) image->columns;
945    iris_info.rows=(unsigned short) image->rows;
946    if (image->alpha_trait != UndefinedPixelTrait)
947      iris_info.depth=4;
948    else
949      {
950        if ((image_info->type != TrueColorType) &&
951            (IsImageGray(image,exception) != MagickFalse))
952          {
953            iris_info.dimension=2;
954            iris_info.depth=1;
955          }
956        else
957          iris_info.depth=3;
958      }
959    iris_info.minimum_value=0;
960    iris_info.maximum_value=(size_t) (image->depth <= 8 ?
961      1UL*ScaleQuantumToChar(QuantumRange) :
962      1UL*ScaleQuantumToShort(QuantumRange));
963    /*
964      Write SGI header.
965    */
966    (void) WriteBlobMSBShort(image,iris_info.magic);
967    (void) WriteBlobByte(image,iris_info.storage);
968    (void) WriteBlobByte(image,iris_info.bytes_per_pixel);
969    (void) WriteBlobMSBShort(image,iris_info.dimension);
970    (void) WriteBlobMSBShort(image,iris_info.columns);
971    (void) WriteBlobMSBShort(image,iris_info.rows);
972    (void) WriteBlobMSBShort(image,iris_info.depth);
973    (void) WriteBlobMSBLong(image,(unsigned int) iris_info.minimum_value);
974    (void) WriteBlobMSBLong(image,(unsigned int) iris_info.maximum_value);
975    (void) WriteBlobMSBLong(image,(unsigned int) iris_info.sans);
976    value=GetImageProperty(image,"label",exception);
977    if (value != (const char *) NULL)
978      (void) CopyMagickString(iris_info.name,value,sizeof(iris_info.name));
979    (void) WriteBlob(image,sizeof(iris_info.name),(unsigned char *)
980      iris_info.name);
981    (void) WriteBlobMSBLong(image,(unsigned int) iris_info.pixel_format);
982    (void) WriteBlob(image,sizeof(iris_info.filler),iris_info.filler);
983    /*
984      Allocate SGI pixels.
985    */
986    number_pixels=(MagickSizeType) image->columns*image->rows;
987    if ((4*iris_info.bytes_per_pixel*number_pixels) !=
988        ((MagickSizeType) (size_t) (4*iris_info.bytes_per_pixel*number_pixels)))
989      ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
990    pixel_info=AcquireVirtualMemory((size_t) number_pixels,4*
991      iris_info.bytes_per_pixel*sizeof(*pixels));
992    if (pixel_info == (MemoryInfo *) NULL)
993      ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
994    pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
995    /*
996      Convert image pixels to uncompressed SGI pixels.
997    */
998    for (y=0; y < (ssize_t) image->rows; y++)
999    {
1000      p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1001      if (p == (const Quantum *) NULL)
1002        break;
1003      if (image->depth <= 8)
1004        for (x=0; x < (ssize_t) image->columns; x++)
1005        {
1006          register unsigned char
1007            *q;
1008
1009          q=(unsigned char *) pixels;
1010          q+=((iris_info.rows-1)-y)*(4*iris_info.columns)+4*x;
1011          *q++=ScaleQuantumToChar(GetPixelRed(image,p));
1012          *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
1013          *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
1014          *q++=ScaleQuantumToChar(GetPixelAlpha(image,p));
1015          p+=GetPixelChannels(image);
1016        }
1017      else
1018        for (x=0; x < (ssize_t) image->columns; x++)
1019        {
1020          register unsigned short
1021            *q;
1022
1023          q=(unsigned short *) pixels;
1024          q+=((iris_info.rows-1)-y)*(4*iris_info.columns)+4*x;
1025          *q++=ScaleQuantumToShort(GetPixelRed(image,p));
1026          *q++=ScaleQuantumToShort(GetPixelGreen(image,p));
1027          *q++=ScaleQuantumToShort(GetPixelBlue(image,p));
1028          *q++=ScaleQuantumToShort(GetPixelAlpha(image,p));
1029          p+=GetPixelChannels(image);
1030        }
1031      if (image->previous == (Image *) NULL)
1032        {
1033          status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1034            image->rows);
1035          if (status == MagickFalse)
1036            break;
1037        }
1038    }
1039    switch (compression)
1040    {
1041      case NoCompression:
1042      {
1043        /*
1044          Write uncompressed SGI pixels.
1045        */
1046        for (z=0; z < (ssize_t) iris_info.depth; z++)
1047        {
1048          for (y=0; y < (ssize_t) iris_info.rows; y++)
1049          {
1050            if (image->depth <= 8)
1051              for (x=0; x < (ssize_t) iris_info.columns; x++)
1052              {
1053                register unsigned char
1054                  *q;
1055
1056                q=(unsigned char *) pixels;
1057                q+=y*(4*iris_info.columns)+4*x+z;
1058                (void) WriteBlobByte(image,*q);
1059              }
1060            else
1061              for (x=0; x < (ssize_t) iris_info.columns; x++)
1062              {
1063                register unsigned short
1064                  *q;
1065
1066                q=(unsigned short *) pixels;
1067                q+=y*(4*iris_info.columns)+4*x+z;
1068                (void) WriteBlobMSBShort(image,*q);
1069              }
1070          }
1071        }
1072        break;
1073      }
1074      default:
1075      {
1076        MemoryInfo
1077          *packet_info;
1078
1079        size_t
1080          length,
1081          number_packets,
1082          *runlength;
1083
1084        ssize_t
1085          offset,
1086          *offsets;
1087
1088        /*
1089          Convert SGI uncompressed pixels.
1090        */
1091        offsets=(ssize_t *) AcquireQuantumMemory(iris_info.rows,
1092          iris_info.depth*sizeof(*offsets));
1093        runlength=(size_t *) AcquireQuantumMemory(iris_info.rows,
1094          iris_info.depth*sizeof(*runlength));
1095        packet_info=AcquireVirtualMemory((2*(size_t) iris_info.columns+10)*
1096          image->rows,4*sizeof(*packets));
1097        if ((offsets == (ssize_t *) NULL) ||
1098            (runlength == (size_t *) NULL) ||
1099            (packet_info == (MemoryInfo *) NULL))
1100          {
1101            if (offsets != (ssize_t *) NULL)
1102              offsets=(ssize_t *) RelinquishMagickMemory(offsets);
1103            if (runlength != (size_t *) NULL)
1104              runlength=(size_t *) RelinquishMagickMemory(runlength);
1105            if (packet_info != (MemoryInfo *) NULL)
1106              packet_info=RelinquishVirtualMemory(packet_info);
1107            ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1108          }
1109        packets=(unsigned char *) GetVirtualMemoryBlob(packet_info);
1110        offset=512+4*2*((ssize_t) iris_info.rows*iris_info.depth);
1111        number_packets=0;
1112        q=pixels;
1113        for (y=0; y < (ssize_t) iris_info.rows; y++)
1114        {
1115          for (z=0; z < (ssize_t) iris_info.depth; z++)
1116          {
1117            length=SGIEncode(q+z,(size_t) iris_info.columns,packets+
1118              number_packets);
1119            number_packets+=length;
1120            offsets[y+z*iris_info.rows]=offset;
1121            runlength[y+z*iris_info.rows]=(size_t) length;
1122            offset+=(ssize_t) length;
1123          }
1124          q+=(iris_info.columns*4);
1125        }
1126        /*
1127          Write out line start and length tables and runlength-encoded pixels.
1128        */
1129        for (i=0; i < (ssize_t) (iris_info.rows*iris_info.depth); i++)
1130          (void) WriteBlobMSBLong(image,(unsigned int) offsets[i]);
1131        for (i=0; i < (ssize_t) (iris_info.rows*iris_info.depth); i++)
1132          (void) WriteBlobMSBLong(image,(unsigned int) runlength[i]);
1133        (void) WriteBlob(image,number_packets,packets);
1134        /*
1135          Relinquish resources.
1136        */
1137        offsets=(ssize_t *) RelinquishMagickMemory(offsets);
1138        runlength=(size_t *) RelinquishMagickMemory(runlength);
1139        packet_info=RelinquishVirtualMemory(packet_info);
1140        break;
1141      }
1142    }
1143    pixel_info=RelinquishVirtualMemory(pixel_info);
1144    if (GetNextImageInList(image) == (Image *) NULL)
1145      break;
1146    image=SyncNextImageInList(image);
1147    status=SetImageProgress(image,SaveImagesTag,scene++,
1148      GetImageListLength(image));
1149    if (status == MagickFalse)
1150      break;
1151  } while (image_info->adjoin != MagickFalse);
1152  (void) CloseBlob(image);
1153  return(MagickTrue);
1154}
1155