Making of the SGI Image Plugin

Why this plugin?
In our studio, we use Silicon Graphics (SGI) workstations to do visual effects work for movies and television. Consequently, a lot of images are generated and stored in the SGI image file format (referred hereafter as SGI images). Personal Computers (PCs) are fast replacing our SGI workstations as a cost-effective alternative. But our film scanners and recorders are still connected to SGI workstations and process only SGI images. We too want to work with SGI images because we are comfortable with it and have pulled off so many successful projects with it. On the PC front, Photoshop is a great program for analyzing images. But Photoshop does not read or write SGI images. So our artists convert the SGI images into TIFF images, load them into Photoshop, manipulate them, save them in TIFF format and convert them back to SGI format. The only problem with this approach apart from the time spent on two way conversion is the amount of disk space required to store the temporary TIFF images. A frame scanned at film resolution (2048w x 1736h) takes 10 MB of disk space if the image data is stored verbatim. A typical shot may have (at least) 100 frames amounting to 1 GB of disk space. The temporary TIFF files with image compression turned on take (at least) 250 MB. Manipulated TIFF files will take another 250 MB (because you don't want to overwrite the original, just in case you wanted to start all over). Conversion back to SGI uncompressed images will take another 1 GB. Therefore, there is a waste of about 500 MB due to lack of direct support for SGI images in Photoshop. All this for a shot with a typical minimum of frames. Imagine a bigger shot of say 2000 frames. That will amount to 10 GB of disk space wastage. This is enough requirement to develop a plugin that would read and write SGI images directly in Photoshop.

Adobe Photoshop Plugin Architecture:
There are two things that need to be defined first.  One is the term plugin host.  A plugin host is responsible for loading plugin modules and calling them.  Photoshop is a plugin host.  The second term is the plugin itself.  A plugin for Photoshop is a code module that extends the features of Photoshop without modifying the program itself.  To support SGI images inside Photoshop,  we need to write what Photoshop calls a Format Plugin.  Such a plugin can be accessed by the user from a pop-up menu in Open ..., Save As ... and Save A Copy... dialogs.

A Format plugin module, defines a function called ENTRYPOINT (in Windows 95/NT).  As the name suggests, ENTRYPOINT is the first function Photoshop calls when the user runs your plugin.  The following are the parameters for this function

Parameter Type Parameter Name
short selector
void * formatParamBlock
long * pluginData
short * result

The selector value indicates the type of operation requested by Photoshop for the plugin to perform.  The following table lists the values selector can take and what the plugin should do in each case.

Selector Value What the Plugin should do
0 Display an About box.
formatSelectorFilterFile Indicate whether file selected by the user can be read.
formatSelectorReadPrepare Allocate memory for reading and processing the file.
formatSelectorReadStart Read any file header information
formatSelectorReadContinue Process and return the next portion of the image data until the file ends or some other error occurs.
formatSelectorReadFinish Clean up the allocated resources
formatSelectorOptionsPrepare Set the memory management strategy
formatSelectorOptionsStart Present dialog to user to get any file specific information needed when file is first created.
formatSelectorOptionsContinue This selector may be skipped
formatSelectorOptionsFinish Clean up, if necessary
formatSelectorEstimatePrepare Set the memory management strategy
formatSelectorEstimateStart Calculate the disk space needed to save the image in memory
formatSelectorEstimateContinue This selector may be skipped
formatSelectorEstimateFinish Clean up, if necessary
formatSelectorWritePrepare Allocate memory for writing and processing the file.
formatSelectorWriteStart Write any file header information
formatSelectorWriteContinue Write into file the next portion of the image data until the the complete image is saved.
formatSelectorWriteFinish Clean up the allocated resources.

Essentially the code in ENTRYPOINT transfers control to the corresponding handler for a given selector value.  If the plugin fails to handle a particular request, it sets an error value in result.   If this error value is positive, the plugin has handled the error situation and Photoshop does not worry about it.  If the error value is negative, Photoshop displays its standard error dialog and terminates the plugin.  The plugin will in other normal cases set the value of result equal to noErr

Through formatParamBlock, the plugin passes information back and forth to Photoshop.  The formatParamBlock is a huge structure containing a lot of parameters of which we will see an important few :

formatParamBlock Parameter

Function

imageSize Represents the size of the image.  imageSize.h specifies the width and imageSize.v specifies the height of the image.
imageMode Represents the color mode of the image.  We support only RGB (with an alpha channel) mode in our plugin.  Therefore in our plugin imageMode will have the value pluginModeRGBColor
depth depth is the number of bits allocated for every color component in an image.  A depth of 8 bits means red, green, blue and alpha components of a pixel have 8 bits allocated for each one of them.   In such a case, a pixel is represented by 32 bits.  In our plugin, we will support images that have a depth of 8 bits. 
planes The number of planes is equal to the number of components making every pixel of the image.  If the image has red, green and blue components, the image has 3 planes.  If it has an alpha component too, it has 4 planes.  Our plugin can read and write images having any number of planes. But Photoshop seems to work unpredictably when the number of planes exceeds 16.   And other programs reading our file may not recognize the image if it has more than 4 planes.    
theRect Represents the rectangular region of the image that is being processed by the read / write routines of the plugin. 
data data is the actual image data.  When the plugin finishes reading the entire image from disk, it must set this field to NULL.  The plugin is also responsible for freeing up any memory data may point to.  
loPlane and hiPlane These parameters represent the number of planes represented by data.  For example if loPlane is 0 and hiPlane is 2, then data has red, green and blue components for every pixel in the rectangular region defined in theRect. 
colBytes colBytes is either 1, 3 or 4.  If colBytes is 1, the image is read one component at a time.  That is the red component of all pixels is read first, followed by green, blue and alpha (if present) components.  If colBytes is 3, the red, green, blue components of every pixel are read simultaneously.  An alpha component is also read if colBytes equals 4.  
rowBytes

 

rowBytes is the number of bytes required to make up a row of the component.  It is equal to colBytes * width of theRect  being returned. 
dataFork Through the dataFork, the plugin reads and writes files and performs other I/O functions. 
minDataBytes and maxDataBytes Through these parameters the plugin specifies minimum and maximum disk space requirements to Photoshop for saving the image in memory.   The plugin does this in the estimate portion of the write sequence.  

pluginData is a pointer to a chunk of global data the plugin maintains.   This usually includes the header information of the image. 

Implementation:

We will develop our plugin by modifying an example format plugin called SimpleFormat supplied with the Photoshop Plugin Software Development Kit.  A SimpleFormat image contains an image header followed by image data.  The image data has red channel of all pixels listed first, followed by green, blue and alpha (if present) channels.  The data is stored verbatim.  Even an SGI image contains a header that is followed by image data if it is stored verbatim.  The SGI header differs from the SimpleFormat header but the structure of the verbatim image data is the same.   Thus we can reuse the code section in the SimpleFormat plugin that reads its image data.  However, we need to replace code that reads the SimpleFormat header with one that reads the SGI header.   If the image data in an SGI image is run length encoded (RLE) (ie) compressed, we need to handle that situation too.  We have decided to write the image data verbatim, so we can reuse the code that writes the image data in the SimpleFormat plugin.  However, we need to make sure that the SGI header gets written and not the SimpleFormat header. 

The meat of the SimpleFormat plugin is in two files SimpleFormat.c and SimpleFormat.h.  In SimpleFormat.h, we find the definition of the SimpleFormat image header.  We modify it to represent the SGI image header instead.    The structure of the SGI image header is displayed in the following table:

Type Size Name Description
short 2 bytes magic Identifies the SGI image.  It must be 474 
char 1 byte storage Identifies whether the image data is stored verbatim or is Run Length Encoded (RLE) ) (ie) compressed.  If storage equals 0, the image data is stored verbatim.  If its 1, the image data is compressed.  
char 1 byte bpc Number of bytes per component per pixel.  The majority of images use 1 byte per component.   We call these images as 8 bit images.  Some images are 16 bit, that is, they use 2 bytes per component.  The only valid values for this field are 1 or 2. 
short 2 bytes dimension Number of dimensions in the image data.  The valid values for this field are 1, 2 or 3.   If its 1, the image data has one component with one scan line.  The width of the scan line is equal to xsize (described below).  If its 2, the image data has one component with a number of scan lines. The width and height of the image is equal to xsize and ysize (described below)  If it is 3, the image data has a number of components with zsize (described below) specifying their exact number.  xsize and ysize specify the width and height of the image. 
short 2 bytes xsize The width of the image.
short 2 bytes ysize The height of the image.
short 2 bytes zsize The number of components in the image.
long 4 bytes PIXMIN minimum component value.
long 4 bytes PIXMAX maximum component value.
char 4 bytes dummy Ignored.
char 80 bytes name The name of the image.
long 4 bytes ColorMap Our plugin ignores this parameter.
char 404 bytes dummy Ignored.

