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