blob: f816360a0361dcc43c84c187b41594ca9af802e8 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% SSSSS GGGG IIIII %
7% SS G I %
8% SSS G GG I %
9% SS G G I %
10% SSSSS GGG IIIII %
11% %
12% %
13% Read/Write Irix RGB 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/property.h"
59#include "magick/quantum-private.h"
60#include "magick/static.h"
61#include "magick/string_.h"
62#include "magick/module.h"
63
64/*
65 Typedef declaractions.
66*/
67typedef struct _SGIInfo
68{
69 unsigned short
70 magic;
71
72 unsigned char
73 storage,
74 bytes_per_pixel;
75
76 unsigned short
77 dimension,
78 columns,
79 rows,
80 depth;
81
82 unsigned long
83 minimum_value,
84 maximum_value,
85 sans;
86
87 char
88 name[80];
89
90 unsigned long
91 pixel_format;
92
93 unsigned char
94 filler[404];
95} SGIInfo;
96
97/*
98 Forward declarations.
99*/
100static MagickBooleanType
101 WriteSGIImage(const ImageInfo *,Image *);
102/*
103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
104% %
105% %
106% %
107% I s S G I %
108% %
109% %
110% %
111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112%
113% IsSGI() returns MagickTrue if the image format type, identified by the
114% magick string, is SGI.
115%
116% The format of the IsSGI method is:
117%
118% MagickBooleanType IsSGI(const unsigned char *magick,const size_t length)
119%
120% A description of each parameter follows:
121%
122% o magick: compare image format pattern against these bytes.
123%
124% o length: Specifies the length of the magick string.
125%
126%
127*/
128static MagickBooleanType IsSGI(const unsigned char *magick,const size_t length)
129{
130 if (length < 2)
131 return(MagickFalse);
132 if (memcmp(magick,"\001\332",2) == 0)
133 return(MagickTrue);
134 return(MagickFalse);
135}
136
137/*
138%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
139% %
140% %
141% %
142% R e a d S G I I m a g e %
143% %
144% %
145% %
146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147%
148% ReadSGIImage() reads a SGI RGB image file and returns it. It
149% allocates the memory necessary for the new Image structure and returns a
150% pointer to the new image.
151%
152% The format of the ReadSGIImage method is:
153%
154% Image *ReadSGIImage(const ImageInfo *image_info,ExceptionInfo *exception)
155%
156% A description of each parameter follows:
157%
158% o image_info: the image info.
159%
160% o exception: return any errors or warnings in this structure.
161%
162*/
163
164static inline size_t MagickMin(const size_t x,const size_t y)
165{
166 if (x < y)
167 return(x);
168 return(y);
169}
170
171static MagickBooleanType SGIDecode(const size_t bytes_per_pixel,
172 long number_packets,unsigned char *packets,long number_pixels,
173 unsigned char *pixels)
174{
175 register unsigned char
176 *p,
177 *q;
178
179 ssize_t
180 count;
181
182 unsigned long
183 pixel;
184
185 p=packets;
186 q=pixels;
187 if (bytes_per_pixel == 2)
188 {
189 for ( ; number_pixels > 0; )
190 {
191 if (number_packets-- == 0)
192 return(MagickFalse);
193 pixel=(unsigned long) (*p++) << 8;
194 pixel|=(*p++);
195 count=(ssize_t) (pixel & 0x7f);
196 if (count == 0)
197 break;
198 if (count > (ssize_t) number_pixels)
199 return(MagickFalse);
200 number_pixels-=count;
201 if ((pixel & 0x80) != 0)
202 for ( ; count != 0; count--)
203 {
204 if (number_packets-- == 0)
205 return(MagickFalse);
206 *q=(*p++);
207 *(q+1)=(*p++);
208 q+=8;
209 }
210 else
211 {
212 pixel=(unsigned long) (*p++) << 8;
213 pixel|=(*p++);
214 for ( ; count != 0; count--)
215 {
216 if (number_packets-- == 0)
217 return(MagickFalse);
218 *q=(unsigned char) (pixel >> 8);
219 *(q+1)=(unsigned char) pixel;
220 q+=8;
221 }
222 }
223 }
224 return(MagickTrue);
225 }
226 for ( ; number_pixels > 0; )
227 {
228 if (number_packets-- == 0)
229 return(MagickFalse);
230 pixel=(unsigned long) (*p++);
231 count=(ssize_t) (pixel & 0x7f);
232 if (count == 0)
233 break;
234 if (count > (ssize_t) number_pixels)
235 return(MagickFalse);
236 number_pixels-=count;
237 if ((pixel & 0x80) != 0)
238 for ( ; count != 0; count--)
239 {
240 if (number_packets-- == 0)
241 return(MagickFalse);
242 *q=(*p++);
243 q+=4;
244 }
245 else
246 {
247 if (number_packets-- == 0)
248 return(MagickFalse);
249 pixel=(unsigned long) (*p++);
250 for ( ; count != 0; count--)
251 {
252 *q=(unsigned char) pixel;
253 q+=4;
254 }
255 }
256 }
257 return(MagickTrue);
258}
259
260static Image *ReadSGIImage(const ImageInfo *image_info,ExceptionInfo *exception)
261{
262 Image
263 *image;
264
265 long
266 y,
267 z;
268
269 MagickBooleanType
270 status;
271
272 MagickSizeType
273 number_pixels;
274
275 register IndexPacket
276 *indexes;
277
278 register long
279 i,
280 x;
281
282 register PixelPacket
283 *q;
284
285 register unsigned char
286 *p;
287
288 ssize_t
289 count;
290
291 SGIInfo
292 iris_info;
293
294 size_t
295 bytes_per_pixel;
296
297 unsigned char
298 *iris_pixels;
299
300 unsigned long
301 quantum;
302
303 /*
304 Open image file.
305 */
306 assert(image_info != (const ImageInfo *) NULL);
307 assert(image_info->signature == MagickSignature);
308 if (image_info->debug != MagickFalse)
309 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
310 image_info->filename);
311 assert(exception != (ExceptionInfo *) NULL);
312 assert(exception->signature == MagickSignature);
313 image=AcquireImage(image_info);
314 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
315 if (status == MagickFalse)
316 {
317 image=DestroyImageList(image);
318 return((Image *) NULL);
319 }
320 /*
321 Read SGI raster header.
322 */
323 iris_info.magic=ReadBlobMSBShort(image);
324 do
325 {
326 /*
327 Verify SGI identifier.
328 */
329 if (iris_info.magic != 0x01DA)
330 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
331 iris_info.storage=(unsigned char) ReadBlobByte(image);
332 switch (iris_info.storage)
333 {
334 case 0x00: image->compression=NoCompression; break;
335 case 0x01: image->compression=RLECompression; break;
336 default:
337 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
338 }
339 iris_info.bytes_per_pixel=(unsigned char) ReadBlobByte(image);
340 if ((iris_info.bytes_per_pixel == 0) || (iris_info.bytes_per_pixel > 2))
341 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
342 iris_info.dimension=ReadBlobMSBShort(image);
343 iris_info.columns=ReadBlobMSBShort(image);
344 iris_info.rows=ReadBlobMSBShort(image);
345 iris_info.depth=ReadBlobMSBShort(image);
346 if ((iris_info.depth == 0) || (iris_info.depth > 4))
347 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
348 iris_info.minimum_value=ReadBlobMSBLong(image);
349 iris_info.maximum_value=ReadBlobMSBLong(image);
350 iris_info.sans=ReadBlobMSBLong(image);
351 (void) ReadBlob(image,sizeof(iris_info.name),(unsigned char *)
352 iris_info.name);
353 iris_info.name[sizeof(iris_info.name)-1]='\0';
354 if (*iris_info.name != '\0')
355 (void) SetImageProperty(image,"label",iris_info.name);
356 iris_info.pixel_format=ReadBlobMSBLong(image);
357 if (iris_info.pixel_format != 0)
358 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
359 count=ReadBlob(image,sizeof(iris_info.filler),iris_info.filler);
360 image->columns=iris_info.columns;
361 image->rows=iris_info.rows;
362 image->depth=(unsigned long) MagickMin(iris_info.depth,MAGICKCORE_QUANTUM_DEPTH);
363 if (iris_info.pixel_format == 0)
364 image->depth=(unsigned long) MagickMin((size_t) 8*
365 iris_info.bytes_per_pixel,MAGICKCORE_QUANTUM_DEPTH);
366 if (iris_info.depth < 3)
367 {
368 image->storage_class=PseudoClass;
369 image->colors=256;
370 }
371 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
372 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
373 break;
374 /*
375 Allocate SGI pixels.
376 */
377 bytes_per_pixel=(size_t) iris_info.bytes_per_pixel;
378 number_pixels=(MagickSizeType) iris_info.columns*iris_info.rows;
379 if ((4*bytes_per_pixel*number_pixels) != ((MagickSizeType) (size_t)
380 (4*bytes_per_pixel*number_pixels)))
381 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
382 iris_pixels=(unsigned char *) AcquireQuantumMemory(iris_info.columns,
383 iris_info.rows*4*bytes_per_pixel*sizeof(*iris_pixels));
384 if (iris_pixels == (unsigned char *) NULL)
385 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
386 if ((int) iris_info.storage != 0x01)
387 {
388 unsigned char
389 *scanline;
390
391 /*
392 Read standard image format.
393 */
394 scanline=(unsigned char *) AcquireQuantumMemory(iris_info.columns,
395 bytes_per_pixel*sizeof(*scanline));
396 if (scanline == (unsigned char *) NULL)
397 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
398 for (z=0; z < (long) iris_info.depth; z++)
399 {
400 p=iris_pixels+bytes_per_pixel*z;
401 for (y=0; y < (long) iris_info.rows; y++)
402 {
403 count=ReadBlob(image,bytes_per_pixel*iris_info.columns,scanline);
404 if (EOFBlob(image) != MagickFalse)
405 break;
406 if (bytes_per_pixel == 2)
407 for (x=0; x < (long) iris_info.columns; x++)
408 {
409 *p=scanline[2*x];
410 *(p+1)=scanline[2*x+1];
411 p+=8;
412 }
413 else
414 for (x=0; x < (long) iris_info.columns; x++)
415 {
416 *p=scanline[x];
417 p+=4;
418 }
419 }
420 }
421 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
422 }
423 else
424 {
425 ssize_t
426 offset,
427 *offsets;
428
429 unsigned char
430 *packets;
431
432 unsigned int
433 data_order;
434
435 unsigned long
436 *runlength;
437
438 /*
439 Read runlength-encoded image format.
440 */
441 offsets=(ssize_t *) AcquireQuantumMemory((size_t) iris_info.rows,
442 iris_info.depth*sizeof(*offsets));
443 packets=(unsigned char *) AcquireQuantumMemory((size_t)
444 iris_info.columns+10UL,4UL*sizeof(*packets));
445 runlength=(unsigned long *) AcquireQuantumMemory(iris_info.rows,
446 iris_info.depth*sizeof(*runlength));
447 if ((offsets == (ssize_t *) NULL) ||
448 (packets == (unsigned char *) NULL) ||
449 (runlength == (unsigned long *) NULL))
450 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
451 for (i=0; i < (long) (iris_info.rows*iris_info.depth); i++)
452 offsets[i]=(ssize_t) ReadBlobMSBLong(image);
453 for (i=0; i < (long) (iris_info.rows*iris_info.depth); i++)
454 {
455 runlength[i]=ReadBlobMSBLong(image);
456 if (runlength[i] > (4*(size_t) iris_info.columns+10))
457 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
458 }
459 /*
460 Check data order.
461 */
462 offset=0;
463 data_order=0;
464 for (y=0; ((y < (long) iris_info.rows) && (data_order == 0)); y++)
465 for (z=0; ((z < (long) iris_info.depth) && (data_order == 0)); z++)
466 {
467 if (offsets[y+z*iris_info.rows] < offset)
468 data_order=1;
469 offset=offsets[y+z*iris_info.rows];
470 }
471 offset=(ssize_t) TellBlob(image);
472 if (data_order == 1)
473 {
474 for (z=0; z < (long) iris_info.depth; z++)
475 {
476 p=iris_pixels;
477 for (y=0; y < (long) iris_info.rows; y++)
478 {
479 if (offset != offsets[y+z*iris_info.rows])
480 {
481 offset=offsets[y+z*iris_info.rows];
482 offset=(ssize_t) SeekBlob(image,(long) offset,SEEK_SET);
483 }
484 count=ReadBlob(image,(size_t) runlength[y+z*iris_info.rows],
485 packets);
486 if (EOFBlob(image) != MagickFalse)
487 break;
488 offset+=runlength[y+z*iris_info.rows];
489 status=SGIDecode(bytes_per_pixel,(long)
490 (runlength[y+z*iris_info.rows]/bytes_per_pixel),packets,
491 1L*iris_info.columns,p+bytes_per_pixel*z);
492 if (status == MagickFalse)
493 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
494 p+=(iris_info.columns*4*bytes_per_pixel);
495 }
496 }
497 }
498 else
499 {
500 MagickOffsetType
501 position;
502
503 position=TellBlob(image);
504 p=iris_pixels;
505 for (y=0; y < (long) iris_info.rows; y++)
506 {
507 for (z=0; z < (long) iris_info.depth; z++)
508 {
509 if (offset != offsets[y+z*iris_info.rows])
510 {
511 offset=offsets[y+z*iris_info.rows];
512 offset=(ssize_t) SeekBlob(image,(long) offset,SEEK_SET);
513 }
514 count=ReadBlob(image,(size_t) runlength[y+z*iris_info.rows],
515 packets);
516 if (EOFBlob(image) != MagickFalse)
517 break;
518 offset+=runlength[y+z*iris_info.rows];
519 status=SGIDecode(bytes_per_pixel,(long)
520 (runlength[y+z*iris_info.rows]/bytes_per_pixel),packets,
521 1L*iris_info.columns,p+bytes_per_pixel*z);
522 if (status == MagickFalse)
523 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
524 }
525 p+=(iris_info.columns*4*bytes_per_pixel);
526 }
527 offset=(ssize_t) SeekBlob(image,position,SEEK_SET);
528 }
529 runlength=(unsigned long *) RelinquishMagickMemory(runlength);
530 packets=(unsigned char *) RelinquishMagickMemory(packets);
531 offsets=(ssize_t *) RelinquishMagickMemory(offsets);
532 }
533 /*
534 Initialize image structure.
535 */
536 image->matte=iris_info.depth == 4 ? MagickTrue : MagickFalse;
537 image->columns=iris_info.columns;
538 image->rows=iris_info.rows;
539 /*
540 Convert SGI raster image to pixel packets.
541 */
542 if (image->storage_class == DirectClass)
543 {
544 /*
545 Convert SGI image to DirectClass pixel packets.
546 */
547 if (bytes_per_pixel == 2)
548 {
549 for (y=0; y < (long) image->rows; y++)
550 {
551 p=iris_pixels+(image->rows-y-1)*8*image->columns;
552 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
553 if (q == (PixelPacket *) NULL)
554 break;
555 for (x=0; x < (long) image->columns; x++)
556 {
557 q->red=ScaleShortToQuantum((unsigned short)
558 ((*(p+0) << 8) | (*(p+1))));
559 q->green=ScaleShortToQuantum((unsigned short)
560 ((*(p+2) << 8) | (*(p+3))));
561 q->blue=ScaleShortToQuantum((unsigned short)
562 ((*(p+4) << 8) | (*(p+5))));
cristyce70c172010-01-07 17:15:30 +0000563 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +0000564 if (image->matte != MagickFalse)
565 q->opacity=(Quantum) (QuantumRange-ScaleShortToQuantum(
566 (unsigned short) ((*(p+6) << 8) | (*(p+7)))));
567 p+=8;
568 q++;
569 }
570 if (SyncAuthenticPixels(image,exception) == MagickFalse)
571 break;
572 if (image->previous == (Image *) NULL)
573 {
574 status=SetImageProgress(image,LoadImageTag,y,image->rows);
575 if (status == MagickFalse)
576 break;
577 }
578 }
579 }
580 else
581 for (y=0; y < (long) image->rows; y++)
582 {
583 p=iris_pixels+(image->rows-y-1)*4*image->columns;
584 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
585 if (q == (PixelPacket *) NULL)
586 break;
587 for (x=0; x < (long) image->columns; x++)
588 {
589 q->red=ScaleCharToQuantum(*p);
590 q->green=ScaleCharToQuantum(*(p+1));
591 q->blue=ScaleCharToQuantum(*(p+2));
cristyce70c172010-01-07 17:15:30 +0000592 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +0000593 if (image->matte != MagickFalse)
594 q->opacity=(Quantum) (QuantumRange-ScaleCharToQuantum(*(p+3)));
595 p+=4;
596 q++;
597 }
598 if (SyncAuthenticPixels(image,exception) == MagickFalse)
599 break;
600 if (image->previous == (Image *) NULL)
601 {
602 status=SetImageProgress(image,LoadImageTag,y,image->rows);
603 if (status == MagickFalse)
604 break;
605 }
606 }
607 }
608 else
609 {
610 /*
611 Create grayscale map.
612 */
613 if (AcquireImageColormap(image,image->colors) == MagickFalse)
614 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
615 /*
616 Convert SGI image to PseudoClass pixel packets.
617 */
618 if (bytes_per_pixel == 2)
619 {
620 for (y=0; y < (long) image->rows; y++)
621 {
622 p=iris_pixels+(image->rows-y-1)*8*image->columns;
623 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
624 if (q == (PixelPacket *) NULL)
625 break;
626 indexes=GetAuthenticIndexQueue(image);
627 for (x=0; x < (long) image->columns; x++)
628 {
629 quantum=(*p << 8);
630 quantum|=(*(p+1));
631 indexes[x]=(IndexPacket) quantum;
632 p+=8;
633 q++;
634 }
635 if (SyncAuthenticPixels(image,exception) == MagickFalse)
636 break;
637 if (image->previous == (Image *) NULL)
638 {
639 status=SetImageProgress(image,LoadImageTag,y,image->rows);
640 if (status == MagickFalse)
641 break;
642 }
643 }
644 }
645 else
646 for (y=0; y < (long) image->rows; y++)
647 {
648 p=iris_pixels+(image->rows-y-1)*4*image->columns;
649 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
650 if (q == (PixelPacket *) NULL)
651 break;
652 indexes=GetAuthenticIndexQueue(image);
653 for (x=0; x < (long) image->columns; x++)
654 {
655 indexes[x]=(IndexPacket) (*p);
656 p+=4;
657 q++;
658 }
659 if (SyncAuthenticPixels(image,exception) == MagickFalse)
660 break;
661 if (image->previous == (Image *) NULL)
662 {
663 status=SetImageProgress(image,LoadImageTag,y,image->rows);
664 if (status == MagickFalse)
665 break;
666 }
667 }
668 (void) SyncImage(image);
669 }
670 iris_pixels=(unsigned char *) RelinquishMagickMemory(iris_pixels);
671 if (EOFBlob(image) != MagickFalse)
672 {
673 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
674 image->filename);
675 break;
676 }
677 /*
678 Proceed to next image.
679 */
680 if (image_info->number_scenes != 0)
681 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
682 break;
683 iris_info.magic=ReadBlobMSBShort(image);
684 if (iris_info.magic == 0x01DA)
685 {
686 /*
687 Allocate next image structure.
688 */
689 AcquireNextImage(image_info,image);
690 if (GetNextImageInList(image) == (Image *) NULL)
691 {
692 image=DestroyImageList(image);
693 return((Image *) NULL);
694 }
695 image=SyncNextImageInList(image);
696 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
697 GetBlobSize(image));
698 if (status == MagickFalse)
699 break;
700 }
701 } while (iris_info.magic == 0x01DA);
702 (void) CloseBlob(image);
703 return(GetFirstImageInList(image));
704}
705
706/*
707%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
708% %
709% %
710% %
711% R e g i s t e r S G I I m a g e %
712% %
713% %
714% %
715%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
716%
717% RegisterSGIImage() adds properties for the SGI image format to
718% the list of supported formats. The properties include the image format
719% tag, a method to read and/or write the format, whether the format
720% supports the saving of more than one frame to the same file or blob,
721% whether the format supports native in-memory I/O, and a brief
722% description of the format.
723%
724% The format of the RegisterSGIImage method is:
725%
726% unsigned long RegisterSGIImage(void)
727%
728*/
729ModuleExport unsigned long RegisterSGIImage(void)
730{
731 MagickInfo
732 *entry;
733
734 entry=SetMagickInfo("SGI");
735 entry->decoder=(DecodeImageHandler *) ReadSGIImage;
736 entry->encoder=(EncodeImageHandler *) WriteSGIImage;
737 entry->magick=(IsImageFormatHandler *) IsSGI;
738 entry->description=ConstantString("Irix RGB image");
739 entry->module=ConstantString("SGI");
740 entry->seekable_stream=MagickTrue;
741 (void) RegisterMagickInfo(entry);
742 return(MagickImageCoderSignature);
743}
744
745/*
746%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
747% %
748% %
749% %
750% U n r e g i s t e r S G I I m a g e %
751% %
752% %
753% %
754%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
755%
756% UnregisterSGIImage() removes format registrations made by the
757% SGI module from the list of supported formats.
758%
759% The format of the UnregisterSGIImage method is:
760%
761% UnregisterSGIImage(void)
762%
763*/
764ModuleExport void UnregisterSGIImage(void)
765{
766 (void) UnregisterMagickInfo("SGI");
767}
768
769/*
770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771% %
772% %
773% %
774% W r i t e S G I I m a g e %
775% %
776% %
777% %
778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
779%
780% WriteSGIImage() writes an image in SGI RGB encoded image format.
781%
782% The format of the WriteSGIImage method is:
783%
784% MagickBooleanType WriteSGIImage(const ImageInfo *image_info,Image *image)
785%
786% A description of each parameter follows.
787%
788% o image_info: the image info.
789%
790% o image: The image.
791%
792*/
793
794static size_t SGIEncode(unsigned char *pixels,size_t length,
795 unsigned char *packets)
796{
797 short
798 runlength;
799
800 register unsigned char
801 *p,
802 *q;
803
804 unsigned char
805 *limit,
806 *mark;
807
808 p=pixels;
809 limit=p+length*4;
810 q=packets;
811 while (p < limit)
812 {
813 mark=p;
814 p+=8;
815 while ((p < limit) && ((*(p-8) != *(p-4)) || (*(p-4) != *p)))
816 p+=4;
817 p-=8;
818 length=(size_t) (p-mark) >> 2;
819 while (length != 0)
820 {
821 runlength=(short) (length > 126 ? 126 : length);
822 length-=runlength;
823 *q++=(unsigned char) (0x80 | runlength);
824 for ( ; runlength > 0; runlength--)
825 {
826 *q++=(*mark);
827 mark+=4;
828 }
829 }
830 mark=p;
831 p+=4;
832 while ((p < limit) && (*p == *mark))
833 p+=4;
834 length=(size_t) (p-mark) >> 2;
835 while (length != 0)
836 {
837 runlength=(short) (length > 126 ? 126 : length);
838 length-=runlength;
839 *q++=(unsigned char) runlength;
840 *q++=(*mark);
841 }
842 }
843 *q++='\0';
844 return((size_t) (q-packets));
845}
846
847static MagickBooleanType WriteSGIImage(const ImageInfo *image_info,Image *image)
848{
849 CompressionType
850 compression;
851
852 const char
853 *value;
854
855 long
856 y,
857 z;
858
859 MagickBooleanType
860 status;
861
862 MagickOffsetType
863 scene;
864
865 MagickSizeType
866 number_pixels;
867
868 SGIInfo
869 iris_info;
870
871 register const PixelPacket
872 *p;
873
874 register long
875 i,
876 x;
877
878 register unsigned char
879 *q;
880
881 unsigned char
882 *iris_pixels,
883 *packets;
884
885 /*
886 Open output image file.
887 */
888 assert(image_info != (const ImageInfo *) NULL);
889 assert(image_info->signature == MagickSignature);
890 assert(image != (Image *) NULL);
891 assert(image->signature == MagickSignature);
892 if (image->debug != MagickFalse)
893 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
894 if ((image->columns > 65535UL) || (image->rows > 65535UL))
895 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
896 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
897 if (status == MagickFalse)
898 return(status);
899 scene=0;
900 do
901 {
902 /*
903 Initialize SGI raster file header.
904 */
905 if (image->colorspace != RGBColorspace)
906 (void) TransformImageColorspace(image,RGBColorspace);
907 (void) ResetMagickMemory(&iris_info,0,sizeof(iris_info));
908 iris_info.magic=0x01DA;
909 compression=image->compression;
910 if (image_info->compression != UndefinedCompression)
911 compression=image_info->compression;
912 if (image->depth > 8)
913 compression=NoCompression;
914 if (compression == NoCompression)
915 iris_info.storage=(unsigned char) 0x00;
916 else
917 iris_info.storage=(unsigned char) 0x01;
918 iris_info.bytes_per_pixel=(unsigned char) (image->depth > 8 ? 2 : 1);
919 iris_info.dimension=3;
920 iris_info.columns=(unsigned short) image->columns;
921 iris_info.rows=(unsigned short) image->rows;
922 if (image->matte != MagickFalse)
923 iris_info.depth=4;
924 else
925 {
926 if ((image_info->type != TrueColorType) &&
927 (IsGrayImage(image,&image->exception) != MagickFalse))
928 {
929 iris_info.dimension=2;
930 iris_info.depth=1;
931 }
932 else
933 iris_info.depth=3;
934 }
935 iris_info.minimum_value=0;
936 iris_info.maximum_value=(unsigned long) (image->depth <= 8 ?
937 1UL*ScaleQuantumToChar((Quantum) QuantumRange) :
938 1UL*ScaleQuantumToShort((Quantum) QuantumRange));
939 /*
940 Write SGI header.
941 */
942 (void) WriteBlobMSBShort(image,iris_info.magic);
943 (void) WriteBlobByte(image,iris_info.storage);
944 (void) WriteBlobByte(image,iris_info.bytes_per_pixel);
945 (void) WriteBlobMSBShort(image,iris_info.dimension);
946 (void) WriteBlobMSBShort(image,iris_info.columns);
947 (void) WriteBlobMSBShort(image,iris_info.rows);
948 (void) WriteBlobMSBShort(image,iris_info.depth);
949 (void) WriteBlobMSBLong(image,iris_info.minimum_value);
950 (void) WriteBlobMSBLong(image,iris_info.maximum_value);
951 (void) WriteBlobMSBLong(image,iris_info.sans);
952 value=GetImageProperty(image,"label");
953 if (value != (const char *) NULL)
954 (void) CopyMagickString(iris_info.name,value,sizeof(iris_info.name));
955 (void) WriteBlob(image,sizeof(iris_info.name),(unsigned char *)
956 iris_info.name);
957 (void) WriteBlobMSBLong(image,iris_info.pixel_format);
958 (void) WriteBlob(image,sizeof(iris_info.filler),iris_info.filler);
959 /*
960 Allocate SGI pixels.
961 */
962 number_pixels=(MagickSizeType) image->columns*image->rows;
963 if ((4*iris_info.bytes_per_pixel*number_pixels) !=
964 ((MagickSizeType) (size_t) (4*iris_info.bytes_per_pixel*number_pixels)))
965 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
966 iris_pixels=(unsigned char *) AcquireQuantumMemory((size_t) number_pixels,
967 4*iris_info.bytes_per_pixel*sizeof(*iris_pixels));
968 if (iris_pixels == (unsigned char *) NULL)
969 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
970 /*
971 Convert image pixels to uncompressed SGI pixels.
972 */
973 for (y=0; y < (long) image->rows; y++)
974 {
975 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
976 if (p == (const PixelPacket *) NULL)
977 break;
978 if (image->depth <= 8)
979 for (x=0; x < (long) image->columns; x++)
980 {
981 register unsigned char
982 *q;
983
984 q=(unsigned char *) iris_pixels;
985 q+=((iris_info.rows-1)-y)*(4*iris_info.columns)+4*x;
cristyce70c172010-01-07 17:15:30 +0000986 *q++=ScaleQuantumToChar(GetRedPixelComponent(p));
987 *q++=ScaleQuantumToChar(GetGreenPixelComponent(p));
988 *q++=ScaleQuantumToChar(GetBluePixelComponent(p));
cristy46f08202010-01-10 04:04:21 +0000989 *q++=ScaleQuantumToChar((Quantum) (GetAlphaPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +0000990 p++;
991 }
992 else
993 for (x=0; x < (long) image->columns; x++)
994 {
995 register unsigned short
996 *q;
997
998 q=(unsigned short *) iris_pixels;
999 q+=((iris_info.rows-1)-y)*(4*iris_info.columns)+4*x;
cristyce70c172010-01-07 17:15:30 +00001000 *q++=ScaleQuantumToShort(GetRedPixelComponent(p));
1001 *q++=ScaleQuantumToShort(GetGreenPixelComponent(p));
1002 *q++=ScaleQuantumToShort(GetBluePixelComponent(p));
cristy46f08202010-01-10 04:04:21 +00001003 *q++=ScaleQuantumToShort((Quantum) (GetAlphaPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +00001004 p++;
1005 }
1006 if (image->previous == (Image *) NULL)
1007 {
1008 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1009 if (status == MagickFalse)
1010 break;
1011 }
1012 }
1013 switch (compression)
1014 {
1015 case NoCompression:
1016 {
1017 /*
1018 Write uncompressed SGI pixels.
1019 */
1020 for (z=0; z < (long) iris_info.depth; z++)
1021 {
1022 for (y=0; y < (long) iris_info.rows; y++)
1023 {
1024 if (image->depth <= 8)
1025 for (x=0; x < (long) iris_info.columns; x++)
1026 {
1027 register unsigned char
1028 *q;
1029
1030 q=(unsigned char *) iris_pixels;
1031 q+=y*(4*iris_info.columns)+4*x+z;
1032 (void) WriteBlobByte(image,*q);
1033 }
1034 else
1035 for (x=0; x < (long) iris_info.columns; x++)
1036 {
1037 register unsigned short
1038 *q;
1039
1040 q=(unsigned short *) iris_pixels;
1041 q+=y*(4*iris_info.columns)+4*x+z;
1042 (void) WriteBlobMSBShort(image,*q);
1043 }
1044 }
1045 }
1046 break;
1047 }
1048 default:
1049 {
1050 ssize_t
1051 offset,
1052 *offsets;
1053
1054 size_t
1055 length,
1056 number_packets;
1057
1058 unsigned long
1059 *runlength;
1060
1061 /*
1062 Convert SGI uncompressed pixels.
1063 */
1064 offsets=(ssize_t *) AcquireQuantumMemory(iris_info.rows*iris_info.depth,
1065 sizeof(*offsets));
1066 packets=(unsigned char *) AcquireQuantumMemory((2*(size_t)
1067 iris_info.columns+10)*image->rows,4*sizeof(*packets));
1068 runlength=(unsigned long *) AcquireQuantumMemory(iris_info.rows,
1069 iris_info.depth*sizeof(*runlength));
1070 if ((offsets == (ssize_t *) NULL) ||
1071 (packets == (unsigned char *) NULL) ||
1072 (runlength == (unsigned long *) NULL))
1073 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1074 offset=512+4*2*((ssize_t) iris_info.rows*iris_info.depth);
1075 number_packets=0;
1076 q=iris_pixels;
1077 for (y=0; y < (long) iris_info.rows; y++)
1078 {
1079 for (z=0; z < (long) iris_info.depth; z++)
1080 {
1081 length=SGIEncode(q+z,(size_t) iris_info.columns,packets+
1082 number_packets);
1083 number_packets+=length;
1084 offsets[y+z*iris_info.rows]=offset;
1085 runlength[y+z*iris_info.rows]=(unsigned long) length;
1086 offset+=(ssize_t) length;
1087 }
1088 q+=(iris_info.columns*4);
1089 }
1090 /*
1091 Write out line start and length tables and runlength-encoded pixels.
1092 */
1093 for (i=0; i < (long) (iris_info.rows*iris_info.depth); i++)
1094 (void) WriteBlobMSBLong(image,(unsigned long) offsets[i]);
1095 for (i=0; i < (long) (iris_info.rows*iris_info.depth); i++)
1096 (void) WriteBlobMSBLong(image,runlength[i]);
1097 (void) WriteBlob(image,number_packets,packets);
1098 /*
1099 Relinquish resources.
1100 */
1101 runlength=(unsigned long *) RelinquishMagickMemory(runlength);
1102 packets=(unsigned char *) RelinquishMagickMemory(packets);
1103 offsets=(ssize_t *) RelinquishMagickMemory(offsets);
1104 break;
1105 }
1106 }
1107 iris_pixels=(unsigned char *) RelinquishMagickMemory(iris_pixels);
1108 if (GetNextImageInList(image) == (Image *) NULL)
1109 break;
1110 image=SyncNextImageInList(image);
1111 status=SetImageProgress(image,SaveImagesTag,scene++,
1112 GetImageListLength(image));
1113 if (status == MagickFalse)
1114 break;
1115 } while (image_info->adjoin != MagickFalse);
1116 (void) CloseBlob(image);
1117 return(MagickTrue);
1118}