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