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