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