]> granicus.if.org Git - imagemagick/blob - coders/sun.c
Remove --enable-zero-configuration as per https://github.com/ImageMagick/ImageMagick...
[imagemagick] / coders / sun.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            SSSSS  U   U  N   N                              %
7 %                            SS     U   U  NN  N                              %
8 %                             SSS   U   U  N N N                              %
9 %                               SS  U   U  N  NN                              %
10 %                            SSSSS   UUU   N   N                              %
11 %                                                                             %
12 %                                                                             %
13 %                    Read/Write Sun Rasterfile 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/colormap-private.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/colorspace-private.h"
53 #include "MagickCore/exception.h"
54 #include "MagickCore/exception-private.h"
55 #include "MagickCore/image.h"
56 #include "MagickCore/image-private.h"
57 #include "MagickCore/list.h"
58 #include "MagickCore/magick.h"
59 #include "MagickCore/memory_.h"
60 #include "MagickCore/memory-private.h"
61 #include "MagickCore/monitor.h"
62 #include "MagickCore/monitor-private.h"
63 #include "MagickCore/pixel-accessor.h"
64 #include "MagickCore/quantum-private.h"
65 #include "MagickCore/static.h"
66 #include "MagickCore/string_.h"
67 #include "MagickCore/module.h"
68 \f
69 /*
70   Forward declarations.
71 */
72 static MagickBooleanType
73   WriteSUNImage(const ImageInfo *,Image *,ExceptionInfo *);
74 \f
75 /*
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77 %                                                                             %
78 %                                                                             %
79 %                                                                             %
80 %   I s S U N                                                                 %
81 %                                                                             %
82 %                                                                             %
83 %                                                                             %
84 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85 %
86 %  IsSUN() returns MagickTrue if the image format type, identified by the
87 %  magick string, is SUN.
88 %
89 %  The format of the IsSUN method is:
90 %
91 %      MagickBooleanType IsSUN(const unsigned char *magick,const size_t length)
92 %
93 %  A description of each parameter follows:
94 %
95 %    o magick: compare image format pattern against these bytes.
96 %
97 %    o length: Specifies the length of the magick string.
98 %
99 */
100 static MagickBooleanType IsSUN(const unsigned char *magick,const size_t length)
101 {
102   if (length < 4)
103     return(MagickFalse);
104   if (memcmp(magick,"\131\246\152\225",4) == 0)
105     return(MagickTrue);
106   return(MagickFalse);
107 }
108 \f
109 /*
110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111 %                                                                             %
112 %                                                                             %
113 %                                                                             %
114 %   D e c o d e I m a g e                                                     %
115 %                                                                             %
116 %                                                                             %
117 %                                                                             %
118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119 %
120 %  DecodeImage unpacks the packed image pixels into  runlength-encoded pixel
121 %  packets.
122 %
123 %  The format of the DecodeImage method is:
124 %
125 %      MagickBooleanType DecodeImage(const unsigned char *compressed_pixels,
126 %        const size_t length,unsigned char *pixels)
127 %
128 %  A description of each parameter follows:
129 %
130 %    o compressed_pixels:  The address of a byte (8 bits) array of compressed
131 %      pixel data.
132 %
133 %    o length:  An integer value that is the total number of bytes of the
134 %      source image (as just read by ReadBlob)
135 %
136 %    o pixels:  The address of a byte (8 bits) array of pixel data created by
137 %      the uncompression process.  The number of bytes in this array
138 %      must be at least equal to the number columns times the number of rows
139 %      of the source pixels.
140 %
141 */
142 static MagickBooleanType DecodeImage(const unsigned char *compressed_pixels,
143   const size_t length,unsigned char *pixels,size_t extent)
144 {
145   register const unsigned char
146     *p;
147
148   register unsigned char
149     *q;
150
151   ssize_t
152     count;
153
154   unsigned char
155     byte;
156
157   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
158   assert(compressed_pixels != (unsigned char *) NULL);
159   assert(pixels != (unsigned char *) NULL);
160   p=compressed_pixels;
161   q=pixels;
162   while (((size_t) (p-compressed_pixels) < length) &&
163          ((size_t) (q-pixels) < extent))
164   {
165     byte=(*p++);
166     if (byte != 128U)
167       *q++=byte;
168     else
169       {
170         /*
171           Runlength-encoded packet: <count><byte>.
172         */
173         if (((size_t) (p-compressed_pixels) >= length))
174           break;
175         count=(*p++);
176         if (count > 0)
177           {
178             if (((size_t) (p-compressed_pixels) >= length))
179               break;
180             byte=(*p++);
181           }
182         while ((count >= 0) && ((size_t) (q-pixels) < extent))
183         {
184           *q++=byte;
185           count--;
186         }
187      }
188   }
189   return(((size_t) (q-pixels) == extent) ? MagickTrue : MagickFalse);
190 }
191 \f
192 /*
193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
194 %                                                                             %
195 %                                                                             %
196 %                                                                             %
197 %   R e a d S U N I m a g e                                                   %
198 %                                                                             %
199 %                                                                             %
200 %                                                                             %
201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
202 %
203 %  ReadSUNImage() reads a SUN image file and returns it.  It allocates
204 %  the memory necessary for the new Image structure and returns a pointer to
205 %  the new image.
206 %
207 %  The format of the ReadSUNImage method is:
208 %
209 %      Image *ReadSUNImage(const ImageInfo *image_info,ExceptionInfo *exception)
210 %
211 %  A description of each parameter follows:
212 %
213 %    o image_info: the image info.
214 %
215 %    o exception: return any errors or warnings in this structure.
216 %
217 */
218 static Image *ReadSUNImage(const ImageInfo *image_info,ExceptionInfo *exception)
219 {
220 #define RMT_EQUAL_RGB  1
221 #define RMT_NONE  0
222 #define RMT_RAW  2
223 #define RT_STANDARD  1
224 #define RT_ENCODED  2
225 #define RT_FORMAT_RGB  3
226
227   typedef struct _SUNInfo
228   {
229     unsigned int
230       magic,
231       width,
232       height,
233       depth,
234       length,
235       type,
236       maptype,
237       maplength;
238   } SUNInfo;
239
240   Image
241     *image;
242
243   int
244     bit;
245
246   MagickBooleanType
247     status;
248
249   MagickSizeType
250     number_pixels;
251
252   register Quantum
253     *q;
254
255   register ssize_t
256     i,
257     x;
258
259   register unsigned char
260     *p;
261
262   size_t
263     bytes_per_line,
264     extent,
265     height,
266     pixels_length,
267     quantum;
268
269   ssize_t
270     count,
271     y;
272
273   SUNInfo
274     sun_info;
275
276   unsigned char
277     *sun_data,
278     *sun_pixels;
279
280   /*
281     Open image file.
282   */
283   assert(image_info != (const ImageInfo *) NULL);
284   assert(image_info->signature == MagickCoreSignature);
285   if (image_info->debug != MagickFalse)
286     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
287       image_info->filename);
288   assert(exception != (ExceptionInfo *) NULL);
289   assert(exception->signature == MagickCoreSignature);
290   image=AcquireImage(image_info,exception);
291   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
292   if (status == MagickFalse)
293     {
294       image=DestroyImageList(image);
295       return((Image *) NULL);
296     }
297   /*
298     Read SUN raster header.
299   */
300   (void) ResetMagickMemory(&sun_info,0,sizeof(sun_info));
301   sun_info.magic=ReadBlobMSBLong(image);
302   do
303   {
304     /*
305       Verify SUN identifier.
306     */
307     if (sun_info.magic != 0x59a66a95)
308       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
309     sun_info.width=ReadBlobMSBLong(image);
310     sun_info.height=ReadBlobMSBLong(image);
311     sun_info.depth=ReadBlobMSBLong(image);
312     sun_info.length=ReadBlobMSBLong(image);
313     sun_info.type=ReadBlobMSBLong(image);
314     sun_info.maptype=ReadBlobMSBLong(image);
315     sun_info.maplength=ReadBlobMSBLong(image);
316     extent=sun_info.height*sun_info.width;
317     if ((sun_info.height != 0) && (sun_info.width != extent/sun_info.height))
318       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
319     if ((sun_info.type != RT_STANDARD) && (sun_info.type != RT_ENCODED) &&
320         (sun_info.type != RT_FORMAT_RGB))
321       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
322     if ((sun_info.maptype == RMT_NONE) && (sun_info.maplength != 0))
323       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
324     if ((sun_info.depth != 1) && (sun_info.depth != 8) &&
325         (sun_info.depth != 24) && (sun_info.depth != 32))
326       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
327     if ((sun_info.maptype != RMT_NONE) && (sun_info.maptype != RMT_EQUAL_RGB) &&
328         (sun_info.maptype != RMT_RAW))
329       ThrowReaderException(CoderError,"ColormapTypeNotSupported");
330     image->columns=sun_info.width;
331     image->rows=sun_info.height;
332     image->depth=sun_info.depth <= 8 ? sun_info.depth :
333       MAGICKCORE_QUANTUM_DEPTH;
334     if (sun_info.depth < 24)
335       {
336         size_t
337           one;
338
339         if (sun_info.maplength > GetBlobSize(image))
340           ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
341         image->colors=sun_info.maplength;
342         one=1;
343         if (sun_info.maptype == RMT_NONE)
344           image->colors=one << sun_info.depth;
345         if (sun_info.maptype == RMT_EQUAL_RGB)
346           image->colors=sun_info.maplength/3;
347         if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
348           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
349       }
350     switch (sun_info.maptype)
351     {
352       case RMT_NONE:
353         break;
354       case RMT_EQUAL_RGB:
355       {
356         unsigned char
357           *sun_colormap;
358
359         /*
360           Read SUN raster colormap.
361         */
362         sun_colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
363           sizeof(*sun_colormap));
364         if (sun_colormap == (unsigned char *) NULL)
365           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
366         count=ReadBlob(image,image->colors,sun_colormap);
367         if (count != (ssize_t) image->colors)
368           {
369             sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
370             ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
371           }
372         for (i=0; i < (ssize_t) image->colors; i++)
373           image->colormap[i].red=(MagickRealType) ScaleCharToQuantum(
374             sun_colormap[i]);
375         count=ReadBlob(image,image->colors,sun_colormap);
376         if (count != (ssize_t) image->colors)
377           {
378             sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
379             ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
380           }
381         for (i=0; i < (ssize_t) image->colors; i++)
382           image->colormap[i].green=(MagickRealType) ScaleCharToQuantum(
383             sun_colormap[i]);
384         count=ReadBlob(image,image->colors,sun_colormap);
385         if (count != (ssize_t) image->colors)
386           {
387             sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
388             ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
389           }
390         for (i=0; i < (ssize_t) image->colors; i++)
391           image->colormap[i].blue=(MagickRealType) ScaleCharToQuantum(
392             sun_colormap[i]);
393         sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
394         break;
395       }
396       case RMT_RAW:
397       {
398         unsigned char
399           *sun_colormap;
400
401         /*
402           Read SUN raster colormap.
403         */
404         sun_colormap=(unsigned char *) AcquireQuantumMemory(sun_info.maplength,
405           sizeof(*sun_colormap));
406         if (sun_colormap == (unsigned char *) NULL)
407           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
408         count=ReadBlob(image,sun_info.maplength,sun_colormap);
409         sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
410         if (count != (ssize_t) sun_info.maplength)
411           ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
412         break;
413       }
414       default:
415         ThrowReaderException(CoderError,"ColormapTypeNotSupported");
416     }
417     image->alpha_trait=sun_info.depth == 32 ? BlendPixelTrait :
418       UndefinedPixelTrait;
419     image->columns=sun_info.width;
420     image->rows=sun_info.height;
421     if (image_info->ping != MagickFalse)
422       {
423         (void) CloseBlob(image);
424         return(GetFirstImageInList(image));
425       }
426     status=SetImageExtent(image,image->columns,image->rows,exception);
427     if (status == MagickFalse)
428       return(DestroyImageList(image));
429     if (sun_info.length == 0)
430       ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
431     number_pixels=(MagickSizeType) (image->columns*image->rows);
432     if ((sun_info.type != RT_ENCODED) &&
433         ((number_pixels*sun_info.depth) > (8UL*sun_info.length)))
434       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
435     if (HeapOverflowSanityCheck(sun_info.width,sun_info.depth) != MagickFalse)
436       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
437     bytes_per_line=sun_info.width*sun_info.depth;
438     if (sun_info.length > GetBlobSize(image))
439       ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
440     sun_data=(unsigned char *) AcquireQuantumMemory(sun_info.length,
441       sizeof(*sun_data));
442     if (sun_data == (unsigned char *) NULL)
443       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
444     count=(ssize_t) ReadBlob(image,sun_info.length,sun_data);
445     if (count != (ssize_t) sun_info.length)
446       {
447         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
448         ThrowReaderException(CorruptImageError,"UnableToReadImageData");
449       }
450     height=sun_info.height;
451     if ((height == 0) || (sun_info.width == 0) || (sun_info.depth == 0) ||
452         ((bytes_per_line/sun_info.depth) != sun_info.width))
453       {
454         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
455         ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
456       }
457     quantum=sun_info.depth == 1 ? 15 : 7;
458     bytes_per_line+=quantum;
459     bytes_per_line<<=1;
460     if ((bytes_per_line >> 1) != (sun_info.width*sun_info.depth+quantum))
461       {
462         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
463         ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
464       }
465     bytes_per_line>>=4;
466     if (HeapOverflowSanityCheck(height,bytes_per_line) != MagickFalse)
467       {
468         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
469         ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
470       }
471     pixels_length=height*bytes_per_line;
472     sun_pixels=(unsigned char *) AcquireQuantumMemory(pixels_length+image->rows,
473       sizeof(*sun_pixels));
474     if (sun_pixels == (unsigned char *) NULL)
475       {
476         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
477         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
478       }
479     ResetMagickMemory(sun_pixels,0,pixels_length*sizeof(*sun_pixels));
480     if (sun_info.type == RT_ENCODED)
481       {
482         status=DecodeImage(sun_data,sun_info.length,sun_pixels,pixels_length);
483         if (status == MagickFalse)
484           {
485             sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
486             sun_pixels=(unsigned char *) RelinquishMagickMemory(sun_pixels);
487             ThrowReaderException(CorruptImageError,"UnableToReadImageData");
488           }
489       }
490     else
491       {
492         if (sun_info.length > pixels_length)
493           {
494             sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
495             sun_pixels=(unsigned char *) RelinquishMagickMemory(sun_pixels);
496             ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
497           }
498         (void) CopyMagickMemory(sun_pixels,sun_data,sun_info.length);
499       }
500     sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
501     /*
502       Convert SUN raster image to pixel packets.
503     */
504     p=sun_pixels;
505     if (sun_info.depth == 1)
506       for (y=0; y < (ssize_t) image->rows; y++)
507       {
508         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
509         if (q == (Quantum *) NULL)
510           break;
511         for (x=0; x < ((ssize_t) image->columns-7); x+=8)
512         {
513           for (bit=7; bit >= 0; bit--)
514           {
515             SetPixelIndex(image,(Quantum) ((*p) & (0x01 << bit) ? 0x00 : 0x01),
516               q);
517             q+=GetPixelChannels(image);
518           }
519           p++;
520         }
521         if ((image->columns % 8) != 0)
522           {
523             for (bit=7; bit >= (int) (8-(image->columns % 8)); bit--)
524             {
525               SetPixelIndex(image,(Quantum) ((*p) & (0x01 << bit) ? 0x00 :
526                 0x01),q);
527               q+=GetPixelChannels(image);
528             }
529             p++;
530           }
531         if ((((image->columns/8)+(image->columns % 8 ? 1 : 0)) % 2) != 0)
532           p++;
533         if (SyncAuthenticPixels(image,exception) == MagickFalse)
534           break;
535         if (image->previous == (Image *) NULL)
536           {
537             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
538               image->rows);
539             if (status == MagickFalse)
540               break;
541           }
542       }
543     else
544       if (image->storage_class == PseudoClass)
545         {
546           for (y=0; y < (ssize_t) image->rows; y++)
547           {
548             q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
549             if (q == (Quantum *) NULL)
550               break;
551             for (x=0; x < (ssize_t) image->columns; x++)
552             {
553               SetPixelIndex(image,ConstrainColormapIndex(image,*p,exception),q);
554               p++;
555               q+=GetPixelChannels(image);
556             }
557             if ((image->columns % 2) != 0)
558               p++;
559             if (SyncAuthenticPixels(image,exception) == MagickFalse)
560               break;
561             if (image->previous == (Image *) NULL)
562               {
563                 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
564                 image->rows);
565                 if (status == MagickFalse)
566                   break;
567               }
568           }
569         }
570       else
571         {
572           size_t
573             bytes_per_pixel;
574
575           bytes_per_pixel=3;
576           if (image->alpha_trait != UndefinedPixelTrait)
577             bytes_per_pixel++;
578           if (bytes_per_line == 0)
579             bytes_per_line=bytes_per_pixel*image->columns;
580           for (y=0; y < (ssize_t) image->rows; y++)
581           {
582             q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
583             if (q == (Quantum *) NULL)
584               break;
585             for (x=0; x < (ssize_t) image->columns; x++)
586             {
587               if (image->alpha_trait != UndefinedPixelTrait)
588                 SetPixelAlpha(image,ScaleCharToQuantum(*p++),q);
589               if (sun_info.type == RT_STANDARD)
590                 {
591                   SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
592                   SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
593                   SetPixelRed(image,ScaleCharToQuantum(*p++),q);
594                 }
595               else
596                 {
597                   SetPixelRed(image,ScaleCharToQuantum(*p++),q);
598                   SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
599                   SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
600                 }
601               if (image->colors != 0)
602                 {
603                   SetPixelRed(image,ClampToQuantum(image->colormap[(ssize_t)
604                     GetPixelRed(image,q)].red),q);
605                   SetPixelGreen(image,ClampToQuantum(image->colormap[(ssize_t)
606                     GetPixelGreen(image,q)].green),q);
607                   SetPixelBlue(image,ClampToQuantum(image->colormap[(ssize_t)
608                     GetPixelBlue(image,q)].blue),q);
609                 }
610               q+=GetPixelChannels(image);
611             }
612             if (((bytes_per_pixel*image->columns) % 2) != 0)
613               p++;
614             if (SyncAuthenticPixels(image,exception) == MagickFalse)
615               break;
616             if (image->previous == (Image *) NULL)
617               {
618                 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
619                 image->rows);
620                 if (status == MagickFalse)
621                   break;
622               }
623           }
624         }
625     if (image->storage_class == PseudoClass)
626       (void) SyncImage(image,exception);
627     sun_pixels=(unsigned char *) RelinquishMagickMemory(sun_pixels);
628     if (EOFBlob(image) != MagickFalse)
629       {
630         ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
631           image->filename);
632         break;
633       }
634     /*
635       Proceed to next image.
636     */
637     if (image_info->number_scenes != 0)
638       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
639         break;
640     sun_info.magic=ReadBlobMSBLong(image);
641     if (sun_info.magic == 0x59a66a95)
642       {
643         /*
644           Allocate next image structure.
645         */
646         AcquireNextImage(image_info,image,exception);
647         if (GetNextImageInList(image) == (Image *) NULL)
648           {
649             image=DestroyImageList(image);
650             return((Image *) NULL);
651           }
652         image=SyncNextImageInList(image);
653         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
654           GetBlobSize(image));
655         if (status == MagickFalse)
656           break;
657       }
658   } while (sun_info.magic == 0x59a66a95);
659   (void) CloseBlob(image);
660   return(GetFirstImageInList(image));
661 }
662 \f
663 /*
664 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
665 %                                                                             %
666 %                                                                             %
667 %                                                                             %
668 %   R e g i s t e r S U N I m a g e                                           %
669 %                                                                             %
670 %                                                                             %
671 %                                                                             %
672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
673 %
674 %  RegisterSUNImage() adds attributes for the SUN image format to
675 %  the list of supported formats.  The attributes include the image format
676 %  tag, a method to read and/or write the format, whether the format
677 %  supports the saving of more than one frame to the same file or blob,
678 %  whether the format supports native in-memory I/O, and a brief
679 %  description of the format.
680 %
681 %  The format of the RegisterSUNImage method is:
682 %
683 %      size_t RegisterSUNImage(void)
684 %
685 */
686 ModuleExport size_t RegisterSUNImage(void)
687 {
688   MagickInfo
689     *entry;
690
691   entry=AcquireMagickInfo("SUN","RAS","SUN Rasterfile");
692   entry->decoder=(DecodeImageHandler *) ReadSUNImage;
693   entry->encoder=(EncodeImageHandler *) WriteSUNImage;
694   entry->magick=(IsImageFormatHandler *) IsSUN;
695   entry->flags|=CoderDecoderSeekableStreamFlag;
696   (void) RegisterMagickInfo(entry);
697   entry=AcquireMagickInfo("SUN","SUN","SUN Rasterfile");
698   entry->decoder=(DecodeImageHandler *) ReadSUNImage;
699   entry->encoder=(EncodeImageHandler *) WriteSUNImage;
700   entry->flags|=CoderDecoderSeekableStreamFlag;
701   (void) RegisterMagickInfo(entry);
702   return(MagickImageCoderSignature);
703 }
704 \f
705 /*
706 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
707 %                                                                             %
708 %                                                                             %
709 %                                                                             %
710 %   U n r e g i s t e r S U N I m a g e                                       %
711 %                                                                             %
712 %                                                                             %
713 %                                                                             %
714 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
715 %
716 %  UnregisterSUNImage() removes format registrations made by the
717 %  SUN module from the list of supported formats.
718 %
719 %  The format of the UnregisterSUNImage method is:
720 %
721 %      UnregisterSUNImage(void)
722 %
723 */
724 ModuleExport void UnregisterSUNImage(void)
725 {
726   (void) UnregisterMagickInfo("RAS");
727   (void) UnregisterMagickInfo("SUN");
728 }
729 \f
730 /*
731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
732 %                                                                             %
733 %                                                                             %
734 %                                                                             %
735 %   W r i t e S U N I m a g e                                                 %
736 %                                                                             %
737 %                                                                             %
738 %                                                                             %
739 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
740 %
741 %  WriteSUNImage() writes an image in the SUN rasterfile format.
742 %
743 %  The format of the WriteSUNImage method is:
744 %
745 %      MagickBooleanType WriteSUNImage(const ImageInfo *image_info,
746 %        Image *image,ExceptionInfo *exception)
747 %
748 %  A description of each parameter follows.
749 %
750 %    o image_info: the image info.
751 %
752 %    o image:  The image.
753 %
754 %    o exception: return any errors or warnings in this structure.
755 %
756 */
757 static MagickBooleanType WriteSUNImage(const ImageInfo *image_info,Image *image,
758   ExceptionInfo *exception)
759 {
760 #define RMT_EQUAL_RGB  1
761 #define RMT_NONE  0
762 #define RMT_RAW  2
763 #define RT_STANDARD  1
764 #define RT_FORMAT_RGB  3
765
766   typedef struct _SUNInfo
767   {
768     unsigned int
769       magic,
770       width,
771       height,
772       depth,
773       length,
774       type,
775       maptype,
776       maplength;
777   } SUNInfo;
778
779   MagickBooleanType
780     status;
781
782   MagickOffsetType
783     scene;
784
785   MagickSizeType
786     number_pixels;
787
788   register const Quantum
789     *p;
790
791   register ssize_t
792     i,
793     x;
794
795   ssize_t
796     y;
797
798   SUNInfo
799     sun_info;
800
801   /*
802     Open output image file.
803   */
804   assert(image_info != (const ImageInfo *) NULL);
805   assert(image_info->signature == MagickCoreSignature);
806   assert(image != (Image *) NULL);
807   assert(image->signature == MagickCoreSignature);
808   if (image->debug != MagickFalse)
809     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
810   assert(exception != (ExceptionInfo *) NULL);
811   assert(exception->signature == MagickCoreSignature);
812   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
813   if (status == MagickFalse)
814     return(status);
815   scene=0;
816   do
817   {
818     /*
819       Initialize SUN raster file header.
820     */
821     (void) TransformImageColorspace(image,sRGBColorspace,exception);
822     sun_info.magic=0x59a66a95;
823     if ((image->columns != (unsigned int) image->columns) ||
824         (image->rows != (unsigned int) image->rows))
825       ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
826     sun_info.width=(unsigned int) image->columns;
827     sun_info.height=(unsigned int) image->rows;
828     sun_info.type=(unsigned int)
829       (image->storage_class == DirectClass ? RT_FORMAT_RGB : RT_STANDARD);
830     sun_info.maptype=RMT_NONE;
831     sun_info.maplength=0;
832     number_pixels=(MagickSizeType) image->columns*image->rows;
833     if ((4*number_pixels) != (size_t) (4*number_pixels))
834       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
835     if (image->storage_class == DirectClass)
836       {
837         /*
838           Full color SUN raster.
839         */
840         sun_info.depth=(unsigned int) image->alpha_trait !=
841           UndefinedPixelTrait ? 32U : 24U;
842         sun_info.length=(unsigned int) ((image->alpha_trait !=
843           UndefinedPixelTrait ? 4 : 3)*number_pixels);
844         sun_info.length+=sun_info.length & 0x01 ? (unsigned int) image->rows :
845           0;
846       }
847     else
848       if (SetImageMonochrome(image,exception) != MagickFalse)
849         {
850           /*
851             Monochrome SUN raster.
852           */
853           sun_info.depth=1;
854           sun_info.length=(unsigned int) (((image->columns+7) >> 3)*
855             image->rows);
856           sun_info.length+=(unsigned int) (((image->columns/8)+(image->columns %
857             8 ? 1 : 0)) % 2 ? image->rows : 0);
858         }
859       else
860         {
861           /*
862             Colormapped SUN raster.
863           */
864           sun_info.depth=8;
865           sun_info.length=(unsigned int) number_pixels;
866           sun_info.length+=(unsigned int) (image->columns & 0x01 ? image->rows :
867             0);
868           sun_info.maptype=RMT_EQUAL_RGB;
869           sun_info.maplength=(unsigned int) (3*image->colors);
870         }
871     /*
872       Write SUN header.
873     */
874     (void) WriteBlobMSBLong(image,sun_info.magic);
875     (void) WriteBlobMSBLong(image,sun_info.width);
876     (void) WriteBlobMSBLong(image,sun_info.height);
877     (void) WriteBlobMSBLong(image,sun_info.depth);
878     (void) WriteBlobMSBLong(image,sun_info.length);
879     (void) WriteBlobMSBLong(image,sun_info.type);
880     (void) WriteBlobMSBLong(image,sun_info.maptype);
881     (void) WriteBlobMSBLong(image,sun_info.maplength);
882     /*
883       Convert MIFF to SUN raster pixels.
884     */
885     x=0;
886     y=0;
887     if (image->storage_class == DirectClass)
888       {
889         register unsigned char
890           *q;
891
892         size_t
893           bytes_per_pixel,
894           length;
895
896         unsigned char
897           *pixels;
898
899         /*
900           Allocate memory for pixels.
901         */
902         bytes_per_pixel=3;
903         if (image->alpha_trait != UndefinedPixelTrait)
904           bytes_per_pixel++;
905         length=image->columns;
906         pixels=(unsigned char *) AcquireQuantumMemory(length,4*sizeof(*pixels));
907         if (pixels == (unsigned char *) NULL)
908           ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
909         /*
910           Convert DirectClass packet to SUN RGB pixel.
911         */
912         for (y=0; y < (ssize_t) image->rows; y++)
913         {
914           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
915           if (p == (const Quantum *) NULL)
916             break;
917           q=pixels;
918           for (x=0; x < (ssize_t) image->columns; x++)
919           {
920             if (image->alpha_trait != UndefinedPixelTrait)
921               *q++=ScaleQuantumToChar(GetPixelAlpha(image,p));
922             *q++=ScaleQuantumToChar(GetPixelRed(image,p));
923             *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
924             *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
925             p+=GetPixelChannels(image);
926           }
927           if (((bytes_per_pixel*image->columns) & 0x01) != 0)
928             *q++='\0';  /* pad scanline */
929           (void) WriteBlob(image,(size_t) (q-pixels),pixels);
930           if (image->previous == (Image *) NULL)
931             {
932               status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
933                 image->rows);
934               if (status == MagickFalse)
935                 break;
936             }
937         }
938         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
939       }
940     else
941       if (SetImageMonochrome(image,exception) != MagickFalse)
942         {
943           register unsigned char
944             bit,
945             byte;
946
947           /*
948             Convert PseudoClass image to a SUN monochrome image.
949           */
950           (void) SetImageType(image,BilevelType,exception);
951           for (y=0; y < (ssize_t) image->rows; y++)
952           {
953             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
954             if (p == (const Quantum *) NULL)
955               break;
956             bit=0;
957             byte=0;
958             for (x=0; x < (ssize_t) image->columns; x++)
959             {
960               byte<<=1;
961               if (GetPixelLuma(image,p) < (QuantumRange/2.0))
962                 byte|=0x01;
963               bit++;
964               if (bit == 8)
965                 {
966                   (void) WriteBlobByte(image,byte);
967                   bit=0;
968                   byte=0;
969                 }
970               p+=GetPixelChannels(image);
971             }
972             if (bit != 0)
973               (void) WriteBlobByte(image,(unsigned char) (byte << (8-bit)));
974             if ((((image->columns/8)+
975                 (image->columns % 8 ? 1 : 0)) % 2) != 0)
976               (void) WriteBlobByte(image,0);  /* pad scanline */
977             if (image->previous == (Image *) NULL)
978               {
979                 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
980                 image->rows);
981                 if (status == MagickFalse)
982                   break;
983               }
984           }
985         }
986       else
987         {
988           /*
989             Dump colormap to file.
990           */
991           for (i=0; i < (ssize_t) image->colors; i++)
992             (void) WriteBlobByte(image,ScaleQuantumToChar(
993               ClampToQuantum(image->colormap[i].red)));
994           for (i=0; i < (ssize_t) image->colors; i++)
995             (void) WriteBlobByte(image,ScaleQuantumToChar(
996               ClampToQuantum(image->colormap[i].green)));
997           for (i=0; i < (ssize_t) image->colors; i++)
998             (void) WriteBlobByte(image,ScaleQuantumToChar(
999               ClampToQuantum(image->colormap[i].blue)));
1000           /*
1001             Convert PseudoClass packet to SUN colormapped pixel.
1002           */
1003           for (y=0; y < (ssize_t) image->rows; y++)
1004           {
1005             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1006             if (p == (const Quantum *) NULL)
1007               break;
1008             for (x=0; x < (ssize_t) image->columns; x++)
1009             {
1010               (void) WriteBlobByte(image,(unsigned char)
1011                 GetPixelIndex(image,p));
1012               p+=GetPixelChannels(image);
1013             }
1014             if (image->columns & 0x01)
1015               (void) WriteBlobByte(image,0);  /* pad scanline */
1016             if (image->previous == (Image *) NULL)
1017               {
1018                 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1019                   image->rows);
1020                 if (status == MagickFalse)
1021                   break;
1022               }
1023           }
1024         }
1025     if (GetNextImageInList(image) == (Image *) NULL)
1026       break;
1027     image=SyncNextImageInList(image);
1028     status=SetImageProgress(image,SaveImagesTag,scene++,
1029       GetImageListLength(image));
1030     if (status == MagickFalse)
1031       break;
1032   } while (image_info->adjoin != MagickFalse);
1033   (void) CloseBlob(image);
1034   return(MagickTrue);
1035 }