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