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