/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- IsSGI
- SGIDecode
- ReadSGIImage
- RegisterSGIImage
- UnregisterSGIImage
- SGIEncode
- WriteSGIImage
/*
% Copyright (C) 2003 GraphicsMagick Group
% Copyright (C) 2002 ImageMagick Studio
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
%
% This program is covered by multiple licenses, which are described in
% Copyright.txt. You should have received a copy of Copyright.txt with this
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% SSSSS GGGG IIIII %
% SS G I %
% SSS G GG I %
% SS G G I %
% SSSSS GGG IIIII %
% %
% %
% Read/Write Irix RGB Image Format. %
% %
% %
% Software Design %
% John Cristy %
% July 1992 %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
*/
/*
Include declarations.
*/
#include "magick/studio.h"
#include "magick/analyze.h"
#include "magick/attribute.h"
#include "magick/blob.h"
#include "magick/colormap.h"
#include "magick/log.h"
#include "magick/magick.h"
#include "magick/monitor.h"
#include "magick/pixel_cache.h"
#include "magick/utility.h"
/*
Typedef declaractions.
*/
/*
The SGI file header is 512 bytes in size.
*/
typedef struct _SGIInfo
{
unsigned short
magic; /* Identification number (474) */
magick_int8_t
storage, /* Compression flag */
bytes_per_pixel; /* Bytes per pixel (per bit plane) */
magick_uint16_t
dimension, /* Number of image dimensions */
xsize, /* Width of image in pixels */
ysize, /* Height of image in pixels */
zsize; /* Number of bit planes */
magick_uint32_t
pix_min, /* Smallest pixel component value */
pix_max; /* Largest pixel component value */
char
dummy1[4]; /* Not used */
char
image_name[80]; /* Name of image (null terminated) */
magick_uint32_t
color_map; /* Format of pixel data */
char
dummy2[404]; /* Not used */
} SGIInfo;
/*
Forward declarations.
*/
static unsigned int
WriteSGIImage(const ImageInfo *,Image *);
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% I s S G I %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Method IsSGI returns True if the image format type, identified by the
% magick string, is SGI.
%
% The format of the IsSGI method is:
%
% unsigned int IsSGI(const unsigned char *magick,const size_t length)
%
% A description of each parameter follows:
%
% o status: Method IsSGI returns True if the image format type is SGI.
%
% o magick: This string is generally the first few bytes of an image file
% or blob.
%
% o length: Specifies the length of the magick string.
%
%
*/
static unsigned int IsSGI(const unsigned char *magick,const size_t length)
{
if (length < 2)
return(False);
if (memcmp(magick,"\001\332",2) == 0)
return(True);
return(False);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e a d S G I I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Method ReadSGIImage reads a SGI RGB image file and returns it. It
% allocates the memory necessary for the new Image structure and returns a
% pointer to the new image.
%
% The format of the ReadSGIImage method is:
%
% Image *ReadSGIImage(const ImageInfo *image_info,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image: Method ReadSGIImage returns a pointer to the image after
% reading. A null image is returned if there is a memory shortage or
% if the image cannot be read.
%
% o image_info: Specifies a pointer to a ImageInfo structure.
%
% o exception: return any errors or warnings in this structure.
%
%
*/
static int SGIDecode(const unsigned long bytes_per_pixel,
unsigned char *max_packets,unsigned char *pixels,
unsigned long npackets,unsigned long npixels)
{
unsigned long
count;
register unsigned char
*p,
*q;
unsigned int
pixel;
p=max_packets;
q=pixels;
if (bytes_per_pixel == 2)
{
for ( ; ; )
{
if (npackets-- == 0)
return -1;
pixel=(*p++) << 8U;
pixel|=(*p++);
count=(pixel & 0x7fU);
if (count == 0)
break;
if (count > npixels)
return -1;
npixels -= count;
if (pixel & 0x80U)
for ( ; count != 0U; count--)
{
if (npackets-- == 0)
return -1;
*q=(*p++);
*(q+1)=(*p++);
q+=8U;
}
else
{
if (npackets-- == 0)
return -1;
pixel=(*p++) << 8U;
pixel|=(*p++);
for ( ; count != 0; count--)
{
*q=(unsigned char) (pixel >> 8U);
*(q+1)=(unsigned char) pixel;
q+=8U;
}
}
}
return 0;
}
for ( ; ; )
{
if (npackets-- == 0)
return -1;
pixel=(*p++);
count= (pixel & 0x7fU);
if (count == 0)
break;
if (count > npixels)
return -1;
npixels -= count;
if (pixel & 0x80)
for ( ; count != 0; count--)
{
if (npackets-- == 0)
return -1;
*q=(*p++);
q+=4;
}
else
{
if (npackets-- == 0)
return -1;
pixel=(*p++);
for ( ; count != 0; count--)
{
*q=(unsigned char) pixel;
q+=4;
}
}
}
return 0;
}
static Image *ReadSGIImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
Image
*image;
long
y,
z;
register IndexPacket
*indexes;
register long
i,
x;
register PixelPacket
*q;
register unsigned char
*p;
SGIInfo
iris_info;
unsigned char
*iris_pixels;
unsigned int
status;
unsigned long
bytes_per_pixel,
number_pixels;
/*
Open image file.
*/
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickSignature);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickSignature);
image=AllocateImage(image_info);
status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
if (status == False)
ThrowReaderException(FileOpenError,UnableToOpenFile,image);
/*
Read SGI raster header.
*/
iris_info.magic=ReadBlobMSBShort(image);
do
{
/*
Verify SGI identifier.
*/
if (iris_info.magic != 0x01DA)
ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
iris_info.storage=ReadBlobByte(image);
iris_info.bytes_per_pixel=ReadBlobByte(image);
iris_info.dimension=ReadBlobMSBShort(image);
iris_info.xsize=ReadBlobMSBShort(image);
iris_info.ysize=ReadBlobMSBShort(image);
iris_info.zsize=ReadBlobMSBShort(image);
iris_info.pix_min=ReadBlobMSBLong(image);
iris_info.pix_max=ReadBlobMSBLong(image);
(void) ReadBlob(image,(unsigned int) sizeof(iris_info.dummy1),
iris_info.dummy1);
(void) ReadBlob(image,(unsigned int) sizeof(iris_info.image_name),
iris_info.image_name);
iris_info.image_name[sizeof(iris_info.image_name)-1]=0;
iris_info.color_map=ReadBlobMSBLong(image);
(void) ReadBlob(image,(unsigned int) sizeof(iris_info.dummy2),
iris_info.dummy2);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Header: Storage=%u, BPC=%u, Dimension=%u, XSize=%u, YSize=%u, ZSize=%u, PixMin=%u, PixMax=%u, image_name=\"%.79s\", color_map=%u",
(unsigned int) iris_info.storage,
(unsigned int) iris_info.bytes_per_pixel,
(unsigned int) iris_info.dimension,
(unsigned int) iris_info.xsize,
(unsigned int) iris_info.ysize,
(unsigned int) iris_info.zsize,
iris_info.pix_min,
iris_info.pix_max,
iris_info.image_name,
iris_info.color_map);
/*
Validate image header and set image attributes.
*/
if (iris_info.storage == 0)
{
/* Uncompressed */
image->compression=NoCompression;
}
else if (iris_info.storage == 1)
{
/* RLE compressed */
image->compression=RLECompression;
}
else
{
/* Error */
ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
}
if (iris_info.color_map != 0)
{
/* We only support images with normal (no) colormap */
ThrowReaderException(CorruptImageError,ImageTypeNotSupported,image);
}
if (iris_info.bytes_per_pixel == 1)
{
/* 8 bits per sample */
image->depth=8;
}
else if (iris_info.bytes_per_pixel == 2)
{
/* 16 bits per sample */
image->depth=Min(16,QuantumDepth);
}
else
{
/* Error */
ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
}
if (iris_info.dimension == 1)
{
/*
Image contains one channel and one scanline, with scanline
length specified by xsize.
*/
image->columns=iris_info.xsize;
image->rows=1;
image->is_grayscale=MagickTrue;
if (iris_info.bytes_per_pixel == 1)
{
/* Use a grayscale colormap */
image->storage_class=PseudoClass;
image->colors=256;
}
}
else if (iris_info.dimension == 2)
{
/*
One channel with a number of scan lines. Width and height
of image are given by the values of xsize and ysize.
*/
image->columns=iris_info.xsize;
image->rows=iris_info.ysize;
image->is_grayscale=MagickTrue;
if (iris_info.bytes_per_pixel == 1)
{
/* Use a grayscale colormap */
image->storage_class=PseudoClass;
image->colors=256;
}
}
else if (iris_info.dimension == 3)
{
/*
A number of channels. Width and height of image are given
by the values of xsize and ysize. The number of channels is
specified by zsize.
B/W images have a zsize of 1. RGB images have a zsize of 3.
RGB images with an alpha channel have a zsize of 4. Images
may have more than four channels but the meaning of
additional channels is implementation dependent.
*/
image->columns=iris_info.xsize;
image->rows=iris_info.ysize;
if (iris_info.zsize == 1)
{
/* B/W image */
image->matte = MagickFalse;
image->is_grayscale=MagickTrue;
if (iris_info.bytes_per_pixel == 1)
{
/* Use a grayscale colormap */
image->storage_class=PseudoClass;
image->colors=256;
}
}
else if (iris_info.zsize == 3)
{
/* RGB */
image->matte=MagickFalse;
}
else if (iris_info.zsize == 4)
{
/* RGBA */
image->matte=MagickTrue;
}
else
{
/* Error */
ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
}
}
else
{
/* Error */
ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
}
if (iris_info.image_name[0])
(void) SetImageAttribute(image,"comment",iris_info.image_name);
if (image_info->ping && (image_info->subrange != 0))
if (image->scene >= (image_info->subimage+image_info->subrange-1))
break;
/*
Allocate SGI pixels.
*/
bytes_per_pixel=iris_info.bytes_per_pixel;
number_pixels=iris_info.xsize*iris_info.ysize;
if (4*bytes_per_pixel*number_pixels < number_pixels) /* Overflow? */
ThrowReaderException(CorruptImageError,InsufficientImageDataInFile,image);
iris_pixels=MagickAllocateMemory(unsigned char *,
4*bytes_per_pixel*number_pixels);
if (iris_pixels == (unsigned char *) NULL)
ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
if (iris_info.storage != 0x01)
{
unsigned char
*scanline;
/*
Read standard image format.
*/
scanline=MagickAllocateMemory(unsigned char *,
bytes_per_pixel*iris_info.xsize);
if (scanline == (unsigned char *) NULL)
ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
image);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Reading SGI scanlines");
for (z=0; z < (int) iris_info.zsize; z++)
{
p=iris_pixels+bytes_per_pixel*z;
for (y=0; y < (long) iris_info.ysize; y++)
{
(void) ReadBlob(image,bytes_per_pixel*iris_info.xsize,
(char *) scanline);
if (EOFBlob(image))
{
ThrowReaderException(CorruptImageError,
UnexpectedEndOfFile, image);
break;
}
if (bytes_per_pixel == 2)
for (x=0; x < (long) iris_info.xsize; x++)
{
*p=scanline[2*x];
*(p+1)=scanline[2*x+1];
p+=8;
}
else
for (x=0; x < (long) iris_info.xsize; x++)
{
*p=scanline[x];
p+=4;
}
}
if (EOFBlob(image))
break;
}
MagickFreeMemory(scanline);
}
else
{
unsigned char
*max_packets;
unsigned int
data_order;
unsigned long
offset,
*offsets,
*runlength;
/*
Read runlength-encoded image format.
*/
offsets=MagickAllocateMemory(unsigned long *,iris_info.ysize*
iris_info.zsize*sizeof(unsigned long));
max_packets=MagickAllocateArray(unsigned char *,iris_info.xsize+10,4);
runlength=MagickAllocateMemory(unsigned long *,iris_info.ysize*
iris_info.zsize*sizeof(unsigned long));
if ((offsets == (unsigned long *) NULL) ||
(max_packets == (unsigned char *) NULL) ||
(runlength == (unsigned long *) NULL))
ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
image);
for (i=0; i < (int) (iris_info.ysize*iris_info.zsize); i++)
offsets[i]=ReadBlobMSBLong(image);
for (i=0; i < (int) (iris_info.ysize*iris_info.zsize); i++)
{
runlength[i]=ReadBlobMSBLong(image);
if (EOFBlob(image))
ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
if (runlength[i] > ((unsigned long) 4*iris_info.xsize+10))
ThrowReaderException(CorruptImageError,UnableToRunlengthDecodeImage,image);
}
/*
Check data order.
*/
offset=0;
data_order=0;
for (y=0; ((y < (long) iris_info.ysize) && !data_order); y++)
for (z=0; ((z < (int) iris_info.zsize) && !data_order); z++)
{
if (offsets[y+z*iris_info.ysize] < offset)
data_order=1;
offset=offsets[y+z*iris_info.ysize];
}
offset=TellBlob(image);
if (data_order == 1)
{
for (z=0; z < (int) iris_info.zsize; z++)
{
p=iris_pixels;
for (y=0; y < (long) iris_info.ysize; y++)
{
if (offset != offsets[y+z*iris_info.ysize])
{
offset=offsets[y+z*iris_info.ysize];
(void) SeekBlob(image,(long) offset,SEEK_SET);
}
(void) ReadBlob(image,runlength[y+z*iris_info.ysize],
(char *) max_packets);
if (EOFBlob(image))
{
ThrowReaderException(CorruptImageError,
UnexpectedEndOfFile, image);
break;
}
offset+=runlength[y+z*iris_info.ysize];
if (SGIDecode(bytes_per_pixel,max_packets,p+bytes_per_pixel*z,
runlength[y+z*iris_info.ysize]/bytes_per_pixel,
iris_info.xsize) == -1)
ThrowReaderException(CorruptImageError,
UnableToRunlengthDecodeImage,image);
p+=(iris_info.xsize*4*bytes_per_pixel);
}
}
}
else
{
p=iris_pixels;
for (y=0; y < (long) iris_info.ysize; y++)
{
for (z=0; z < (int) iris_info.zsize; z++)
{
if (offset != offsets[y+z*iris_info.ysize])
{
offset=offsets[y+z*iris_info.ysize];
(void) SeekBlob(image,(long) offset,SEEK_SET);
}
(void) ReadBlob(image,runlength[y+z*iris_info.ysize],
(char *) max_packets);
if (EOFBlob(image))
{
ThrowReaderException(CorruptImageError,
UnexpectedEndOfFile, image);
break;
}
offset+=runlength[y+z*iris_info.ysize];
if (SGIDecode(bytes_per_pixel,max_packets,p+bytes_per_pixel*z,
runlength[y+z*iris_info.ysize]/bytes_per_pixel,
iris_info.xsize) == -1)
ThrowReaderException(CorruptImageError,
UnableToRunlengthDecodeImage,image);
}
p+=(iris_info.xsize*4*bytes_per_pixel);
if (EOFBlob(image))
break;
}
}
MagickFreeMemory(runlength);
MagickFreeMemory(max_packets);
MagickFreeMemory(offsets);
}
/*
Convert SGI raster image to pixel packets.
*/
if (image->storage_class == DirectClass)
{
/*
Convert SGI image to DirectClass pixel packets.
*/
if (bytes_per_pixel == 2)
{
for (y=0; y < (long) image->rows; y++)
{
p=iris_pixels+(image->rows-y-1)*8*image->columns;
q=SetImagePixels(image,0,y,image->columns,1);
if (q == (PixelPacket *) NULL)
break;
for (x=0; x < (long) image->columns; x++)
{
q->red=ScaleShortToQuantum((*(p+0) << 8) | (*(p+1)));
q->green=ScaleShortToQuantum((*(p+2) << 8) | (*(p+3)));
q->blue=ScaleShortToQuantum((*(p+4) << 8) | (*(p+5)));
if (image->matte)
q->opacity=(Quantum)
(MaxRGB-ScaleShortToQuantum((*(p+6) << 8) | (*(p+7))));
else
q->opacity=OpaqueOpacity;
p+=8;
q++;
}
if (!SyncImagePixels(image))
break;
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,exception,
LoadImageText,image->filename,
image->columns,image->rows))
break;
}
}
else
for (y=0; y < (long) image->rows; y++)
{
p=iris_pixels+(image->rows-y-1)*4*image->columns;
q=SetImagePixels(image,0,y,image->columns,1);
if (q == (PixelPacket *) NULL)
break;
for (x=0; x < (long) image->columns; x++)
{
q->red=ScaleCharToQuantum(*p);
q->green=ScaleCharToQuantum(*(p+1));
q->blue=ScaleCharToQuantum(*(p+2));
if (image->matte)
q->opacity=(Quantum) (MaxRGB-ScaleCharToQuantum(*(p+3)));
else
q->opacity=OpaqueOpacity;
p+=4;
q++;
}
if (!SyncImagePixels(image))
break;
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,exception,
LoadImageText,image->filename,
image->columns,image->rows))
break;
}
}
else
{
/*
Create grayscale map.
*/
if (!AllocateImageColormap(image,image->colors))
ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
image);
/*
Convert SGI image to PseudoClass pixel packets.
*/
if (bytes_per_pixel == 2)
{
for (y=0; y < (long) image->rows; y++)
{
p=iris_pixels+(image->rows-y-1)*8*image->columns;
q=SetImagePixels(image,0,y,image->columns,1);
if (q == (PixelPacket *) NULL)
break;
indexes=AccessMutableIndexes(image);
for (x=0; x < (long) image->columns; x++)
{
indexes[x]=(*p << 8);
indexes[x]|=(*(p+1));
p+=8;
q++;
}
if (!SyncImagePixels(image))
break;
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,exception,
LoadImageText,image->filename,
image->columns,image->rows))
break;
}
}
else
for (y=0; y < (long) image->rows; y++)
{
p=iris_pixels+(image->rows-y-1)*4*image->columns;
q=SetImagePixels(image,0,y,image->columns,1);
if (q == (PixelPacket *) NULL)
break;
indexes=AccessMutableIndexes(image);
for (x=0; x < (long) image->columns; x++)
{
indexes[x]=(*p);
p+=4;
q++;
}
if (!SyncImagePixels(image))
break;
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,exception,
LoadImageText,image->filename,
image->columns,image->rows))
break;
}
(void) SyncImage(image);
}
MagickFreeMemory(iris_pixels);
if (EOFBlob(image))
{
ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,
image);
break;
}
/*
Proceed to next image.
*/
if (image_info->subrange != 0)
if (image->scene >= (image_info->subimage+image_info->subrange-1))
break;
iris_info.magic=ReadBlobMSBShort(image);
if (iris_info.magic == 0x01DA)
{
/*
Allocate next image structure.
*/
AllocateNextImage(image_info,image);
if (image->next == (Image *) NULL)
{
DestroyImageList(image);
return((Image *) NULL);
}
image=SyncNextImageInList(image);
if (!MagickMonitorFormatted(TellBlob(image),GetBlobSize(image),exception,
LoadImagesText,image->filename))
break;
}
} while (iris_info.magic == 0x01DA);
while (image->previous != (Image *) NULL)
image=image->previous;
CloseBlob(image);
return(image);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e g i s t e r S G I I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Method RegisterSGIImage adds attributes for the SGI image format to
% the list of supported formats. The attributes include the image format
% tag, a method to read and/or write the format, whether the format
% supports the saving of more than one frame to the same file or blob,
% whether the format supports native in-memory I/O, and a brief
% description of the format.
%
% The format of the RegisterSGIImage method is:
%
% RegisterSGIImage(void)
%
*/
ModuleExport void RegisterSGIImage(void)
{
MagickInfo
*entry;
entry=SetMagickInfo("SGI");
entry->decoder=(DecoderHandler) ReadSGIImage;
entry->encoder=(EncoderHandler) WriteSGIImage;
entry->magick=(MagickHandler) IsSGI;
entry->description="Irix RGB image";
entry->module="SGI";
entry->seekable_stream=True;
(void) RegisterMagickInfo(entry);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% U n r e g i s t e r S G I I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Method UnregisterSGIImage removes format registrations made by the
% SGI module from the list of supported formats.
%
% The format of the UnregisterSGIImage method is:
%
% UnregisterSGIImage(void)
%
*/
ModuleExport void UnregisterSGIImage(void)
{
(void) UnregisterMagickInfo("SGI");
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% W r i t e S G I I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Method WriteSGIImage writes an image in SGI RGB encoded image format.
%
% The format of the WriteSGIImage method is:
%
% unsigned int WriteSGIImage(const ImageInfo *image_info,Image *image)
%
% A description of each parameter follows.
%
% o status: Method WriteSGIImage return True if the image is written.
% False is returned is there is a memory shortage or if the image file
% fails to write.
%
% o image_info: Specifies a pointer to a ImageInfo structure.
%
% o image: A pointer to an Image structure.
%
%
*/
static size_t SGIEncode(unsigned char *pixels,size_t count,
unsigned char *packets)
{
short
runlength;
register unsigned char
*p,
*q;
unsigned char
*limit,
*mark;
p=pixels;
limit=p+count*4;
q=packets;
while (p < limit)
{
mark=p;
p+=8;
while ((p < limit) && ((*(p-8) != *(p-4)) || (*(p-4) != *p)))
p+=4;
p-=8;
count=((p-mark) >> 2);
while (count)
{
runlength=(short) (count > 126 ? 126 : count);
count-=runlength;
*q++=0x80 | runlength;
for ( ; runlength > 0; runlength--)
{
*q++=(*mark);
mark+=4;
}
}
mark=p;
p+=4;
while ((p < limit) && (*p == *mark))
p+=4;
count=((p-mark) >> 2);
while (count)
{
runlength=(short) (count > 126 ? 126 : count);
count-=runlength;
*q++=(unsigned char) runlength;
*q++=(*mark);
}
}
*q++=0;
return(q-packets);
}
static unsigned int WriteSGIImage(const ImageInfo *image_info,Image *image)
{
long
y,
z;
SGIInfo
iris_info;
register const PixelPacket
*p;
register long
i,
x;
register unsigned char
*q;
unsigned char
*iris_pixels,
*packets;
unsigned int
status;
unsigned long
number_pixels,
scene;
/*
Open output image file.
*/
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickSignature);
assert(image != (Image *) NULL);
assert(image->signature == MagickSignature);
if ((image->columns > 65535L) || (image->rows > 65535L))
ThrowWriterException(ImageError,WidthOrHeightExceedsLimit,image);
status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
if (status == MagickFail)
ThrowWriterException(FileOpenError,UnableToOpenFile,image);
scene=0;
do
{
ImageCharacteristics
characteristics;
/*
Ensure that image is in an RGB space.
*/
(void) TransformColorspace(image,RGBColorspace);
/*
Analyze image to be written.
*/
(void) GetImageCharacteristics(image,&characteristics,
(OptimizeType == image_info->type),
&image->exception);
/*
Initialize SGI raster file header.
*/
iris_info.magic=0x01DA;
if (image_info->compression == NoCompression)
iris_info.storage=0x00;
else
iris_info.storage=0x01;
iris_info.bytes_per_pixel=1; /* one byte per pixel */
iris_info.dimension=3;
iris_info.xsize=(unsigned short) image->columns;
iris_info.ysize=(unsigned short) image->rows;
if (image->matte != MagickFalse)
iris_info.zsize=4;
else
{
if ((image_info->type != TrueColorType) &&
(characteristics.grayscale))
{
iris_info.dimension=2;
iris_info.zsize=1;
}
else
iris_info.zsize=3;
}
iris_info.pix_min=0;
iris_info.pix_max=ScaleQuantumToChar(MaxRGB);
(void) memset(iris_info.dummy1,0,sizeof(iris_info.dummy1));
{
const ImageAttribute
*attribute;
(void) memset(iris_info.image_name,0,sizeof(iris_info.image_name));
if ((attribute=GetImageAttribute(image,"comment")))
(void) strlcpy(iris_info.image_name,attribute->value,sizeof(iris_info.image_name));
}
iris_info.color_map=0;
(void) memset(iris_info.dummy2,0,sizeof(iris_info.dummy2));
/*
Write SGI header.
*/
(void) WriteBlobMSBShort(image,iris_info.magic);
(void) WriteBlobByte(image,iris_info.storage);
(void) WriteBlobByte(image,iris_info.bytes_per_pixel);
(void) WriteBlobMSBShort(image,iris_info.dimension);
(void) WriteBlobMSBShort(image,iris_info.xsize);
(void) WriteBlobMSBShort(image,iris_info.ysize);
(void) WriteBlobMSBShort(image,iris_info.zsize);
(void) WriteBlobMSBLong(image,iris_info.pix_min);
(void) WriteBlobMSBLong(image,iris_info.pix_max);
(void) WriteBlob(image,sizeof(iris_info.dummy1),
(char *) iris_info.dummy1);
(void) WriteBlob(image,sizeof(iris_info.image_name),
(char *) iris_info.image_name);
(void) WriteBlobMSBLong(image,iris_info.color_map);
(void) WriteBlob(image,sizeof(iris_info.dummy2),
(char *) iris_info.dummy2);
/*
Allocate SGI pixels.
*/
number_pixels=image->columns*image->rows;
iris_pixels=MagickAllocateMemory(unsigned char *,4*number_pixels);
if (iris_pixels == (unsigned char *) NULL)
ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);
/*
Convert image pixels to uncompressed SGI pixels.
*/
for (y=0; y < (long) image->rows; y++)
{
p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
if (p == (const PixelPacket *) NULL)
break;
q=iris_pixels+((iris_info.ysize-1)-y)*(iris_info.xsize*4);
for (x=0; x < (long) image->columns; x++)
{
*q++=ScaleQuantumToChar(p->red);
*q++=ScaleQuantumToChar(p->green);
*q++=ScaleQuantumToChar(p->blue);
*q++=ScaleQuantumToChar(MaxRGB-p->opacity);
p++;
}
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,&image->exception,
SaveImageText,image->filename,
image->columns,image->rows))
break;
}
if (image_info->compression == NoCompression)
{
unsigned char
*scanline;
/*
Write uncompressed SGI pixels.
*/
scanline=MagickAllocateMemory(unsigned char *,iris_info.xsize);
if (scanline == (unsigned char *) NULL)
ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,
image);
for (z=0; z < (int) iris_info.zsize; z++)
{
q=iris_pixels+z;
for (y=0; y < (long) iris_info.ysize; y++)
{
for (x=0; x < (long) iris_info.xsize; x++)
{
scanline[x]=(*q);
q+=4;
}
(void) WriteBlob(image,iris_info.xsize,(char *) scanline);
}
}
MagickFreeMemory(scanline);
}
else
{
unsigned long
length,
number_packets,
offset,
*offsets,
*runlength;
/*
Convert SGI uncompressed pixels.
*/
offsets=MagickAllocateMemory(unsigned long *,iris_info.ysize*
iris_info.zsize*sizeof(unsigned long));
packets=MagickAllocateMemory(unsigned char *,
4*(2*iris_info.xsize+10)*image->rows);
runlength=MagickAllocateMemory(unsigned long *,iris_info.ysize*
iris_info.zsize*sizeof(unsigned long));
if ((offsets == (unsigned long *) NULL) ||
(packets == (unsigned char *) NULL) ||
(runlength == (unsigned long *) NULL))
ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,
image);
offset=512+4*2*(iris_info.ysize*iris_info.zsize);
number_packets=0;
q=iris_pixels;
for (y=0; y < (long) iris_info.ysize; y++)
{
for (z=0; z < (int) iris_info.zsize; z++)
{
length=
SGIEncode(q+z,(int) iris_info.xsize,packets+number_packets);
number_packets+=length;
offsets[y+z*iris_info.ysize]=offset;
runlength[y+z*iris_info.ysize]=length;
offset+=length;
}
q+=(iris_info.xsize*4);
}
/*
Write out line start and length tables and runlength-encoded pixels.
*/
for (i=0; i < (int) (iris_info.ysize*iris_info.zsize); i++)
(void) WriteBlobMSBLong(image,offsets[i]);
for (i=0; i < (int) (iris_info.ysize*iris_info.zsize); i++)
(void) WriteBlobMSBLong(image,runlength[i]);
(void) WriteBlob(image,number_packets,(char *) packets);
/*
Free memory.
*/
MagickFreeMemory(runlength);
MagickFreeMemory(packets);
MagickFreeMemory(offsets);
}
MagickFreeMemory(iris_pixels);
if (image->next == (Image *) NULL)
break;
image=SyncNextImageInList(image);
if (!MagickMonitorFormatted(scene++,GetImageListLength(image),
&image->exception,SaveImagesText,
image->filename))
break;
} while (image_info->adjoin);
if (image_info->adjoin)
while (image->previous != (Image *) NULL)
image=image->previous;
CloseBlob(image);
return(True);
}