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