+<html><head><title>SGI Image File Format Specification</title></head><body bgcolor="#ffffff">\r
+<font face="Helvetica, Arial, sans-serif" size="-1">\r
+</font><center>\r
+<h3><font face="Helvetica, Arial, sans-serif" size="-1">The SGI Image File Format</font></h3>\r
+<p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1">Version 1.00\r
+</font></p><p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1">Paul Haeberli<br>\r
+Silicon Graphics Computer Systems<br>\r
+paulhaeberli@yahoo.com<br>\r
+</font></p></center>\r
+<h3><font face="Helvetica, Arial, sans-serif" size="-1">Introduction</font></h3>\r
+<p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1">This is the definitive document describing the SGI image file format. This \r
+is a low level spec that describes the actual byte level format of SGI image\r
+files. On SGI machines the preferred way of reading and writing SGI image\r
+files is to use the image library -limage. This library provides a set\r
+of functions that make it easy to read and write SGI images. If you are \r
+on an SGI workstation you can get info on -limage by doing:\r
+</font></p><blockquote><pre><font face="Helvetica, Arial, sans-serif" size="-1">% man 4 rgb\r
+</font></pre></blockquote>\r
+<h3><font face="Helvetica, Arial, sans-serif" size="-1">A note on byte order of values in the SGI image files</font></h3>\r
+<p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1">In the following description a notation like bits[7..0] is used to denote\r
+a range of bits in a binary value. Bit 0 is the lowest order bit in\r
+the value.\r
+</font></p><p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1">All short values are represented by 2 bytes. The first byte stores the\r
+high order 8 bits of the value: bits[15..8]. The second byte stores\r
+the low order 8 bits of the value: bits[7..0]. \r
+</font></p><p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1">So this function will read a short value from the file:\r
+</font></p><pre><font face="Helvetica, Arial, sans-serif" size="-1"> unsigned short getshort(inf)\r
+ FILE *inf;\r
+ {\r
+ unsigned char buf[2];\r
+\r
+ fread(buf,2,1,inf);\r
+ return (buf[0]<<8)+(buf[1]<<0);\r
+ }\r
+</font></pre>\r
+<p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1">All long values are represented by 4 bytes. The first byte stores the\r
+high order 8 bits of the value: bits[31..24]. The second byte stores\r
+bits[23..16]. The third byte stores bits[15..8]. The forth byte stores\r
+the low order 8 bits of the value: bits[7..0]. \r
+</font></p><p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1">And this function will read a long value from the file:\r
+</font></p><pre><font face="Helvetica, Arial, sans-serif" size="-1"> static long getlong(inf)\r
+ FILE *inf;\r
+ {\r
+ unsigned char buf[4];\r
+\r
+ fread(buf,4,1,inf);\r
+ return (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+(buf[3]<<0);\r
+ }\r
+</font></pre>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> \r
+</font><p>\r
+</p><h3><font face="Helvetica, Arial, sans-serif" size="-1">The general structure of an SGI image file</font></h3>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> The header indicates whether the image is run length encoded (RLE).\r
+</font><p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> If the image is not run length encoded, this is the structure:\r
+</font></p><blockquote>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> The Header<br>\r
+ The Image Data\r
+</font></blockquote>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ If the image is run length encoded, this is the structure:\r
+</font><blockquote>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> The Header<br>\r
+ The Offset Tables<br>\r
+ The Image Data\r
+</font></blockquote>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> \r
+</font><h3><font face="Helvetica, Arial, sans-serif" size="-1">The Header</font></h3>\r
+<p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1">The header consists of the following:\r
+</font></p><pre><font face="Helvetica, Arial, sans-serif" size="-1"> Size | Type | Name | Description \r
+ \r
+ 2 bytes | short | MAGIC | IRIS image file magic number\r
+ 1 byte | char | STORAGE | Storage format\r
+ 1 byte | char | BPC | Number of bytes per pixel channel \r
+ 2 bytes | ushort | DIMENSION | Number of dimensions\r
+ 2 bytes | ushort | XSIZE | X size in pixels \r
+ 2 bytes | ushort | YSIZE | Y size in pixels \r
+ 2 bytes | ushort | ZSIZE | Number of channels\r
+ 4 bytes | long | PIXMIN | Minimum pixel value\r
+ 4 bytes | long | PIXMAX | Maximum pixel value\r
+ 4 bytes | char | DUMMY | Ignored\r
+ 80 bytes | char | IMAGENAME | Image name\r
+ 4 bytes | long | COLORMAP | Colormap ID\r
+ 404 bytes | char | DUMMY | Ignored\r
+</font></pre>\r
+<blockquote>\r
+<p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+Here is a description of each field in the image file header:\r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ MAGIC - This is the decimal value 474 saved as a short. This\r
+ identifies the file as an SGI image file.\r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ STORAGE - specifies whether the image is stored using run\r
+ length encoding (RLE) or not (VERBATIM). If RLE is used, the value \r
+ of this byte will be 1. Otherwise the value of this byte will be 0.\r
+ The only allowed values for this field are 0 or 1.\r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ BPC - describes the precision that is used to store each\r
+ channel of an image. This is the number of bytes per pixel\r
+ component. The majority of SGI image files use 1 byte per \r
+ pixel component, giving 256 levels. Some SGI image files use \r
+ 2 bytes per component. The only allowed values for this field \r
+ are 1 or 2.\r
+ \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ DIMENSION - described the number of dimensions in the data stored\r
+ in the image file. The only allowed values are 1, 2, or 3. If\r
+ this value is 1, the image file consists of only 1 channel and \r
+ only 1 scanline (row). The length of this scanline is given by the \r
+ value of XSIZE below. If this value is 2, the file consists of a \r
+ single channel with a number of scanlines. The width and height\r
+ of the image are given by the values of XSIZE and YSIZE below.\r
+ If this value is 3, the file consists of a number of channels.\r
+ The width and height of the image are given by the values of \r
+ XSIZE and YSIZE below. The number of channels is given by the \r
+ value of ZSIZE below. \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ XSIZE - The width of the image in pixels\r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ \r
+ YSIZE - The height of the image in pixels\r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ \r
+ ZSIZE - The number of channels in the image. B/W (greyscale) images \r
+ are stored as 2 dimensional images with a ZSIZE or 1. RGB color \r
+ images are stored as 3 dimensional images with a ZSIZE of 3. An RGB \r
+ image with an ALPHA channel is stored as a 3 dimensional image with \r
+ a ZSIZE of 4. There are no inherent limitations in the SGI image \r
+ file format that would preclude the creation of image files with more \r
+ than 4 channels.\r
+ \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ PINMIN - The minimum pixel value in the image. The value of\r
+ 0 may be used if no pixel has a value that is smaller than 0.\r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ \r
+ PINMAX - The maximum pixel value in the image. The value of\r
+ 255 may be used if no pixel has a value that is greater than 255.\r
+ This is the value that is considered to be full brightness in \r
+ the image. \r
+ \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ DUMMY - This 4 bytes of data should be set to 0. \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ \r
+ IMAGENAME - An null terminated ascii string of up to 79 characters \r
+ terminated by a null may be included here. This is not commonly\r
+ used.\r
+ \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ COLORMAP - This controls how the pixel values in the file should be\r
+ interpreted. It can have one of these four values:\r
+ \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ 0: NORMAL - The data in the channels represent B/W values\r
+ for images with 1 channel, RGB values for images with 3\r
+ channels, and RGBA values for images with 4 channels.\r
+ Almost all the SGI image files are of this type. \r
+ \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ 1: DITHERED - The image will have only 1 channel of data.\r
+ For each pixel, RGB data is packed into one 8 bit value.\r
+ 3 bits are used for red and green, while blue uses 2 bits.\r
+ Red data is found in bits[2..0], green data in bits[5..3],\r
+ and blue data in bits[7..6]. This format is obsolete.\r
+ \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ 2: SCREEN - The image will have only 1 channel of data.\r
+ This format was used to store color-indexed pixels.\r
+ To convert the pixel values into RGB values a colormap\r
+ must be used. The appropriate color map varies from\r
+ image to image. This format is obsolete.\r
+ \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ 3: COLORMAP - The image is used to store a color map from\r
+ an SGI machine. In this case the image is not displayable\r
+ in the conventional sense.\r
+ \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ DUMMY - This 404 bytes of data should be set to 0. This makes the\r
+ header exactly 512 bytes. \r
+</font></p></blockquote>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> \r
+</font><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+</font></p><h3><font face="Helvetica, Arial, sans-serif" size="-1">The Image Data (if not RLE)</font></h3>\r
+<p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ If the image is stored verbatim (without RLE), then image data directly\r
+ follows the 512 byte header. The data for each scanline of the first\r
+ channel is written first. If the image has more than 1 channel, all\r
+ the data for the first channel is written, followed by the remaining\r
+ channels. If the BPC value is 1, then each scanline is written as XSIZE\r
+ bytes. If the BPC value is 2, then each scanline is written as XSIZE \r
+ shorts. These shorts are stored in the byte order described above.\r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+</font></p><h3><font face="Helvetica, Arial, sans-serif" size="-1">The Offset Tables (if RLE)</font></h3>\r
+<p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ If the image is stored using run length encoding, offset tables\r
+ follow the header that describe what the file offsets are to the \r
+ RLE for each scanline. This information only applies if the value \r
+ for STORAGE above is 1.\r
+</font></p><pre><font face="Helvetica, Arial, sans-serif" size="-1"> Size | Type | Name | Description \r
+\r
+ tablen longs | long | STARTTAB | Start table\r
+ tablen longs | long | LENGTHTAB | Length table\r
+</font></pre><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+</font><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ One entry in each table is needed for each scanline of RLE data. The \r
+ total number of scanlines in the image (tablen) is determined by the\r
+ product of the YSIZE and ZSIZE. There are two tables of longs that \r
+ are written. Each consists of tablen longs of data. The first\r
+ table has the file offsets to the RLE data for each scanline in the\r
+ image. In a file with more than 1 channel (ZSIZE > 1) this table first \r
+ has all the offsets for the scanlines in the first channel, followed\r
+ be offsets for the scanlines in the second channel, etc. The second \r
+ table has the RLE data length for each scanline in the image. In a \r
+ file with more than 1 channel (ZSIZE > 1) this table first has all the \r
+ RLE data lengths for the scanlines in the first channel, followed\r
+ be RLE data lengths for the scanlines in the second channel, etc.\r
+ \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ To find the the file offset, and the number of bytes in the RLE data \r
+ for a particular scanline, these two arrays may be read in and indexed as\r
+ follows: \r
+ \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ To read in the tables:\r
+</font></p><pre><font face="Helvetica, Arial, sans-serif" size="-1"> unsigned long *starttab, *lengthtab;\r
+\r
+ tablen = YSIZE*ZSIZE*sizeof(long);\r
+ starttab = (unsigned long *)mymalloc(tablen);\r
+ lengthtab = (unsigned long *)mymalloc(tablen);\r
+ fseek(inf,512,SEEK_SET);\r
+ readlongtab(inf,starttab);\r
+ readlongtab(ing,lengthtab);\r
+</font></pre><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+\r
+</font><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ To find the file offset and RLE data length for a scanline:\r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ rowno is an integer in the range 0 to YSIZE-1\r
+ channo is an integer in the range 0 to ZSIZE-1\r
+</font></p><pre><font face="Helvetica, Arial, sans-serif" size="-1"> rleoffset = starttab[rowno+channo*YSIZE]\r
+ rlelength = lengthtab[rowno+channo*YSIZE]\r
+</font></pre>\r
+<p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ It is possible for two identical rows (scanlines) to share compressed \r
+ data. A completely white image could be written as a single compressed \r
+ row and having all table entries point to that row. Another little hack \r
+ that should work is if you are writing out a RGB RLE file, and a \r
+ particular scanline is achromatic (greyscale), you could just make the \r
+ r, g and b rows point to the same data!!\r
+</font></p><h3><font face="Helvetica, Arial, sans-serif" size="-1">The Image Data (if RLE)</font></h3>\r
+<p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ This information only applies if the value for STORAGE above is 1. If\r
+ the image is stored using run length encoding, the image data follows\r
+ the offset tables above. The RLE data is not in any particular order.\r
+ The offset tables above are used to locate the rle data for any scanline.\r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ The RLE data must be read in from the file and expanded into pixel \r
+ data in the following manner:\r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ If BPC is 1, then there is one byte per pixel. In this case the \r
+ RLE data should be read into an array of chars. To expand\r
+ data, the low order seven bits of the first byte: bits[6..0]\r
+ are used to form a count. If the high order bit of the first\r
+ byte is 1: bit[7], then the count is used to specify how many\r
+ bytes to copy from the RLE data buffer to the destination.\r
+ Otherwise, if the high order bit of the first byte is 0: bit[7],\r
+ then the count is used to specify how many times to repeat the \r
+ value of the following byte, in the destination. This process\r
+ continues until a count of 0 is found. This should decompress\r
+ exactly XSIZE pixels. \r
+</font></p><p><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+ Here is example code to decompress a scanline:\r
+</font></p><pre><font face="Helvetica, Arial, sans-serif" size="-1"> expandrow(optr,iptr,z)\r
+ unsigned char *optr, *iptr;\r
+ int z;\r
+ {\r
+ unsigned char pixel, count;\r
+ \r
+ optr += z;\r
+ while(1) {\r
+ pixel = *iptr++;\r
+ if ( !(count = (pixel & 0x7f)) )\r
+ return;\r
+ if(pixel & 0x80) {\r
+ while(count--) \r
+ *optr++ = *iptr++;\r
+ } else {\r
+ pixel = *iptr++;\r
+ while(count--) \r
+ *optr++ = pixel;\r
+ }\r
+ }\r
+ }\r
+</font></pre><font face="Helvetica, Arial, sans-serif" size="-1"> \r
+</font><p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> If BPC is 2, there is one short (2 bytes) per pixel. In this \r
+ case the RLE data should be read into an array of shorts. To \r
+ expand data, the low order seven bits of the first short: bits[6..0]\r
+ are used to form a count. If bit[7] of the first short is 1, then \r
+ the count is used to specify how many shorts to copy from the RLE \r
+ data buffer to the destination. Otherwise, if bit[7] of the first \r
+ short is 0, then the count is used to specify how many times to \r
+ repeat the value of the following short, in the destination. This \r
+ process proceeds until a count of 0 is found. This should decompress\r
+ exactly XSIZE pixels. Note that the byte order of short data in\r
+ the input file should be used, as described above.\r
+</font></p><h3><font face="Helvetica, Arial, sans-serif" size="-1">Implementation notes</font></h3>\r
+<p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> Implementation of both RLE and VERBATIM format for images with\r
+ BPC of 1 is required since the great majority of SGI images are in\r
+ this format. Support for images with a 2 BPC is encouraged.\r
+</font></p><p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> If the ZSIZE of an image is 1, it is assumed to represent B/W\r
+ values. If the ZSIZE is 3, it is assumed to represent RGB data,\r
+ and if ZSIZE is 4, it is assumed to contain RGB data with alpha.\r
+</font></p><p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> The origin for all SGI images is the lower left hand corner. The\r
+ first scanline (row 0) is always the bottom row of the image. \r
+</font></p><h3><font face="Helvetica, Arial, sans-serif" size="-1">Naming Conventions</font></h3>\r
+<p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> On SGI systems, SGI image files end with the extension .bw if\r
+ they are B/W images, they end in .rgb if they contain RGB image\r
+ data, and end in .rgba if they are RGB images with alpha channel.\r
+</font></p><p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1"> Sometimes the .sgi extension is used as well.\r
+</font></p><h3><font face="Helvetica, Arial, sans-serif" size="-1">An example</font></h3>\r
+<p>\r
+<font face="Helvetica, Arial, sans-serif" size="-1">This program will write out a valid B/W SGI image file:\r
+</font></p><pre><font face="Helvetica, Arial, sans-serif" size="-1"> #include "stdio.h"\r
+ \r
+ #define IXSIZE (23)\r
+ #define IYSIZE (15)\r
+ \r
+ putbyte(outf,val)\r
+ FILE *outf;\r
+ unsigned char val;\r
+ {\r
+ unsigned char buf[1];\r
+ \r
+ buf[0] = val;\r
+ fwrite(buf,1,1,outf);\r
+ }\r
+ \r
+ putshort(outf,val)\r
+ FILE *outf;\r
+ unsigned short val;\r
+ {\r
+ unsigned char buf[2];\r
+ \r
+ buf[0] = (val>>8);\r
+ buf[1] = (val>>0);\r
+ fwrite(buf,2,1,outf);\r
+ }\r
+ \r
+ static int putlong(outf,val)\r
+ FILE *outf;\r
+ unsigned long val;\r
+ {\r
+ unsigned char buf[4];\r
+ \r
+ buf[0] = (val>>24);\r
+ buf[1] = (val>>16);\r
+ buf[2] = (val>>8);\r
+ buf[3] = (val>>0);\r
+ return fwrite(buf,4,1,outf);\r
+ }\r
+ \r
+ main()\r
+ {\r
+ FILE *of;\r
+ char iname[80];\r
+ unsigned char outbuf[IXSIZE];\r
+ int i, x, y;\r
+ \r
+ of = fopen("example.rgb","w");\r
+ if(!of) {\r
+ fprintf(stderr,"sgiimage: can't open output file\n");\r
+ exit(1);\r
+ }\r
+ putshort(of,474); /* MAGIC */\r
+ putbyte(of,0); /* STORAGE is VERBATIM */\r
+ putbyte(of,1); /* BPC is 1 */\r
+ putshort(of,2); /* DIMENSION is 2 */\r
+ putshort(of,IXSIZE); /* XSIZE */\r
+ putshort(of,IYSIZE); /* YSIZE */\r
+ putshort(of,1); /* ZSIZE */\r
+ putlong(of,0); /* PIXMIN is 0 */\r
+ putlong(of,255); /* PIXMAX is 255 */\r
+ for(i=0; i<4; i++) /* DUMMY 4 bytes */\r
+ putbyte(of,0);\r
+ strcpy(iname,"No Name");\r
+ fwrite(iname,80,1,of); /* IMAGENAME */\r
+ putlong(of,0); /* COLORMAP is 0 */\r
+ for(i=0; i<404; i++) /* DUMMY 404 bytes */\r
+ putbyte(of,0);\r
+ \r
+ for(y=0; y<IYSIZE; y++) {\r
+ for(x=0; x<IXSIZE; x++) \r
+ outbuf[x] = (255*x)/(IXSIZE-1);\r
+ fwrite(outbuf,IXSIZE,1,of);\r
+ }\r
+ fclose(of);\r
+ }\r
+</font></pre>\r
+\r
+</body></html>
\ No newline at end of file