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-2021 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://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 
39 /*
40   Include declarations.
41 */
42 #include "magick/studio.h"
43 #include "magick/attribute.h"
44 #include "magick/blob.h"
45 #include "magick/blob-private.h"
46 #include "magick/cache.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/colormap.h"
50 #include "magick/colormap-private.h"
51 #include "magick/colorspace.h"
52 #include "magick/colorspace-private.h"
53 #include "magick/exception.h"
54 #include "magick/exception-private.h"
55 #include "magick/image.h"
56 #include "magick/image-private.h"
57 #include "magick/list.h"
58 #include "magick/magick.h"
59 #include "magick/memory_.h"
60 #include "magick/memory-private.h"
61 #include "magick/monitor.h"
62 #include "magick/monitor-private.h"
63 #include "magick/pixel-accessor.h"
64 #include "magick/quantum-private.h"
65 #include "magick/static.h"
66 #include "magick/string_.h"
67 #include "magick/module.h"
68 
69 /*
70   Forward declarations.
71 */
72 static MagickBooleanType
73   WriteSUNImage(const ImageInfo *,Image *);
74 
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 */
IsSUN(const unsigned char * magick,const size_t length)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 
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 */
DecodeImage(const unsigned char * compressed_pixels,const size_t length,unsigned char * pixels,size_t extent)142 static MagickBooleanType DecodeImage(const unsigned char *compressed_pixels,
143   const size_t length,unsigned char *pixels,size_t extent)
144 {
145   const unsigned char
146     *p;
147 
148   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 
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 */
ReadSUNImage(const ImageInfo * image_info,ExceptionInfo * exception)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   IndexPacket
253     *indexes;
254 
255   PixelPacket
256     *q;
257 
258   ssize_t
259     i,
260     x;
261 
262   unsigned char
263     *p;
264 
265   size_t
266     bytes_per_line,
267     extent,
268     height,
269     pixels_length,
270     quantum;
271 
272   ssize_t
273     count,
274     y;
275 
276   SUNInfo
277     sun_info;
278 
279   unsigned char
280     *sun_data,
281     *sun_pixels;
282 
283   /*
284     Open image file.
285   */
286   assert(image_info != (const ImageInfo *) NULL);
287   assert(image_info->signature == MagickCoreSignature);
288   if (image_info->debug != MagickFalse)
289     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
290       image_info->filename);
291   assert(exception != (ExceptionInfo *) NULL);
292   assert(exception->signature == MagickCoreSignature);
293   image=AcquireImage(image_info);
294   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
295   if (status == MagickFalse)
296     {
297       image=DestroyImageList(image);
298       return((Image *) NULL);
299     }
300   /*
301     Read SUN raster header.
302   */
303   (void) memset(&sun_info,0,sizeof(sun_info));
304   sun_info.magic=ReadBlobMSBLong(image);
305   do
306   {
307     /*
308       Verify SUN identifier.
309     */
310     if (sun_info.magic != 0x59a66a95)
311       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
312     sun_info.width=ReadBlobMSBLong(image);
313     sun_info.height=ReadBlobMSBLong(image);
314     sun_info.depth=ReadBlobMSBLong(image);
315     sun_info.length=ReadBlobMSBLong(image);
316     sun_info.type=ReadBlobMSBLong(image);
317     sun_info.maptype=ReadBlobMSBLong(image);
318     sun_info.maplength=ReadBlobMSBLong(image);
319     if (sun_info.maplength > GetBlobSize(image))
320       ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
321     extent=(size_t) (sun_info.height*sun_info.width);
322     if ((sun_info.height != 0) && (sun_info.width != (extent/sun_info.height)))
323       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
324     if ((sun_info.type != RT_STANDARD) && (sun_info.type != RT_ENCODED) &&
325         (sun_info.type != RT_FORMAT_RGB))
326       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
327     if ((sun_info.maptype == RMT_NONE) && (sun_info.maplength != 0))
328       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
329     if ((sun_info.depth != 1) && (sun_info.depth != 8) &&
330         (sun_info.depth != 24) && (sun_info.depth != 32))
331       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
332     if ((sun_info.maptype != RMT_NONE) && (sun_info.maptype != RMT_EQUAL_RGB) &&
333         (sun_info.maptype != RMT_RAW))
334       ThrowReaderException(CoderError,"ColormapTypeNotSupported");
335     image->columns=sun_info.width;
336     image->rows=sun_info.height;
337     image->depth=sun_info.depth <= 8 ? sun_info.depth :
338       MAGICKCORE_QUANTUM_DEPTH;
339     if (sun_info.depth < 24)
340       {
341         size_t
342           one;
343 
344         image->colors=sun_info.maplength;
345         one=1;
346         if (sun_info.maptype == RMT_NONE)
347           image->colors=one << sun_info.depth;
348         if (sun_info.maptype == RMT_EQUAL_RGB)
349           image->colors=sun_info.maplength/3;
350         if (image->colors == 0)
351           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
352         if (AcquireImageColormap(image,image->colors) == MagickFalse)
353           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
354       }
355     switch (sun_info.maptype)
356     {
357       case RMT_NONE:
358         break;
359       case RMT_EQUAL_RGB:
360       {
361         unsigned char
362           *sun_colormap;
363 
364         /*
365           Read SUN raster colormap.
366         */
367         sun_colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
368           sizeof(*sun_colormap));
369         if (sun_colormap == (unsigned char *) NULL)
370           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
371         count=ReadBlob(image,image->colors,sun_colormap);
372         if (count != (ssize_t) image->colors)
373           {
374             sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
375             ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
376           }
377         for (i=0; i < (ssize_t) image->colors; i++)
378           image->colormap[i].red=ScaleCharToQuantum(sun_colormap[i]);
379         count=ReadBlob(image,image->colors,sun_colormap);
380         if (count != (ssize_t) image->colors)
381           {
382             sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
383             ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
384           }
385         for (i=0; i < (ssize_t) image->colors; i++)
386           image->colormap[i].green=ScaleCharToQuantum(sun_colormap[i]);
387         count=ReadBlob(image,image->colors,sun_colormap);
388         if (count != (ssize_t) image->colors)
389           {
390             sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
391             ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
392           }
393         for (i=0; i < (ssize_t) image->colors; i++)
394           image->colormap[i].blue=ScaleCharToQuantum(sun_colormap[i]);
395         sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
396         break;
397       }
398       case RMT_RAW:
399       {
400         unsigned char
401           *sun_colormap;
402 
403         /*
404           Read SUN raster colormap.
405         */
406         sun_colormap=(unsigned char *) AcquireQuantumMemory(sun_info.maplength,
407           sizeof(*sun_colormap));
408         if (sun_colormap == (unsigned char *) NULL)
409           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
410         count=ReadBlob(image,sun_info.maplength,sun_colormap);
411         sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
412         if (count != (ssize_t) sun_info.maplength)
413           ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
414         break;
415       }
416       default:
417         break;
418     }
419     image->matte=sun_info.depth == 32 ? MagickTrue : MagickFalse;
420     image->columns=sun_info.width;
421     image->rows=sun_info.height;
422     if (image_info->ping != MagickFalse)
423       {
424         (void) CloseBlob(image);
425         return(GetFirstImageInList(image));
426       }
427     status=SetImageExtent(image,image->columns,image->rows);
428     if (status == MagickFalse)
429       {
430         InheritException(exception,&image->exception);
431         return(DestroyImageList(image));
432       }
433     if (sun_info.length == 0)
434       ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
435     number_pixels=(MagickSizeType) (image->columns*image->rows);
436     if ((sun_info.type != RT_ENCODED) &&
437         ((number_pixels*sun_info.depth) > (8UL*sun_info.length)))
438       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
439     if (HeapOverflowSanityCheck(sun_info.width,sun_info.depth) != MagickFalse)
440       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
441     if (HeapOverflowSanityCheckGetSize(sun_info.width,sun_info.depth,&bytes_per_line) != MagickFalse)
442       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
443     if ((sun_info.type != RT_ENCODED) && (sun_info.length > GetBlobSize(image)))
444       ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
445     sun_data=(unsigned char *) AcquireQuantumMemory(sun_info.length,
446       sizeof(*sun_data));
447     if (sun_data == (unsigned char *) NULL)
448       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
449     (void) memset(sun_data,0,sun_info.length*sizeof(*sun_data));
450     count=(ssize_t) ReadBlob(image,sun_info.length,sun_data);
451     if ((sun_info.type != RT_ENCODED) && (count != (ssize_t) sun_info.length))
452       {
453         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
454         ThrowReaderException(CorruptImageError,"UnableToReadImageData");
455       }
456     height=sun_info.height;
457     if ((height == 0) || (sun_info.width == 0) || (sun_info.depth == 0) ||
458         ((bytes_per_line/sun_info.depth) != sun_info.width))
459       {
460         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
461         ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
462       }
463     quantum=sun_info.depth == 1 ? 15 : 7;
464     bytes_per_line+=quantum;
465     bytes_per_line<<=1;
466     if ((bytes_per_line >> 1) != ((size_t) sun_info.width*sun_info.depth+quantum))
467       {
468         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
469         ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
470       }
471     bytes_per_line>>=4;
472     if (HeapOverflowSanityCheck(height,bytes_per_line) != MagickFalse)
473       {
474         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
475         ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
476       }
477     pixels_length=height*bytes_per_line;
478     sun_pixels=(unsigned char *) AcquireQuantumMemory(pixels_length+image->rows,
479       sizeof(*sun_pixels));
480     if (sun_pixels == (unsigned char *) NULL)
481       {
482         sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
483         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
484       }
485     (void) memset(sun_pixels,0,(pixels_length+image->rows)*sizeof(*sun_pixels));
486     if (sun_info.type == RT_ENCODED)
487       {
488         status=DecodeImage(sun_data,sun_info.length,sun_pixels,pixels_length);
489         if (status == MagickFalse)
490           {
491             sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
492             sun_pixels=(unsigned char *) RelinquishMagickMemory(sun_pixels);
493             ThrowReaderException(CorruptImageError,"UnableToReadImageData");
494           }
495       }
496     else
497       {
498         if (EOFBlob(image) != MagickFalse)
499           {
500             ThrowFileException(exception,CorruptImageError,
501               "UnexpectedEndOfFile",image->filename);
502             break;
503           }
504         if (sun_info.length > (pixels_length+image->rows))
505           {
506             sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
507             sun_pixels=(unsigned char *) RelinquishMagickMemory(sun_pixels);
508             ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
509           }
510         (void) memcpy(sun_pixels,sun_data,sun_info.length);
511       }
512     sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
513     /*
514       Convert SUN raster image to pixel packets.
515     */
516     p=sun_pixels;
517     if (sun_info.depth == 1)
518       for (y=0; y < (ssize_t) image->rows; y++)
519       {
520         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
521         if (q == (PixelPacket *) NULL)
522           break;
523         indexes=GetAuthenticIndexQueue(image);
524         for (x=0; x < ((ssize_t) image->columns-7); x+=8)
525         {
526           for (bit=7; bit >= 0; bit--)
527             SetPixelIndex(indexes+x+7-bit,((*p) & (0x01 << bit) ? 0x00 : 0x01));
528           p++;
529         }
530         if ((image->columns % 8) != 0)
531           {
532             for (bit=7; bit >= (int) (8-(image->columns % 8)); bit--)
533               SetPixelIndex(indexes+x+7-bit,(*p) & (0x01 << bit) ? 0x00 : 0x01);
534             p++;
535           }
536         if ((((image->columns/8)+(image->columns % 8 ? 1 : 0)) % 2) != 0)
537           p++;
538         if (SyncAuthenticPixels(image,exception) == MagickFalse)
539           break;
540         if (image->previous == (Image *) NULL)
541           {
542             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
543               image->rows);
544             if (status == MagickFalse)
545               break;
546           }
547       }
548     else
549       if (image->storage_class == PseudoClass)
550         {
551           for (y=0; y < (ssize_t) image->rows; y++)
552           {
553             q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
554             if (q == (PixelPacket *) NULL)
555               break;
556             indexes=GetAuthenticIndexQueue(image);
557             for (x=0; x < (ssize_t) image->columns; x++)
558             {
559               SetPixelIndex(indexes+x,ConstrainColormapIndex(image,*p));
560               p++;
561             }
562             if ((image->columns % 2) != 0)
563               p++;
564             if (SyncAuthenticPixels(image,exception) == MagickFalse)
565               break;
566             if (image->previous == (Image *) NULL)
567               {
568                 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
569                 image->rows);
570                 if (status == MagickFalse)
571                   break;
572               }
573           }
574         }
575       else
576         {
577           size_t
578             bytes_per_pixel;
579 
580           bytes_per_pixel=3;
581           if (image->matte != MagickFalse)
582             bytes_per_pixel++;
583           for (y=0; y < (ssize_t) image->rows; y++)
584           {
585             q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
586             if (q == (PixelPacket *) NULL)
587               break;
588             for (x=0; x < (ssize_t) image->columns; x++)
589             {
590               if (image->matte != MagickFalse)
591                 SetPixelAlpha(q,ScaleCharToQuantum(*p++));
592               if (sun_info.type == RT_STANDARD)
593                 {
594                   SetPixelBlue(q,ScaleCharToQuantum(*p++));
595                   SetPixelGreen(q,ScaleCharToQuantum(*p++));
596                   SetPixelRed(q,ScaleCharToQuantum(*p++));
597                 }
598               else
599                 {
600                   SetPixelRed(q,ScaleCharToQuantum(*p++));
601                   SetPixelGreen(q,ScaleCharToQuantum(*p++));
602                   SetPixelBlue(q,ScaleCharToQuantum(*p++));
603                 }
604               if (image->colors != 0)
605                 {
606                   SetPixelRed(q,image->colormap[(ssize_t)
607                     GetPixelRed(q)].red);
608                   SetPixelGreen(q,image->colormap[(ssize_t)
609                     GetPixelGreen(q)].green);
610                   SetPixelBlue(q,image->colormap[(ssize_t)
611                     GetPixelBlue(q)].blue);
612                 }
613               q++;
614             }
615             if (((bytes_per_pixel*image->columns) % 2) != 0)
616               p++;
617             if (SyncAuthenticPixels(image,exception) == MagickFalse)
618               break;
619             if (image->previous == (Image *) NULL)
620               {
621                 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
622                 image->rows);
623                 if (status == MagickFalse)
624                   break;
625               }
626           }
627         }
628     if (image->storage_class == PseudoClass)
629       (void) SyncImage(image);
630     sun_pixels=(unsigned char *) RelinquishMagickMemory(sun_pixels);
631     /*
632       Proceed to next image.
633     */
634     if (image_info->number_scenes != 0)
635       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
636         break;
637     sun_info.magic=ReadBlobMSBLong(image);
638     if (sun_info.magic == 0x59a66a95)
639       {
640         /*
641           Allocate next image structure.
642         */
643         AcquireNextImage(image_info,image);
644         if (GetNextImageInList(image) == (Image *) NULL)
645           {
646             status=MagickFalse;
647             break;
648           }
649         image=SyncNextImageInList(image);
650         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
651           GetBlobSize(image));
652         if (status == MagickFalse)
653           break;
654       }
655   } while (sun_info.magic == 0x59a66a95);
656   (void) CloseBlob(image);
657   if (status == MagickFalse)
658     return(DestroyImageList(image));
659   return(GetFirstImageInList(image));
660 }
661 
662 /*
663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
664 %                                                                             %
665 %                                                                             %
666 %                                                                             %
667 %   R e g i s t e r S U N I m a g e                                           %
668 %                                                                             %
669 %                                                                             %
670 %                                                                             %
671 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
672 %
673 %  RegisterSUNImage() adds attributes for the SUN image format to
674 %  the list of supported formats.  The attributes include the image format
675 %  tag, a method to read and/or write the format, whether the format
676 %  supports the saving of more than one frame to the same file or blob,
677 %  whether the format supports native in-memory I/O, and a brief
678 %  description of the format.
679 %
680 %  The format of the RegisterSUNImage method is:
681 %
682 %      size_t RegisterSUNImage(void)
683 %
684 */
RegisterSUNImage(void)685 ModuleExport size_t RegisterSUNImage(void)
686 {
687   MagickInfo
688     *entry;
689 
690   entry=SetMagickInfo("RAS");
691   entry->decoder=(DecodeImageHandler *) ReadSUNImage;
692   entry->encoder=(EncodeImageHandler *) WriteSUNImage;
693   entry->magick=(IsImageFormatHandler *) IsSUN;
694   entry->description=ConstantString("SUN Rasterfile");
695   entry->seekable_stream=MagickTrue;
696   entry->magick_module=ConstantString("SUN");
697   (void) RegisterMagickInfo(entry);
698   entry=SetMagickInfo("SUN");
699   entry->decoder=(DecodeImageHandler *) ReadSUNImage;
700   entry->encoder=(EncodeImageHandler *) WriteSUNImage;
701   entry->description=ConstantString("SUN Rasterfile");
702   entry->seekable_stream=MagickTrue;
703   entry->magick_module=ConstantString("SUN");
704   (void) RegisterMagickInfo(entry);
705   return(MagickImageCoderSignature);
706 }
707 
708 /*
709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
710 %                                                                             %
711 %                                                                             %
712 %                                                                             %
713 %   U n r e g i s t e r S U N I m a g e                                       %
714 %                                                                             %
715 %                                                                             %
716 %                                                                             %
717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
718 %
719 %  UnregisterSUNImage() removes format registrations made by the
720 %  SUN module from the list of supported formats.
721 %
722 %  The format of the UnregisterSUNImage method is:
723 %
724 %      UnregisterSUNImage(void)
725 %
726 */
UnregisterSUNImage(void)727 ModuleExport void UnregisterSUNImage(void)
728 {
729   (void) UnregisterMagickInfo("RAS");
730   (void) UnregisterMagickInfo("SUN");
731 }
732 
733 /*
734 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
735 %                                                                             %
736 %                                                                             %
737 %                                                                             %
738 %   W r i t e S U N I m a g e                                                 %
739 %                                                                             %
740 %                                                                             %
741 %                                                                             %
742 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
743 %
744 %  WriteSUNImage() writes an image in the SUN rasterfile format.
745 %
746 %  The format of the WriteSUNImage method is:
747 %
748 %      MagickBooleanType WriteSUNImage(const ImageInfo *image_info,Image *image)
749 %
750 %  A description of each parameter follows.
751 %
752 %    o image_info: the image info.
753 %
754 %    o image:  The image.
755 %
756 */
WriteSUNImage(const ImageInfo * image_info,Image * image)757 static MagickBooleanType WriteSUNImage(const ImageInfo *image_info,Image *image)
758 {
759 #define RMT_EQUAL_RGB  1
760 #define RMT_NONE  0
761 #define RMT_RAW  2
762 #define RT_STANDARD  1
763 #define RT_FORMAT_RGB  3
764 
765   typedef struct _SUNInfo
766   {
767     unsigned int
768       magic,
769       width,
770       height,
771       depth,
772       length,
773       type,
774       maptype,
775       maplength;
776   } SUNInfo;
777 
778   MagickBooleanType
779     status;
780 
781   MagickOffsetType
782     scene;
783 
784   MagickSizeType
785     number_pixels;
786 
787   const IndexPacket
788     *indexes;
789 
790   const PixelPacket
791     *p;
792 
793   ssize_t
794     i,
795     x;
796 
797   size_t
798     imageListLength;
799 
800   ssize_t
801     y;
802 
803   SUNInfo
804     sun_info;
805 
806   /*
807     Open output image file.
808   */
809   assert(image_info != (const ImageInfo *) NULL);
810   assert(image_info->signature == MagickCoreSignature);
811   assert(image != (Image *) NULL);
812   assert(image->signature == MagickCoreSignature);
813   if (image->debug != MagickFalse)
814     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
815   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
816   if (status == MagickFalse)
817     return(status);
818   scene=0;
819   imageListLength=GetImageListLength(image);
820   do
821   {
822     /*
823       Initialize SUN raster file header.
824     */
825     if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
826       (void) TransformImageColorspace(image,sRGBColorspace);
827     sun_info.magic=0x59a66a95;
828     if ((image->columns != (unsigned int) image->columns) ||
829         (image->rows != (unsigned int) image->rows))
830       ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
831     sun_info.width=(unsigned int) image->columns;
832     sun_info.height=(unsigned int) image->rows;
833     sun_info.type=(unsigned int) (image->storage_class == DirectClass ?
834       RT_FORMAT_RGB : RT_STANDARD);
835     sun_info.maptype=RMT_NONE;
836     sun_info.maplength=0;
837     number_pixels=(MagickSizeType) image->columns*image->rows;
838     if ((4*number_pixels) != (size_t) (4*number_pixels))
839       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
840     if (image->storage_class == DirectClass)
841       {
842         /*
843           Full color SUN raster.
844         */
845         sun_info.depth=(unsigned int) image->matte ? 32U : 24U;
846         sun_info.length=(unsigned int) ((image->matte ? 4 : 3)*number_pixels);
847         sun_info.length+=sun_info.length & 0x01 ? (unsigned int) image->rows :
848           0;
849       }
850     else
851       if (SetImageMonochrome(image,&image->exception))
852         {
853           /*
854             Monochrome SUN raster.
855           */
856           sun_info.depth=1;
857           sun_info.length=(unsigned int) (((image->columns+7) >> 3)*
858             image->rows);
859           sun_info.length+=(unsigned int) (((image->columns/8)+(image->columns %
860             8 ? 1 : 0)) % 2 ? image->rows : 0);
861         }
862       else
863         {
864           /*
865             Colormapped SUN raster.
866           */
867           sun_info.depth=8;
868           sun_info.length=(unsigned int) number_pixels;
869           sun_info.length+=(unsigned int) (image->columns & 0x01 ? image->rows :
870             0);
871           sun_info.maptype=RMT_EQUAL_RGB;
872           sun_info.maplength=(unsigned int) (3*image->colors);
873         }
874     /*
875       Write SUN header.
876     */
877     (void) WriteBlobMSBLong(image,sun_info.magic);
878     (void) WriteBlobMSBLong(image,sun_info.width);
879     (void) WriteBlobMSBLong(image,sun_info.height);
880     (void) WriteBlobMSBLong(image,sun_info.depth);
881     (void) WriteBlobMSBLong(image,sun_info.length);
882     (void) WriteBlobMSBLong(image,sun_info.type);
883     (void) WriteBlobMSBLong(image,sun_info.maptype);
884     (void) WriteBlobMSBLong(image,sun_info.maplength);
885     /*
886       Convert MIFF to SUN raster pixels.
887     */
888     x=0;
889     y=0;
890     if (image->storage_class == DirectClass)
891       {
892         unsigned char
893           *q;
894 
895         size_t
896           bytes_per_pixel,
897           length;
898 
899         unsigned char
900           *pixels;
901 
902         /*
903           Allocate memory for pixels.
904         */
905         bytes_per_pixel=3;
906         if (image->matte != MagickFalse)
907           bytes_per_pixel++;
908         length=image->columns;
909         pixels=(unsigned char *) AcquireQuantumMemory(length,4*sizeof(*pixels));
910         if (pixels == (unsigned char *) NULL)
911           ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
912         /*
913           Convert DirectClass packet to SUN RGB pixel.
914         */
915         for (y=0; y < (ssize_t) image->rows; y++)
916         {
917           p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
918           if (p == (const PixelPacket *) NULL)
919             break;
920           q=pixels;
921           for (x=0; x < (ssize_t) image->columns; x++)
922           {
923             if (image->matte != MagickFalse)
924               *q++=ScaleQuantumToChar(GetPixelAlpha(p));
925             *q++=ScaleQuantumToChar(GetPixelRed(p));
926             *q++=ScaleQuantumToChar(GetPixelGreen(p));
927             *q++=ScaleQuantumToChar(GetPixelBlue(p));
928             p++;
929           }
930           if (((bytes_per_pixel*image->columns) & 0x01) != 0)
931             *q++='\0';  /* pad scanline */
932           (void) WriteBlob(image,(size_t) (q-pixels),pixels);
933           if (image->previous == (Image *) NULL)
934             {
935               status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
936                 image->rows);
937               if (status == MagickFalse)
938                 break;
939             }
940         }
941         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
942       }
943     else
944       if (SetImageMonochrome(image,&image->exception))
945         {
946           unsigned char
947             bit,
948             byte;
949 
950           /*
951             Convert PseudoClass image to a SUN monochrome image.
952           */
953           (void) SetImageType(image,BilevelType);
954           for (y=0; y < (ssize_t) image->rows; y++)
955           {
956             p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
957             if (p == (const PixelPacket *) NULL)
958               break;
959             indexes=GetVirtualIndexQueue(image);
960             bit=0;
961             byte=0;
962             for (x=0; x < (ssize_t) image->columns; x++)
963             {
964               byte<<=1;
965               if (GetPixelLuma(image,p) < (QuantumRange/2.0))
966                 byte|=0x01;
967               bit++;
968               if (bit == 8)
969                 {
970                   (void) WriteBlobByte(image,byte);
971                   bit=0;
972                   byte=0;
973                 }
974               p++;
975             }
976             if (bit != 0)
977               (void) WriteBlobByte(image,(unsigned char) (byte << (8-bit)));
978             if ((((image->columns/8)+
979                 (image->columns % 8 ? 1 : 0)) % 2) != 0)
980               (void) WriteBlobByte(image,0);  /* pad scanline */
981             if (image->previous == (Image *) NULL)
982               {
983                 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
984                 image->rows);
985                 if (status == MagickFalse)
986                   break;
987               }
988           }
989         }
990       else
991         {
992           /*
993             Dump colormap to file.
994           */
995           for (i=0; i < (ssize_t) image->colors; i++)
996             (void) WriteBlobByte(image,ScaleQuantumToChar(
997               image->colormap[i].red));
998           for (i=0; i < (ssize_t) image->colors; i++)
999             (void) WriteBlobByte(image,ScaleQuantumToChar(
1000               image->colormap[i].green));
1001           for (i=0; i < (ssize_t) image->colors; i++)
1002             (void) WriteBlobByte(image,ScaleQuantumToChar(
1003               image->colormap[i].blue));
1004           /*
1005             Convert PseudoClass packet to SUN colormapped pixel.
1006           */
1007           for (y=0; y < (ssize_t) image->rows; y++)
1008           {
1009             p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1010             if (p == (const PixelPacket *) NULL)
1011               break;
1012             indexes=GetVirtualIndexQueue(image);
1013             for (x=0; x < (ssize_t) image->columns; x++)
1014             {
1015               (void) WriteBlobByte(image,(unsigned char)
1016                 GetPixelIndex(indexes+x));
1017               p++;
1018             }
1019             if (image->columns & 0x01)
1020               (void) WriteBlobByte(image,0);  /* pad scanline */
1021             if (image->previous == (Image *) NULL)
1022               {
1023                 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1024                   image->rows);
1025                 if (status == MagickFalse)
1026                   break;
1027               }
1028           }
1029         }
1030     if (GetNextImageInList(image) == (Image *) NULL)
1031       break;
1032     image=SyncNextImageInList(image);
1033     status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
1034     if (status == MagickFalse)
1035       break;
1036   } while (image_info->adjoin != MagickFalse);
1037   (void) CloseBlob(image);
1038   return(MagickTrue);
1039 }
1040