]> granicus.if.org Git - imagemagick/blob - coders/sun.c
https://github.com/ImageMagick/ImageMagick/issues/659
[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     sun_data=(unsigned char *) AcquireQuantumMemory(sun_info.length,
439       sizeof(*sun_data));
440     if (sun_data == (unsigned char *) NULL)
441       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
442     count=(ssize_t) ReadBlob(image,sun_info.length,sun_data);
443     if (count != (ssize_t) sun_info.length)
444       {
445         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
446         ThrowReaderException(CorruptImageError,"UnableToReadImageData");
447       }
448     height=sun_info.height;
449     if ((height == 0) || (sun_info.width == 0) || (sun_info.depth == 0) ||
450         ((bytes_per_line/sun_info.depth) != sun_info.width))
451       {
452         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
453         ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
454       }
455     quantum=sun_info.depth == 1 ? 15 : 7;
456     bytes_per_line+=quantum;
457     bytes_per_line<<=1;
458     if ((bytes_per_line >> 1) != (sun_info.width*sun_info.depth+quantum))
459       {
460         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
461         ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
462       }
463     bytes_per_line>>=4;
464     if (HeapOverflowSanityCheck(height,bytes_per_line) != MagickFalse)
465       {
466         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
467         ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
468       }
469     pixels_length=height*bytes_per_line;
470     sun_pixels=(unsigned char *) AcquireQuantumMemory(pixels_length+image->rows,
471       sizeof(*sun_pixels));
472     if (sun_pixels == (unsigned char *) NULL)
473       {
474         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
475         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
476       }
477     ResetMagickMemory(sun_pixels,0,pixels_length*sizeof(*sun_pixels));
478     if (sun_info.type == RT_ENCODED)
479       {
480         status=DecodeImage(sun_data,sun_info.length,sun_pixels,pixels_length);
481         if (status == MagickFalse)
482           ThrowReaderException(CorruptImageError,"UnableToReadImageData");
483       }
484     else
485       {
486         if (sun_info.length > pixels_length)
487           {
488             sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
489             sun_pixels=(unsigned char *) RelinquishMagickMemory(sun_pixels);
490             ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
491           }
492         (void) CopyMagickMemory(sun_pixels,sun_data,sun_info.length);
493       }
494     sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
495     /*
496       Convert SUN raster image to pixel packets.
497     */
498     p=sun_pixels;
499     if (sun_info.depth == 1)
500       for (y=0; y < (ssize_t) image->rows; y++)
501       {
502         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
503         if (q == (Quantum *) NULL)
504           break;
505         for (x=0; x < ((ssize_t) image->columns-7); x+=8)
506         {
507           for (bit=7; bit >= 0; bit--)
508           {
509             SetPixelIndex(image,(Quantum) ((*p) & (0x01 << bit) ? 0x00 : 0x01),
510               q);
511             q+=GetPixelChannels(image);
512           }
513           p++;
514         }
515         if ((image->columns % 8) != 0)
516           {
517             for (bit=7; bit >= (int) (8-(image->columns % 8)); bit--)
518             {
519               SetPixelIndex(image,(Quantum) ((*p) & (0x01 << bit) ? 0x00 :
520                 0x01),q);
521               q+=GetPixelChannels(image);
522             }
523             p++;
524           }
525         if ((((image->columns/8)+(image->columns % 8 ? 1 : 0)) % 2) != 0)
526           p++;
527         if (SyncAuthenticPixels(image,exception) == MagickFalse)
528           break;
529         if (image->previous == (Image *) NULL)
530           {
531             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
532               image->rows);
533             if (status == MagickFalse)
534               break;
535           }
536       }
537     else
538       if (image->storage_class == PseudoClass)
539         {
540           for (y=0; y < (ssize_t) image->rows; y++)
541           {
542             q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
543             if (q == (Quantum *) NULL)
544               break;
545             for (x=0; x < (ssize_t) image->columns; x++)
546             {
547               SetPixelIndex(image,ConstrainColormapIndex(image,*p,exception),q);
548               p++;
549               q+=GetPixelChannels(image);
550             }
551             if ((image->columns % 2) != 0)
552               p++;
553             if (SyncAuthenticPixels(image,exception) == MagickFalse)
554               break;
555             if (image->previous == (Image *) NULL)
556               {
557                 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
558                 image->rows);
559                 if (status == MagickFalse)
560                   break;
561               }
562           }
563         }
564       else
565         {
566           size_t
567             bytes_per_pixel;
568
569           bytes_per_pixel=3;
570           if (image->alpha_trait != UndefinedPixelTrait)
571             bytes_per_pixel++;
572           if (bytes_per_line == 0)
573             bytes_per_line=bytes_per_pixel*image->columns;
574           for (y=0; y < (ssize_t) image->rows; y++)
575           {
576             q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
577             if (q == (Quantum *) NULL)
578               break;
579             for (x=0; x < (ssize_t) image->columns; x++)
580             {
581               if (image->alpha_trait != UndefinedPixelTrait)
582                 SetPixelAlpha(image,ScaleCharToQuantum(*p++),q);
583               if (sun_info.type == RT_STANDARD)
584                 {
585                   SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
586                   SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
587                   SetPixelRed(image,ScaleCharToQuantum(*p++),q);
588                 }
589               else
590                 {
591                   SetPixelRed(image,ScaleCharToQuantum(*p++),q);
592                   SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
593                   SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
594                 }
595               if (image->colors != 0)
596                 {
597                   SetPixelRed(image,ClampToQuantum(image->colormap[(ssize_t)
598                     GetPixelRed(image,q)].red),q);
599                   SetPixelGreen(image,ClampToQuantum(image->colormap[(ssize_t)
600                     GetPixelGreen(image,q)].green),q);
601                   SetPixelBlue(image,ClampToQuantum(image->colormap[(ssize_t)
602                     GetPixelBlue(image,q)].blue),q);
603                 }
604               q+=GetPixelChannels(image);
605             }
606             if (((bytes_per_pixel*image->columns) % 2) != 0)
607               p++;
608             if (SyncAuthenticPixels(image,exception) == MagickFalse)
609               break;
610             if (image->previous == (Image *) NULL)
611               {
612                 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
613                 image->rows);
614                 if (status == MagickFalse)
615                   break;
616               }
617           }
618         }
619     if (image->storage_class == PseudoClass)
620       (void) SyncImage(image,exception);
621     sun_pixels=(unsigned char *) RelinquishMagickMemory(sun_pixels);
622     if (EOFBlob(image) != MagickFalse)
623       {
624         ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
625           image->filename);
626         break;
627       }
628     /*
629       Proceed to next image.
630     */
631     if (image_info->number_scenes != 0)
632       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
633         break;
634     sun_info.magic=ReadBlobMSBLong(image);
635     if (sun_info.magic == 0x59a66a95)
636       {
637         /*
638           Allocate next image structure.
639         */
640         AcquireNextImage(image_info,image,exception);
641         if (GetNextImageInList(image) == (Image *) NULL)
642           {
643             image=DestroyImageList(image);
644             return((Image *) NULL);
645           }
646         image=SyncNextImageInList(image);
647         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
648           GetBlobSize(image));
649         if (status == MagickFalse)
650           break;
651       }
652   } while (sun_info.magic == 0x59a66a95);
653   (void) CloseBlob(image);
654   return(GetFirstImageInList(image));
655 }
656 \f
657 /*
658 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
659 %                                                                             %
660 %                                                                             %
661 %                                                                             %
662 %   R e g i s t e r S U N I m a g e                                           %
663 %                                                                             %
664 %                                                                             %
665 %                                                                             %
666 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667 %
668 %  RegisterSUNImage() adds attributes for the SUN image format to
669 %  the list of supported formats.  The attributes include the image format
670 %  tag, a method to read and/or write the format, whether the format
671 %  supports the saving of more than one frame to the same file or blob,
672 %  whether the format supports native in-memory I/O, and a brief
673 %  description of the format.
674 %
675 %  The format of the RegisterSUNImage method is:
676 %
677 %      size_t RegisterSUNImage(void)
678 %
679 */
680 ModuleExport size_t RegisterSUNImage(void)
681 {
682   MagickInfo
683     *entry;
684
685   entry=AcquireMagickInfo("SUN","RAS","SUN Rasterfile");
686   entry->decoder=(DecodeImageHandler *) ReadSUNImage;
687   entry->encoder=(EncodeImageHandler *) WriteSUNImage;
688   entry->magick=(IsImageFormatHandler *) IsSUN;
689   entry->flags|=CoderDecoderSeekableStreamFlag;
690   (void) RegisterMagickInfo(entry);
691   entry=AcquireMagickInfo("SUN","SUN","SUN Rasterfile");
692   entry->decoder=(DecodeImageHandler *) ReadSUNImage;
693   entry->encoder=(EncodeImageHandler *) WriteSUNImage;
694   entry->flags|=CoderDecoderSeekableStreamFlag;
695   (void) RegisterMagickInfo(entry);
696   return(MagickImageCoderSignature);
697 }
698 \f
699 /*
700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
701 %                                                                             %
702 %                                                                             %
703 %                                                                             %
704 %   U n r e g i s t e r S U N I m a g e                                       %
705 %                                                                             %
706 %                                                                             %
707 %                                                                             %
708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
709 %
710 %  UnregisterSUNImage() removes format registrations made by the
711 %  SUN module from the list of supported formats.
712 %
713 %  The format of the UnregisterSUNImage method is:
714 %
715 %      UnregisterSUNImage(void)
716 %
717 */
718 ModuleExport void UnregisterSUNImage(void)
719 {
720   (void) UnregisterMagickInfo("RAS");
721   (void) UnregisterMagickInfo("SUN");
722 }
723 \f
724 /*
725 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
726 %                                                                             %
727 %                                                                             %
728 %                                                                             %
729 %   W r i t e S U N I m a g e                                                 %
730 %                                                                             %
731 %                                                                             %
732 %                                                                             %
733 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
734 %
735 %  WriteSUNImage() writes an image in the SUN rasterfile format.
736 %
737 %  The format of the WriteSUNImage method is:
738 %
739 %      MagickBooleanType WriteSUNImage(const ImageInfo *image_info,
740 %        Image *image,ExceptionInfo *exception)
741 %
742 %  A description of each parameter follows.
743 %
744 %    o image_info: the image info.
745 %
746 %    o image:  The image.
747 %
748 %    o exception: return any errors or warnings in this structure.
749 %
750 */
751 static MagickBooleanType WriteSUNImage(const ImageInfo *image_info,Image *image,
752   ExceptionInfo *exception)
753 {
754 #define RMT_EQUAL_RGB  1
755 #define RMT_NONE  0
756 #define RMT_RAW  2
757 #define RT_STANDARD  1
758 #define RT_FORMAT_RGB  3
759
760   typedef struct _SUNInfo
761   {
762     unsigned int
763       magic,
764       width,
765       height,
766       depth,
767       length,
768       type,
769       maptype,
770       maplength;
771   } SUNInfo;
772
773   MagickBooleanType
774     status;
775
776   MagickOffsetType
777     scene;
778
779   MagickSizeType
780     number_pixels;
781
782   register const Quantum
783     *p;
784
785   register ssize_t
786     i,
787     x;
788
789   ssize_t
790     y;
791
792   SUNInfo
793     sun_info;
794
795   /*
796     Open output image file.
797   */
798   assert(image_info != (const ImageInfo *) NULL);
799   assert(image_info->signature == MagickCoreSignature);
800   assert(image != (Image *) NULL);
801   assert(image->signature == MagickCoreSignature);
802   if (image->debug != MagickFalse)
803     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
804   assert(exception != (ExceptionInfo *) NULL);
805   assert(exception->signature == MagickCoreSignature);
806   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
807   if (status == MagickFalse)
808     return(status);
809   scene=0;
810   do
811   {
812     /*
813       Initialize SUN raster file header.
814     */
815     (void) TransformImageColorspace(image,sRGBColorspace,exception);
816     sun_info.magic=0x59a66a95;
817     if ((image->columns != (unsigned int) image->columns) ||
818         (image->rows != (unsigned int) image->rows))
819       ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
820     sun_info.width=(unsigned int) image->columns;
821     sun_info.height=(unsigned int) image->rows;
822     sun_info.type=(unsigned int)
823       (image->storage_class == DirectClass ? RT_FORMAT_RGB : RT_STANDARD);
824     sun_info.maptype=RMT_NONE;
825     sun_info.maplength=0;
826     number_pixels=(MagickSizeType) image->columns*image->rows;
827     if ((4*number_pixels) != (size_t) (4*number_pixels))
828       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
829     if (image->storage_class == DirectClass)
830       {
831         /*
832           Full color SUN raster.
833         */
834         sun_info.depth=(unsigned int) image->alpha_trait !=
835           UndefinedPixelTrait ? 32U : 24U;
836         sun_info.length=(unsigned int) ((image->alpha_trait !=
837           UndefinedPixelTrait ? 4 : 3)*number_pixels);
838         sun_info.length+=sun_info.length & 0x01 ? (unsigned int) image->rows :
839           0;
840       }
841     else
842       if (SetImageMonochrome(image,exception) != MagickFalse)
843         {
844           /*
845             Monochrome SUN raster.
846           */
847           sun_info.depth=1;
848           sun_info.length=(unsigned int) (((image->columns+7) >> 3)*
849             image->rows);
850           sun_info.length+=(unsigned int) (((image->columns/8)+(image->columns %
851             8 ? 1 : 0)) % 2 ? image->rows : 0);
852         }
853       else
854         {
855           /*
856             Colormapped SUN raster.
857           */
858           sun_info.depth=8;
859           sun_info.length=(unsigned int) number_pixels;
860           sun_info.length+=(unsigned int) (image->columns & 0x01 ? image->rows :
861             0);
862           sun_info.maptype=RMT_EQUAL_RGB;
863           sun_info.maplength=(unsigned int) (3*image->colors);
864         }
865     /*
866       Write SUN header.
867     */
868     (void) WriteBlobMSBLong(image,sun_info.magic);
869     (void) WriteBlobMSBLong(image,sun_info.width);
870     (void) WriteBlobMSBLong(image,sun_info.height);
871     (void) WriteBlobMSBLong(image,sun_info.depth);
872     (void) WriteBlobMSBLong(image,sun_info.length);
873     (void) WriteBlobMSBLong(image,sun_info.type);
874     (void) WriteBlobMSBLong(image,sun_info.maptype);
875     (void) WriteBlobMSBLong(image,sun_info.maplength);
876     /*
877       Convert MIFF to SUN raster pixels.
878     */
879     x=0;
880     y=0;
881     if (image->storage_class == DirectClass)
882       {
883         register unsigned char
884           *q;
885
886         size_t
887           bytes_per_pixel,
888           length;
889
890         unsigned char
891           *pixels;
892
893         /*
894           Allocate memory for pixels.
895         */
896         bytes_per_pixel=3;
897         if (image->alpha_trait != UndefinedPixelTrait)
898           bytes_per_pixel++;
899         length=image->columns;
900         pixels=(unsigned char *) AcquireQuantumMemory(length,4*sizeof(*pixels));
901         if (pixels == (unsigned char *) NULL)
902           ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
903         /*
904           Convert DirectClass packet to SUN RGB pixel.
905         */
906         for (y=0; y < (ssize_t) image->rows; y++)
907         {
908           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
909           if (p == (const Quantum *) NULL)
910             break;
911           q=pixels;
912           for (x=0; x < (ssize_t) image->columns; x++)
913           {
914             if (image->alpha_trait != UndefinedPixelTrait)
915               *q++=ScaleQuantumToChar(GetPixelAlpha(image,p));
916             *q++=ScaleQuantumToChar(GetPixelRed(image,p));
917             *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
918             *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
919             p+=GetPixelChannels(image);
920           }
921           if (((bytes_per_pixel*image->columns) & 0x01) != 0)
922             *q++='\0';  /* pad scanline */
923           (void) WriteBlob(image,(size_t) (q-pixels),pixels);
924           if (image->previous == (Image *) NULL)
925             {
926               status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
927                 image->rows);
928               if (status == MagickFalse)
929                 break;
930             }
931         }
932         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
933       }
934     else
935       if (SetImageMonochrome(image,exception) != MagickFalse)
936         {
937           register unsigned char
938             bit,
939             byte;
940
941           /*
942             Convert PseudoClass image to a SUN monochrome image.
943           */
944           (void) SetImageType(image,BilevelType,exception);
945           for (y=0; y < (ssize_t) image->rows; y++)
946           {
947             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
948             if (p == (const Quantum *) NULL)
949               break;
950             bit=0;
951             byte=0;
952             for (x=0; x < (ssize_t) image->columns; x++)
953             {
954               byte<<=1;
955               if (GetPixelLuma(image,p) < (QuantumRange/2.0))
956                 byte|=0x01;
957               bit++;
958               if (bit == 8)
959                 {
960                   (void) WriteBlobByte(image,byte);
961                   bit=0;
962                   byte=0;
963                 }
964               p+=GetPixelChannels(image);
965             }
966             if (bit != 0)
967               (void) WriteBlobByte(image,(unsigned char) (byte << (8-bit)));
968             if ((((image->columns/8)+
969                 (image->columns % 8 ? 1 : 0)) % 2) != 0)
970               (void) WriteBlobByte(image,0);  /* pad scanline */
971             if (image->previous == (Image *) NULL)
972               {
973                 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
974                 image->rows);
975                 if (status == MagickFalse)
976                   break;
977               }
978           }
979         }
980       else
981         {
982           /*
983             Dump colormap to file.
984           */
985           for (i=0; i < (ssize_t) image->colors; i++)
986             (void) WriteBlobByte(image,ScaleQuantumToChar(
987               ClampToQuantum(image->colormap[i].red)));
988           for (i=0; i < (ssize_t) image->colors; i++)
989             (void) WriteBlobByte(image,ScaleQuantumToChar(
990               ClampToQuantum(image->colormap[i].green)));
991           for (i=0; i < (ssize_t) image->colors; i++)
992             (void) WriteBlobByte(image,ScaleQuantumToChar(
993               ClampToQuantum(image->colormap[i].blue)));
994           /*
995             Convert PseudoClass packet to SUN colormapped pixel.
996           */
997           for (y=0; y < (ssize_t) image->rows; y++)
998           {
999             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1000             if (p == (const Quantum *) NULL)
1001               break;
1002             for (x=0; x < (ssize_t) image->columns; x++)
1003             {
1004               (void) WriteBlobByte(image,(unsigned char)
1005                 GetPixelIndex(image,p));
1006               p+=GetPixelChannels(image);
1007             }
1008             if (image->columns & 0x01)
1009               (void) WriteBlobByte(image,0);  /* pad scanline */
1010             if (image->previous == (Image *) NULL)
1011               {
1012                 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1013                   image->rows);
1014                 if (status == MagickFalse)
1015                   break;
1016               }
1017           }
1018         }
1019     if (GetNextImageInList(image) == (Image *) NULL)
1020       break;
1021     image=SyncNextImageInList(image);
1022     status=SetImageProgress(image,SaveImagesTag,scene++,
1023       GetImageListLength(image));
1024     if (status == MagickFalse)
1025       break;
1026   } while (image_info->adjoin != MagickFalse);
1027   (void) CloseBlob(image);
1028   return(MagickTrue);
1029 }