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