In SimpleFormat.c, the function ENTRYPOINT is defined.  Based on the selector parameter value, it transfers control to an handler.  The following table lists the range of selector values supported and names of their corresponding handlers.

Selector Value Handler Name
formatSelectorFilterFile DoFilterFile
formatSelectorReadPrepare DoReadPrepare
formatSelectorReadStart DoReadStart
formatSelectorReadContinue DoReadContinue
formatSelectorReadFinish DoReadFinish
formatSelectorOptionsPrepare DoOptionsPrepare
formatSelectorOptionsStart DoOptionsStart
formatSelectorOptionsContinue DoOptionsContinue
formatSelectorOptionsFinish DoOptionsFinish
formatSelectorEstimatePrepare DoEstimatePrepare
formatSelectorEstimateStart DoEstimateStart
formatSelectorEstimateContinue DoEstimateContinue
formatSelectorEstimateFinish DoEstimateFinish
formatSelectorWritePrepare DoWritePrepare
formatSelectorWriteStart DoWriteStart
formatSelectorWriteContinue DoWriteContinue
formatSelectorWriteFinish DoWriteFinish

Reading the SGI image:
When the user selects a particular image file for loading,  Photoshop calls DoFilterFile to know if our plugin can handle the file.  In DoFilterFile, we check if the first two bytes of the file evaluate to 474.  If yes,  the selected file is an SGI image and we express our desire to handle it.  Otherwise, we set an error value in the result parameter.

