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