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