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