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