blob: 59bfda5a7b8a8dd7081925dc19b9bd9d44277833 [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"
cristye7e40552010-04-24 21:34:22 +000048#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +000049#include "magick/colorspace.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/image.h"
53#include "magick/image-private.h"
54#include "magick/list.h"
55#include "magick/magick.h"
56#include "magick/memory_.h"
57#include "magick/monitor.h"
58#include "magick/monitor-private.h"
59#include "magick/property.h"
60#include "magick/quantum-private.h"
61#include "magick/static.h"
62#include "magick/string_.h"
63#include "magick/module.h"
64
65/*
66 Typedef declaractions.
67*/
68typedef struct _SGIInfo
69{
70 unsigned short
71 magic;
72
73 unsigned char
74 storage,
75 bytes_per_pixel;
76
77 unsigned short
78 dimension,
79 columns,
80 rows,
81 depth;
82
cristybb503372010-05-27 20:51:26 +000083 size_t
cristy3ed852e2009-09-05 21:47:34 +000084 minimum_value,
85 maximum_value,
86 sans;
87
88 char
89 name[80];
90
cristybb503372010-05-27 20:51:26 +000091 size_t
cristy3ed852e2009-09-05 21:47:34 +000092 pixel_format;
93
94 unsigned char
95 filler[404];
96} SGIInfo;
97
98/*
99 Forward declarations.
100*/
101static MagickBooleanType
102 WriteSGIImage(const ImageInfo *,Image *);
103/*
104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105% %
106% %
107% %
108% I s S G I %
109% %
110% %
111% %
112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113%
114% IsSGI() returns MagickTrue if the image format type, identified by the
115% magick string, is SGI.
116%
117% The format of the IsSGI method is:
118%
119% MagickBooleanType IsSGI(const unsigned char *magick,const size_t length)
120%
121% A description of each parameter follows:
122%
123% o magick: compare image format pattern against these bytes.
124%
125% o length: Specifies the length of the magick string.
126%
127%
128*/
129static MagickBooleanType IsSGI(const unsigned char *magick,const size_t length)
130{
131 if (length < 2)
132 return(MagickFalse);
133 if (memcmp(magick,"\001\332",2) == 0)
134 return(MagickTrue);
135 return(MagickFalse);
136}
137
138/*
139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140% %
141% %
142% %
143% R e a d S G I I m a g e %
144% %
145% %
146% %
147%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148%
149% ReadSGIImage() reads a SGI RGB image file and returns it. It
150% allocates the memory necessary for the new Image structure and returns a
151% pointer to the new image.
152%
153% The format of the ReadSGIImage method is:
154%
155% Image *ReadSGIImage(const ImageInfo *image_info,ExceptionInfo *exception)
156%
157% A description of each parameter follows:
158%
159% o image_info: the image info.
160%
161% o exception: return any errors or warnings in this structure.
162%
163*/
164
165static inline size_t MagickMin(const size_t x,const size_t y)
166{
167 if (x < y)
168 return(x);
169 return(y);
170}
171
172static MagickBooleanType SGIDecode(const size_t bytes_per_pixel,
cristybb503372010-05-27 20:51:26 +0000173 ssize_t number_packets,unsigned char *packets,ssize_t number_pixels,
cristy3ed852e2009-09-05 21:47:34 +0000174 unsigned char *pixels)
175{
176 register unsigned char
177 *p,
178 *q;
179
180 ssize_t
181 count;
182
cristybb503372010-05-27 20:51:26 +0000183 size_t
cristy3ed852e2009-09-05 21:47:34 +0000184 pixel;
185
186 p=packets;
187 q=pixels;
188 if (bytes_per_pixel == 2)
189 {
190 for ( ; number_pixels > 0; )
191 {
192 if (number_packets-- == 0)
193 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000194 pixel=(size_t) (*p++) << 8;
cristy3ed852e2009-09-05 21:47:34 +0000195 pixel|=(*p++);
196 count=(ssize_t) (pixel & 0x7f);
197 if (count == 0)
198 break;
199 if (count > (ssize_t) number_pixels)
200 return(MagickFalse);
201 number_pixels-=count;
202 if ((pixel & 0x80) != 0)
203 for ( ; count != 0; count--)
204 {
205 if (number_packets-- == 0)
206 return(MagickFalse);
207 *q=(*p++);
208 *(q+1)=(*p++);
209 q+=8;
210 }
211 else
212 {
cristybb503372010-05-27 20:51:26 +0000213 pixel=(size_t) (*p++) << 8;
cristy3ed852e2009-09-05 21:47:34 +0000214 pixel|=(*p++);
215 for ( ; count != 0; count--)
216 {
217 if (number_packets-- == 0)
218 return(MagickFalse);
219 *q=(unsigned char) (pixel >> 8);
220 *(q+1)=(unsigned char) pixel;
221 q+=8;
222 }
223 }
224 }
225 return(MagickTrue);
226 }
227 for ( ; number_pixels > 0; )
228 {
229 if (number_packets-- == 0)
230 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000231 pixel=(size_t) (*p++);
cristy3ed852e2009-09-05 21:47:34 +0000232 count=(ssize_t) (pixel & 0x7f);
233 if (count == 0)
234 break;
235 if (count > (ssize_t) number_pixels)
236 return(MagickFalse);
237 number_pixels-=count;
238 if ((pixel & 0x80) != 0)
239 for ( ; count != 0; count--)
240 {
241 if (number_packets-- == 0)
242 return(MagickFalse);
243 *q=(*p++);
244 q+=4;
245 }
246 else
247 {
248 if (number_packets-- == 0)
249 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000250 pixel=(size_t) (*p++);
cristy3ed852e2009-09-05 21:47:34 +0000251 for ( ; count != 0; count--)
252 {
253 *q=(unsigned char) pixel;
254 q+=4;
255 }
256 }
257 }
258 return(MagickTrue);
259}
260
261static Image *ReadSGIImage(const ImageInfo *image_info,ExceptionInfo *exception)
262{
263 Image
264 *image;
265
cristybb503372010-05-27 20:51:26 +0000266 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000267 y,
268 z;
269
270 MagickBooleanType
271 status;
272
273 MagickSizeType
274 number_pixels;
275
276 register IndexPacket
277 *indexes;
278
cristybb503372010-05-27 20:51:26 +0000279 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000280 i,
281 x;
282
283 register PixelPacket
284 *q;
285
286 register unsigned char
287 *p;
288
289 ssize_t
290 count;
291
292 SGIInfo
293 iris_info;
294
295 size_t
296 bytes_per_pixel;
297
298 unsigned char
299 *iris_pixels;
300
cristybb503372010-05-27 20:51:26 +0000301 size_t
cristy3ed852e2009-09-05 21:47:34 +0000302 quantum;
303
304 /*
305 Open image file.
306 */
307 assert(image_info != (const ImageInfo *) NULL);
308 assert(image_info->signature == MagickSignature);
309 if (image_info->debug != MagickFalse)
310 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
311 image_info->filename);
312 assert(exception != (ExceptionInfo *) NULL);
313 assert(exception->signature == MagickSignature);
314 image=AcquireImage(image_info);
315 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
316 if (status == MagickFalse)
317 {
318 image=DestroyImageList(image);
319 return((Image *) NULL);
320 }
321 /*
322 Read SGI raster header.
323 */
324 iris_info.magic=ReadBlobMSBShort(image);
325 do
326 {
327 /*
328 Verify SGI identifier.
329 */
330 if (iris_info.magic != 0x01DA)
331 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
332 iris_info.storage=(unsigned char) ReadBlobByte(image);
333 switch (iris_info.storage)
334 {
335 case 0x00: image->compression=NoCompression; break;
336 case 0x01: image->compression=RLECompression; break;
337 default:
338 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
339 }
340 iris_info.bytes_per_pixel=(unsigned char) ReadBlobByte(image);
341 if ((iris_info.bytes_per_pixel == 0) || (iris_info.bytes_per_pixel > 2))
342 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
343 iris_info.dimension=ReadBlobMSBShort(image);
344 iris_info.columns=ReadBlobMSBShort(image);
345 iris_info.rows=ReadBlobMSBShort(image);
346 iris_info.depth=ReadBlobMSBShort(image);
347 if ((iris_info.depth == 0) || (iris_info.depth > 4))
348 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
349 iris_info.minimum_value=ReadBlobMSBLong(image);
350 iris_info.maximum_value=ReadBlobMSBLong(image);
351 iris_info.sans=ReadBlobMSBLong(image);
352 (void) ReadBlob(image,sizeof(iris_info.name),(unsigned char *)
353 iris_info.name);
354 iris_info.name[sizeof(iris_info.name)-1]='\0';
355 if (*iris_info.name != '\0')
356 (void) SetImageProperty(image,"label",iris_info.name);
357 iris_info.pixel_format=ReadBlobMSBLong(image);
358 if (iris_info.pixel_format != 0)
359 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
360 count=ReadBlob(image,sizeof(iris_info.filler),iris_info.filler);
361 image->columns=iris_info.columns;
362 image->rows=iris_info.rows;
cristybb503372010-05-27 20:51:26 +0000363 image->depth=(size_t) MagickMin(iris_info.depth,MAGICKCORE_QUANTUM_DEPTH);
cristy3ed852e2009-09-05 21:47:34 +0000364 if (iris_info.pixel_format == 0)
cristybb503372010-05-27 20:51:26 +0000365 image->depth=(size_t) MagickMin((size_t) 8*
cristy3ed852e2009-09-05 21:47:34 +0000366 iris_info.bytes_per_pixel,MAGICKCORE_QUANTUM_DEPTH);
367 if (iris_info.depth < 3)
368 {
369 image->storage_class=PseudoClass;
370 image->colors=256;
371 }
372 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
373 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
374 break;
375 /*
376 Allocate SGI pixels.
377 */
378 bytes_per_pixel=(size_t) iris_info.bytes_per_pixel;
379 number_pixels=(MagickSizeType) iris_info.columns*iris_info.rows;
380 if ((4*bytes_per_pixel*number_pixels) != ((MagickSizeType) (size_t)
381 (4*bytes_per_pixel*number_pixels)))
382 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
383 iris_pixels=(unsigned char *) AcquireQuantumMemory(iris_info.columns,
384 iris_info.rows*4*bytes_per_pixel*sizeof(*iris_pixels));
385 if (iris_pixels == (unsigned char *) NULL)
386 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
387 if ((int) iris_info.storage != 0x01)
388 {
389 unsigned char
390 *scanline;
391
392 /*
393 Read standard image format.
394 */
395 scanline=(unsigned char *) AcquireQuantumMemory(iris_info.columns,
396 bytes_per_pixel*sizeof(*scanline));
397 if (scanline == (unsigned char *) NULL)
398 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +0000399 for (z=0; z < (ssize_t) iris_info.depth; z++)
cristy3ed852e2009-09-05 21:47:34 +0000400 {
401 p=iris_pixels+bytes_per_pixel*z;
cristybb503372010-05-27 20:51:26 +0000402 for (y=0; y < (ssize_t) iris_info.rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000403 {
404 count=ReadBlob(image,bytes_per_pixel*iris_info.columns,scanline);
405 if (EOFBlob(image) != MagickFalse)
406 break;
407 if (bytes_per_pixel == 2)
cristybb503372010-05-27 20:51:26 +0000408 for (x=0; x < (ssize_t) iris_info.columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000409 {
410 *p=scanline[2*x];
411 *(p+1)=scanline[2*x+1];
412 p+=8;
413 }
414 else
cristybb503372010-05-27 20:51:26 +0000415 for (x=0; x < (ssize_t) iris_info.columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000416 {
417 *p=scanline[x];
418 p+=4;
419 }
420 }
421 }
422 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
423 }
424 else
425 {
426 ssize_t
427 offset,
428 *offsets;
429
430 unsigned char
431 *packets;
432
433 unsigned int
434 data_order;
435
cristybb503372010-05-27 20:51:26 +0000436 size_t
cristy3ed852e2009-09-05 21:47:34 +0000437 *runlength;
438
439 /*
440 Read runlength-encoded image format.
441 */
442 offsets=(ssize_t *) AcquireQuantumMemory((size_t) iris_info.rows,
443 iris_info.depth*sizeof(*offsets));
444 packets=(unsigned char *) AcquireQuantumMemory((size_t)
445 iris_info.columns+10UL,4UL*sizeof(*packets));
cristybb503372010-05-27 20:51:26 +0000446 runlength=(size_t *) AcquireQuantumMemory(iris_info.rows,
cristy3ed852e2009-09-05 21:47:34 +0000447 iris_info.depth*sizeof(*runlength));
448 if ((offsets == (ssize_t *) NULL) ||
449 (packets == (unsigned char *) NULL) ||
cristybb503372010-05-27 20:51:26 +0000450 (runlength == (size_t *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000451 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +0000452 for (i=0; i < (ssize_t) (iris_info.rows*iris_info.depth); i++)
cristy6cff05d2010-09-02 11:22:46 +0000453 offsets[i]=(int) ReadBlobMSBLong(image);
cristybb503372010-05-27 20:51:26 +0000454 for (i=0; i < (ssize_t) (iris_info.rows*iris_info.depth); i++)
cristy3ed852e2009-09-05 21:47:34 +0000455 {
456 runlength[i]=ReadBlobMSBLong(image);
457 if (runlength[i] > (4*(size_t) iris_info.columns+10))
458 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
459 }
460 /*
461 Check data order.
462 */
463 offset=0;
464 data_order=0;
cristybb503372010-05-27 20:51:26 +0000465 for (y=0; ((y < (ssize_t) iris_info.rows) && (data_order == 0)); y++)
466 for (z=0; ((z < (ssize_t) iris_info.depth) && (data_order == 0)); z++)
cristy3ed852e2009-09-05 21:47:34 +0000467 {
468 if (offsets[y+z*iris_info.rows] < offset)
469 data_order=1;
470 offset=offsets[y+z*iris_info.rows];
471 }
472 offset=(ssize_t) TellBlob(image);
473 if (data_order == 1)
474 {
cristybb503372010-05-27 20:51:26 +0000475 for (z=0; z < (ssize_t) iris_info.depth; z++)
cristy3ed852e2009-09-05 21:47:34 +0000476 {
477 p=iris_pixels;
cristybb503372010-05-27 20:51:26 +0000478 for (y=0; y < (ssize_t) iris_info.rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000479 {
480 if (offset != offsets[y+z*iris_info.rows])
481 {
482 offset=offsets[y+z*iris_info.rows];
cristybb503372010-05-27 20:51:26 +0000483 offset=(ssize_t) SeekBlob(image,(ssize_t) offset,SEEK_SET);
cristy3ed852e2009-09-05 21:47:34 +0000484 }
485 count=ReadBlob(image,(size_t) runlength[y+z*iris_info.rows],
486 packets);
487 if (EOFBlob(image) != MagickFalse)
488 break;
cristyeaedf062010-05-29 22:36:02 +0000489 offset+=(ssize_t) runlength[y+z*iris_info.rows];
cristybb503372010-05-27 20:51:26 +0000490 status=SGIDecode(bytes_per_pixel,(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +0000491 (runlength[y+z*iris_info.rows]/bytes_per_pixel),packets,
492 1L*iris_info.columns,p+bytes_per_pixel*z);
493 if (status == MagickFalse)
494 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
495 p+=(iris_info.columns*4*bytes_per_pixel);
496 }
497 }
498 }
499 else
500 {
501 MagickOffsetType
502 position;
503
504 position=TellBlob(image);
505 p=iris_pixels;
cristybb503372010-05-27 20:51:26 +0000506 for (y=0; y < (ssize_t) iris_info.rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000507 {
cristybb503372010-05-27 20:51:26 +0000508 for (z=0; z < (ssize_t) iris_info.depth; z++)
cristy3ed852e2009-09-05 21:47:34 +0000509 {
510 if (offset != offsets[y+z*iris_info.rows])
511 {
512 offset=offsets[y+z*iris_info.rows];
cristybb503372010-05-27 20:51:26 +0000513 offset=(ssize_t) SeekBlob(image,(ssize_t) offset,SEEK_SET);
cristy3ed852e2009-09-05 21:47:34 +0000514 }
515 count=ReadBlob(image,(size_t) runlength[y+z*iris_info.rows],
516 packets);
517 if (EOFBlob(image) != MagickFalse)
518 break;
cristyeaedf062010-05-29 22:36:02 +0000519 offset+=(ssize_t) runlength[y+z*iris_info.rows];
cristybb503372010-05-27 20:51:26 +0000520 status=SGIDecode(bytes_per_pixel,(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +0000521 (runlength[y+z*iris_info.rows]/bytes_per_pixel),packets,
522 1L*iris_info.columns,p+bytes_per_pixel*z);
523 if (status == MagickFalse)
524 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
525 }
526 p+=(iris_info.columns*4*bytes_per_pixel);
527 }
528 offset=(ssize_t) SeekBlob(image,position,SEEK_SET);
529 }
cristybb503372010-05-27 20:51:26 +0000530 runlength=(size_t *) RelinquishMagickMemory(runlength);
cristy3ed852e2009-09-05 21:47:34 +0000531 packets=(unsigned char *) RelinquishMagickMemory(packets);
532 offsets=(ssize_t *) RelinquishMagickMemory(offsets);
533 }
534 /*
535 Initialize image structure.
536 */
537 image->matte=iris_info.depth == 4 ? MagickTrue : MagickFalse;
538 image->columns=iris_info.columns;
539 image->rows=iris_info.rows;
540 /*
541 Convert SGI raster image to pixel packets.
542 */
543 if (image->storage_class == DirectClass)
544 {
545 /*
546 Convert SGI image to DirectClass pixel packets.
547 */
548 if (bytes_per_pixel == 2)
549 {
cristybb503372010-05-27 20:51:26 +0000550 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000551 {
552 p=iris_pixels+(image->rows-y-1)*8*image->columns;
553 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
554 if (q == (PixelPacket *) NULL)
555 break;
cristybb503372010-05-27 20:51:26 +0000556 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000557 {
558 q->red=ScaleShortToQuantum((unsigned short)
559 ((*(p+0) << 8) | (*(p+1))));
560 q->green=ScaleShortToQuantum((unsigned short)
561 ((*(p+2) << 8) | (*(p+3))));
562 q->blue=ScaleShortToQuantum((unsigned short)
563 ((*(p+4) << 8) | (*(p+5))));
cristyce70c172010-01-07 17:15:30 +0000564 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +0000565 if (image->matte != MagickFalse)
566 q->opacity=(Quantum) (QuantumRange-ScaleShortToQuantum(
567 (unsigned short) ((*(p+6) << 8) | (*(p+7)))));
568 p+=8;
569 q++;
570 }
571 if (SyncAuthenticPixels(image,exception) == MagickFalse)
572 break;
573 if (image->previous == (Image *) NULL)
574 {
cristycee97112010-05-28 00:44:52 +0000575 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
576 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000577 if (status == MagickFalse)
578 break;
579 }
580 }
581 }
582 else
cristybb503372010-05-27 20:51:26 +0000583 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000584 {
585 p=iris_pixels+(image->rows-y-1)*4*image->columns;
586 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
587 if (q == (PixelPacket *) NULL)
588 break;
cristybb503372010-05-27 20:51:26 +0000589 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000590 {
591 q->red=ScaleCharToQuantum(*p);
592 q->green=ScaleCharToQuantum(*(p+1));
593 q->blue=ScaleCharToQuantum(*(p+2));
cristyce70c172010-01-07 17:15:30 +0000594 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +0000595 if (image->matte != MagickFalse)
596 q->opacity=(Quantum) (QuantumRange-ScaleCharToQuantum(*(p+3)));
597 p+=4;
598 q++;
599 }
600 if (SyncAuthenticPixels(image,exception) == MagickFalse)
601 break;
602 if (image->previous == (Image *) NULL)
603 {
cristycee97112010-05-28 00:44:52 +0000604 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
605 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000606 if (status == MagickFalse)
607 break;
608 }
609 }
610 }
611 else
612 {
613 /*
614 Create grayscale map.
615 */
616 if (AcquireImageColormap(image,image->colors) == MagickFalse)
617 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
618 /*
619 Convert SGI image to PseudoClass pixel packets.
620 */
621 if (bytes_per_pixel == 2)
622 {
cristybb503372010-05-27 20:51:26 +0000623 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000624 {
625 p=iris_pixels+(image->rows-y-1)*8*image->columns;
626 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
627 if (q == (PixelPacket *) NULL)
628 break;
629 indexes=GetAuthenticIndexQueue(image);
cristybb503372010-05-27 20:51:26 +0000630 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000631 {
632 quantum=(*p << 8);
633 quantum|=(*(p+1));
634 indexes[x]=(IndexPacket) quantum;
635 p+=8;
636 q++;
637 }
638 if (SyncAuthenticPixels(image,exception) == MagickFalse)
639 break;
640 if (image->previous == (Image *) NULL)
641 {
cristycee97112010-05-28 00:44:52 +0000642 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
643 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000644 if (status == MagickFalse)
645 break;
646 }
647 }
648 }
649 else
cristybb503372010-05-27 20:51:26 +0000650 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000651 {
652 p=iris_pixels+(image->rows-y-1)*4*image->columns;
653 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
654 if (q == (PixelPacket *) NULL)
655 break;
656 indexes=GetAuthenticIndexQueue(image);
cristybb503372010-05-27 20:51:26 +0000657 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000658 {
659 indexes[x]=(IndexPacket) (*p);
660 p+=4;
661 q++;
662 }
663 if (SyncAuthenticPixels(image,exception) == MagickFalse)
664 break;
665 if (image->previous == (Image *) NULL)
666 {
cristycee97112010-05-28 00:44:52 +0000667 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
668 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000669 if (status == MagickFalse)
670 break;
671 }
672 }
673 (void) SyncImage(image);
674 }
675 iris_pixels=(unsigned char *) RelinquishMagickMemory(iris_pixels);
676 if (EOFBlob(image) != MagickFalse)
677 {
678 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
679 image->filename);
680 break;
681 }
682 /*
683 Proceed to next image.
684 */
685 if (image_info->number_scenes != 0)
686 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
687 break;
688 iris_info.magic=ReadBlobMSBShort(image);
689 if (iris_info.magic == 0x01DA)
690 {
691 /*
692 Allocate next image structure.
693 */
694 AcquireNextImage(image_info,image);
695 if (GetNextImageInList(image) == (Image *) NULL)
696 {
697 image=DestroyImageList(image);
698 return((Image *) NULL);
699 }
700 image=SyncNextImageInList(image);
701 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
702 GetBlobSize(image));
703 if (status == MagickFalse)
704 break;
705 }
706 } while (iris_info.magic == 0x01DA);
707 (void) CloseBlob(image);
708 return(GetFirstImageInList(image));
709}
710
711/*
712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
713% %
714% %
715% %
716% R e g i s t e r S G I I m a g e %
717% %
718% %
719% %
720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
721%
722% RegisterSGIImage() adds properties for the SGI image format to
723% the list of supported formats. The properties include the image format
724% tag, a method to read and/or write the format, whether the format
725% supports the saving of more than one frame to the same file or blob,
726% whether the format supports native in-memory I/O, and a brief
727% description of the format.
728%
729% The format of the RegisterSGIImage method is:
730%
cristybb503372010-05-27 20:51:26 +0000731% size_t RegisterSGIImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000732%
733*/
cristybb503372010-05-27 20:51:26 +0000734ModuleExport size_t RegisterSGIImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000735{
736 MagickInfo
737 *entry;
738
739 entry=SetMagickInfo("SGI");
740 entry->decoder=(DecodeImageHandler *) ReadSGIImage;
741 entry->encoder=(EncodeImageHandler *) WriteSGIImage;
742 entry->magick=(IsImageFormatHandler *) IsSGI;
743 entry->description=ConstantString("Irix RGB image");
744 entry->module=ConstantString("SGI");
745 entry->seekable_stream=MagickTrue;
746 (void) RegisterMagickInfo(entry);
747 return(MagickImageCoderSignature);
748}
749
750/*
751%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
752% %
753% %
754% %
755% U n r e g i s t e r S G I I m a g e %
756% %
757% %
758% %
759%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
760%
761% UnregisterSGIImage() removes format registrations made by the
762% SGI module from the list of supported formats.
763%
764% The format of the UnregisterSGIImage method is:
765%
766% UnregisterSGIImage(void)
767%
768*/
769ModuleExport void UnregisterSGIImage(void)
770{
771 (void) UnregisterMagickInfo("SGI");
772}
773
774/*
775%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
776% %
777% %
778% %
779% W r i t e S G I I m a g e %
780% %
781% %
782% %
783%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
784%
785% WriteSGIImage() writes an image in SGI RGB encoded image format.
786%
787% The format of the WriteSGIImage method is:
788%
789% MagickBooleanType WriteSGIImage(const ImageInfo *image_info,Image *image)
790%
791% A description of each parameter follows.
792%
793% o image_info: the image info.
794%
795% o image: The image.
796%
797*/
798
799static size_t SGIEncode(unsigned char *pixels,size_t length,
800 unsigned char *packets)
801{
802 short
803 runlength;
804
805 register unsigned char
806 *p,
807 *q;
808
809 unsigned char
810 *limit,
811 *mark;
812
813 p=pixels;
814 limit=p+length*4;
815 q=packets;
816 while (p < limit)
817 {
818 mark=p;
819 p+=8;
820 while ((p < limit) && ((*(p-8) != *(p-4)) || (*(p-4) != *p)))
821 p+=4;
822 p-=8;
823 length=(size_t) (p-mark) >> 2;
824 while (length != 0)
825 {
826 runlength=(short) (length > 126 ? 126 : length);
827 length-=runlength;
828 *q++=(unsigned char) (0x80 | runlength);
829 for ( ; runlength > 0; runlength--)
830 {
831 *q++=(*mark);
832 mark+=4;
833 }
834 }
835 mark=p;
836 p+=4;
837 while ((p < limit) && (*p == *mark))
838 p+=4;
839 length=(size_t) (p-mark) >> 2;
840 while (length != 0)
841 {
842 runlength=(short) (length > 126 ? 126 : length);
843 length-=runlength;
844 *q++=(unsigned char) runlength;
845 *q++=(*mark);
846 }
847 }
848 *q++='\0';
849 return((size_t) (q-packets));
850}
851
852static MagickBooleanType WriteSGIImage(const ImageInfo *image_info,Image *image)
853{
854 CompressionType
855 compression;
856
857 const char
858 *value;
859
cristybb503372010-05-27 20:51:26 +0000860 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000861 y,
862 z;
863
864 MagickBooleanType
865 status;
866
867 MagickOffsetType
868 scene;
869
870 MagickSizeType
871 number_pixels;
872
873 SGIInfo
874 iris_info;
875
876 register const PixelPacket
877 *p;
878
cristybb503372010-05-27 20:51:26 +0000879 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000880 i,
881 x;
882
883 register unsigned char
884 *q;
885
886 unsigned char
887 *iris_pixels,
888 *packets;
889
890 /*
891 Open output image file.
892 */
893 assert(image_info != (const ImageInfo *) NULL);
894 assert(image_info->signature == MagickSignature);
895 assert(image != (Image *) NULL);
896 assert(image->signature == MagickSignature);
897 if (image->debug != MagickFalse)
898 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
899 if ((image->columns > 65535UL) || (image->rows > 65535UL))
900 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
901 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
902 if (status == MagickFalse)
903 return(status);
904 scene=0;
905 do
906 {
907 /*
908 Initialize SGI raster file header.
909 */
910 if (image->colorspace != RGBColorspace)
911 (void) TransformImageColorspace(image,RGBColorspace);
912 (void) ResetMagickMemory(&iris_info,0,sizeof(iris_info));
913 iris_info.magic=0x01DA;
914 compression=image->compression;
915 if (image_info->compression != UndefinedCompression)
916 compression=image_info->compression;
917 if (image->depth > 8)
918 compression=NoCompression;
919 if (compression == NoCompression)
920 iris_info.storage=(unsigned char) 0x00;
921 else
922 iris_info.storage=(unsigned char) 0x01;
923 iris_info.bytes_per_pixel=(unsigned char) (image->depth > 8 ? 2 : 1);
924 iris_info.dimension=3;
925 iris_info.columns=(unsigned short) image->columns;
926 iris_info.rows=(unsigned short) image->rows;
927 if (image->matte != MagickFalse)
928 iris_info.depth=4;
929 else
930 {
931 if ((image_info->type != TrueColorType) &&
932 (IsGrayImage(image,&image->exception) != MagickFalse))
933 {
934 iris_info.dimension=2;
935 iris_info.depth=1;
936 }
937 else
938 iris_info.depth=3;
939 }
940 iris_info.minimum_value=0;
cristybb503372010-05-27 20:51:26 +0000941 iris_info.maximum_value=(size_t) (image->depth <= 8 ?
cristy3ed852e2009-09-05 21:47:34 +0000942 1UL*ScaleQuantumToChar((Quantum) QuantumRange) :
943 1UL*ScaleQuantumToShort((Quantum) QuantumRange));
944 /*
945 Write SGI header.
946 */
947 (void) WriteBlobMSBShort(image,iris_info.magic);
948 (void) WriteBlobByte(image,iris_info.storage);
949 (void) WriteBlobByte(image,iris_info.bytes_per_pixel);
950 (void) WriteBlobMSBShort(image,iris_info.dimension);
951 (void) WriteBlobMSBShort(image,iris_info.columns);
952 (void) WriteBlobMSBShort(image,iris_info.rows);
953 (void) WriteBlobMSBShort(image,iris_info.depth);
cristyeaedf062010-05-29 22:36:02 +0000954 (void) WriteBlobMSBLong(image,(unsigned int) iris_info.minimum_value);
955 (void) WriteBlobMSBLong(image,(unsigned int) iris_info.maximum_value);
956 (void) WriteBlobMSBLong(image,(unsigned int) iris_info.sans);
cristy3ed852e2009-09-05 21:47:34 +0000957 value=GetImageProperty(image,"label");
958 if (value != (const char *) NULL)
959 (void) CopyMagickString(iris_info.name,value,sizeof(iris_info.name));
960 (void) WriteBlob(image,sizeof(iris_info.name),(unsigned char *)
961 iris_info.name);
cristy0b29b252010-05-30 01:59:46 +0000962 (void) WriteBlobMSBLong(image,(unsigned int) iris_info.pixel_format);
cristy3ed852e2009-09-05 21:47:34 +0000963 (void) WriteBlob(image,sizeof(iris_info.filler),iris_info.filler);
964 /*
965 Allocate SGI pixels.
966 */
967 number_pixels=(MagickSizeType) image->columns*image->rows;
968 if ((4*iris_info.bytes_per_pixel*number_pixels) !=
969 ((MagickSizeType) (size_t) (4*iris_info.bytes_per_pixel*number_pixels)))
970 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
971 iris_pixels=(unsigned char *) AcquireQuantumMemory((size_t) number_pixels,
972 4*iris_info.bytes_per_pixel*sizeof(*iris_pixels));
973 if (iris_pixels == (unsigned char *) NULL)
974 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
975 /*
976 Convert image pixels to uncompressed SGI pixels.
977 */
cristybb503372010-05-27 20:51:26 +0000978 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000979 {
980 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
981 if (p == (const PixelPacket *) NULL)
982 break;
983 if (image->depth <= 8)
cristybb503372010-05-27 20:51:26 +0000984 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000985 {
986 register unsigned char
987 *q;
988
989 q=(unsigned char *) iris_pixels;
990 q+=((iris_info.rows-1)-y)*(4*iris_info.columns)+4*x;
cristyce70c172010-01-07 17:15:30 +0000991 *q++=ScaleQuantumToChar(GetRedPixelComponent(p));
992 *q++=ScaleQuantumToChar(GetGreenPixelComponent(p));
993 *q++=ScaleQuantumToChar(GetBluePixelComponent(p));
cristy46f08202010-01-10 04:04:21 +0000994 *q++=ScaleQuantumToChar((Quantum) (GetAlphaPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +0000995 p++;
996 }
997 else
cristybb503372010-05-27 20:51:26 +0000998 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000999 {
1000 register unsigned short
1001 *q;
1002
1003 q=(unsigned short *) iris_pixels;
1004 q+=((iris_info.rows-1)-y)*(4*iris_info.columns)+4*x;
cristyce70c172010-01-07 17:15:30 +00001005 *q++=ScaleQuantumToShort(GetRedPixelComponent(p));
1006 *q++=ScaleQuantumToShort(GetGreenPixelComponent(p));
1007 *q++=ScaleQuantumToShort(GetBluePixelComponent(p));
cristy46f08202010-01-10 04:04:21 +00001008 *q++=ScaleQuantumToShort((Quantum) (GetAlphaPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +00001009 p++;
1010 }
1011 if (image->previous == (Image *) NULL)
1012 {
cristycee97112010-05-28 00:44:52 +00001013 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1014 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001015 if (status == MagickFalse)
1016 break;
1017 }
1018 }
1019 switch (compression)
1020 {
1021 case NoCompression:
1022 {
1023 /*
1024 Write uncompressed SGI pixels.
1025 */
cristybb503372010-05-27 20:51:26 +00001026 for (z=0; z < (ssize_t) iris_info.depth; z++)
cristy3ed852e2009-09-05 21:47:34 +00001027 {
cristybb503372010-05-27 20:51:26 +00001028 for (y=0; y < (ssize_t) iris_info.rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001029 {
1030 if (image->depth <= 8)
cristybb503372010-05-27 20:51:26 +00001031 for (x=0; x < (ssize_t) iris_info.columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001032 {
1033 register unsigned char
1034 *q;
1035
1036 q=(unsigned char *) iris_pixels;
1037 q+=y*(4*iris_info.columns)+4*x+z;
1038 (void) WriteBlobByte(image,*q);
1039 }
1040 else
cristybb503372010-05-27 20:51:26 +00001041 for (x=0; x < (ssize_t) iris_info.columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001042 {
1043 register unsigned short
1044 *q;
1045
1046 q=(unsigned short *) iris_pixels;
1047 q+=y*(4*iris_info.columns)+4*x+z;
1048 (void) WriteBlobMSBShort(image,*q);
1049 }
1050 }
1051 }
1052 break;
1053 }
1054 default:
1055 {
1056 ssize_t
1057 offset,
1058 *offsets;
1059
1060 size_t
1061 length,
1062 number_packets;
1063
cristybb503372010-05-27 20:51:26 +00001064 size_t
cristy3ed852e2009-09-05 21:47:34 +00001065 *runlength;
1066
1067 /*
1068 Convert SGI uncompressed pixels.
1069 */
1070 offsets=(ssize_t *) AcquireQuantumMemory(iris_info.rows*iris_info.depth,
1071 sizeof(*offsets));
1072 packets=(unsigned char *) AcquireQuantumMemory((2*(size_t)
1073 iris_info.columns+10)*image->rows,4*sizeof(*packets));
cristybb503372010-05-27 20:51:26 +00001074 runlength=(size_t *) AcquireQuantumMemory(iris_info.rows,
cristy3ed852e2009-09-05 21:47:34 +00001075 iris_info.depth*sizeof(*runlength));
1076 if ((offsets == (ssize_t *) NULL) ||
1077 (packets == (unsigned char *) NULL) ||
cristybb503372010-05-27 20:51:26 +00001078 (runlength == (size_t *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001079 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1080 offset=512+4*2*((ssize_t) iris_info.rows*iris_info.depth);
1081 number_packets=0;
1082 q=iris_pixels;
cristybb503372010-05-27 20:51:26 +00001083 for (y=0; y < (ssize_t) iris_info.rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001084 {
cristybb503372010-05-27 20:51:26 +00001085 for (z=0; z < (ssize_t) iris_info.depth; z++)
cristy3ed852e2009-09-05 21:47:34 +00001086 {
1087 length=SGIEncode(q+z,(size_t) iris_info.columns,packets+
1088 number_packets);
1089 number_packets+=length;
1090 offsets[y+z*iris_info.rows]=offset;
cristybb503372010-05-27 20:51:26 +00001091 runlength[y+z*iris_info.rows]=(size_t) length;
cristy3ed852e2009-09-05 21:47:34 +00001092 offset+=(ssize_t) length;
1093 }
1094 q+=(iris_info.columns*4);
1095 }
1096 /*
1097 Write out line start and length tables and runlength-encoded pixels.
1098 */
cristybb503372010-05-27 20:51:26 +00001099 for (i=0; i < (ssize_t) (iris_info.rows*iris_info.depth); i++)
cristyf9cca6a2010-06-04 23:49:28 +00001100 (void) WriteBlobMSBLong(image,(unsigned int) offsets[i]);
cristybb503372010-05-27 20:51:26 +00001101 for (i=0; i < (ssize_t) (iris_info.rows*iris_info.depth); i++)
cristyeaedf062010-05-29 22:36:02 +00001102 (void) WriteBlobMSBLong(image,(unsigned int) runlength[i]);
cristy3ed852e2009-09-05 21:47:34 +00001103 (void) WriteBlob(image,number_packets,packets);
1104 /*
1105 Relinquish resources.
1106 */
cristybb503372010-05-27 20:51:26 +00001107 runlength=(size_t *) RelinquishMagickMemory(runlength);
cristy3ed852e2009-09-05 21:47:34 +00001108 packets=(unsigned char *) RelinquishMagickMemory(packets);
1109 offsets=(ssize_t *) RelinquishMagickMemory(offsets);
1110 break;
1111 }
1112 }
1113 iris_pixels=(unsigned char *) RelinquishMagickMemory(iris_pixels);
1114 if (GetNextImageInList(image) == (Image *) NULL)
1115 break;
1116 image=SyncNextImageInList(image);
1117 status=SetImageProgress(image,SaveImagesTag,scene++,
1118 GetImageListLength(image));
1119 if (status == MagickFalse)
1120 break;
1121 } while (image_info->adjoin != MagickFalse);
1122 (void) CloseBlob(image);
1123 return(MagickTrue);
1124}