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