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