When Photoshop finds that our plugin is willing to handle the file, it calls DoReadPrepare.  We now have the option of specifying the amount of memory we require to do the job.  In our plugin, we will allocate memory as we want it and hence set the field of required memory to zero.

The plugin is now ready to read the image file.  Photoshop calls DoReadStart to begin the process.  We read the image header in DoReadStart and set some important parameters of the formatParamBlock structure.  We set imageMode to pluginModeRGBColor,    height of the image in imageSize.v and the width in imageSize.h and  the number of color components of the image in planes.    SGI images may store only the alpha channel.  But Photoshop cannot accept a value less than 3 in the planes parameter when the imageMode parameter is set to pluginModeRGBColor.  Therefore, we fool Photoshop by setting the planes value to 4, dont load anything into red, green and blue channels (thus leaving them empty) and load the alpha channel in channel #4. 

We read the image data component by component, one scan line at a time in DoReadContinue.  As we know, SGI images can have their data stored verbatim or be compressed using the RLE scheme.  If the data is stored verbatim it is processed in DoVerbatimReadContinue else it goes to DoRLEReadContinue.   The code in DoVerbatimReadContinue is the same as the code that reads the image data in the SimpleFormat plugin.  We will now discuss how the compressed image data is parsed in DoRLEReadContinue. 

When the image data is compressed, two tables follow the image header.  These tables are of type long and their length is equal to the product of the number of components and the number of scan lines in the image.   For example, If the image has red, green and blue components and has 500 scan lines, then these tables will have 1500 entries each.  The first compressed red scan line will begin in the file at value of table entry 0, the first compressed green scan line will begin at the value of table entry 500 and the first compressed blue scan line will begin at the value of table entry 1000.  The corresponding entries in the second table will give the size of the compressed scan lines in bytes.    To parse a given scan line, we position the file pointer to the location suggested by the appropriate first table entry.  We read the compressed scan line into a buffer.  The amount of bytes to read is got from the corresponding second table entry.  We begin by reading a byte from this buffer.  We construct a runCount  from the first 7 bits (bit 0 through bit 6) and a  runCountFlag from bit 7.  If the runCountFlag is 0,  we read an another byte from the buffer and copy it runCount number of times into the data parameter of formatParamBlock.  If the runCountFlag is 1, we copy runCount number of bytes from the buffer to data.   We repeat this procedure until all bytes are consumed from the buffer.  The result is the scan line. 

Writing the SGI image:
When the user decides to save the image in memory in SGI format, Photoshop initiates the write sequence of the plugin.  The write sequence has the following sections:

Options

Estimate

Write

We ignore the Options section.  In DoEstimatePrepare (Estimate section), we inform Photoshop how much disk space we want to save the file successfully.  Photoshop checks whether the requested space exists on the disk and if not terminates the plugin.   In DoWriteStart (Write section), we write the image header and the verbatim image data to disk.

Resources:
The user invokes our plugin by choosing the option "SGI" from the popups that appear in Open..., Save As..., and Save A Copy dialogs.  To get the text "SGI" to show up in those popups  we need to edit the resource identifiers in SimpleFormat.r.  The following table lists the resource identifiers that our plugin sets.

Resource Identifier Our Value
plugInName SGI
plugInCopyrightYear 1998
plugInDescription Author : Jagannathan Sampath, EFX R + D
Reads and Writes SGI image files
Copyright (c) 1998 EFX.  All Rights Reserved.
vendorName EFX
plugInAETEComment SGI Image File Reader / Writer
plugInSuiteID sdk4
pluginClassID sgiI
ReadExtensions { { 'rgb ', 'sgi ' }
WriteExtensions { { 'rgb ' }
FilteredExtensions { { 'rgb ', 'sgi ' }

The plugInName along with the extensions appear in the popups.  We also use the plugInName and pluginDescription in the about box displayed when DoAbout is called.   

Conclusion:
As you know, this plugin reads only 8 bit SGI images at this time.  Though Photoshop supports conversion of 8 bit images into 16 bit images, all its image manipulation tools work only on 8 bit images.  Thus there is no point in reading into Photoshop a 16 bit image.  We will wait for the day when Photoshop can finally give us 16 bit image manipulation tools.  Until then, good bye.

References:

The Adobe Photoshop Plugin API Guide | Release 4.0.1

The SGI Image File Format Specification by Paul Haeberli of SGI.

 

Best viewed in Netscape 4.x or IE 4.x @ 1024 | 768 | 16-bit colors
Please send bouquets or brickbats to sjagan@hotmail.com
Copyright (c) 1998 Jaganathan Sampath
All Rights Reserved


This page was last updated on: December 9, 1998
All content on this web site is dedicated to the luse