Running plug-ins
plugin
/usr/local/bin/myplugin
./joes_plugin [argument1 [argument2]...]
Plugin execution dialog
Creating plug-ins
The plug-in interface is still experimental and may change slightly in future versions.
Plugin programming considerations
newimage(g.image_count,x,y,bitsperpixel,colortype,frames);
g.image_count++;
where x The image bytes can then be addressed as
z[image_no].image[frame][y][x]
Pixels are packed in standard format according to image depth:
Indexed color:
8 bits/pixel: One byte = 1 pixel
Grayscale: Rounded up to next highest multiple of 8 bits/pixel,
maximum of 32 bits/pixel.
Color:
15 bits/pixel: 0rrrrrgg gggbbbbb
16 bits/pixel: rrrrrggg gggbbbbb
24 bits/pixel: rrrrrrrr gggggggg bbbbbbbb
32 bits/pixel: 00000000 rrrrrrrr gggggggg bbbbbbbb
48 bits/pixel: 00000000 rrrrrrrr 00000000 gggggggg 00000000 bbbbbbbb
This means that each horizontal line of n pixels in the image can
be n, 2
n, 3
n, 4
n, or 6
n bytes long, depending
on the image depth. When creating a loop over the pixels, it is necessary
to increment by the correct number of bytes in the x direction. For example,
the following loop increases the brightness of frame 0 of a 16-bit grayscale
image by 100:
uchar *address;
int x, y, value;
int bpp = z[ci].bpp; // bpp is bits per pixel
int step = g.off[z[ci].bpp]; // step is bytes per pixel
for(y=0; y < z[ci].ysize; y++)
for(x=0; x < step*z[ci].xsize; x+=step)
{ address = z[ci].image[0][y][x];
// Read pixel value in depth-independent manner
value = 100 + pixelat(address, bpp);
// Make sure value doesn't exceed maximum permitted value
// for the given image depth.
value = min(value, (int)g.maxvalue[bpp]);
// Put pixel back into image
putpixelbytes(address, value, 1, bpp, 1);
}
For color images, the red, green, and blue components must be extracted from the pixel and processed separately. To determine whether the image is color, check z[ci].colortype which can be one of GRAY, INDEXED, COLOR, or GRAPH.
The function uint RGBvalue(int red, int green, int blue, int bpp) takes the red, green, and blue components and converts them into a composite value that can be stored into the array.
The function void valuetoRGB(uint pix, int &rr, int &gg, int &bb, int bpp ) does the reverse, putting values for red, green, and blue in the parameters rr, gg, and bb that are appropriate for the given bits/pixel. The parameters rr, gg, and bb are passed by reference. If you don't like this, you can easily change it to pointers in your plugin.
The functions pixelat(), RGBvalue(), valuetoRGB, and putpixelbytes() can be copied to your plugin from xmtnimage42.cc .
There are a number of other parameters in the Image struct that may prove useful in plugins - see xmtnimage.h for a description. Some of these, such as xsize and ysize, must correspond to the dimensions of the image array and should not be changed arbitrarily.
The parent program will take care of scaling and redrawing the image when the plugin finishes, provided that g.changed[ci] is set to 1.
All plugins must be recompiled if you change to a new version of imal.
for(k=0;k<256;k++)
{ z[ino].palette[k].red = k/4;
z[ino].palette[k].green = k/4;
z[ino].palette[k].blue = k/4;
}
memcpy(z[ino].opalette, z[ino].palette,768);
memcpy(z[ino].spalette, z[ino].palette,768);
(where ino is the image number).
Each image has 3 copies of the colormap, to facilitate undo operations.
g.changed[ino] = 1;
z[ino].touched = 1;
and the image should be given a default title:
strcpy(z[ino].name, "New_title"); (or other title).
This is followed by a null character and the remaining configuration information, followed by the image data.
This arrangement allows any program that takes command-line arguments and writes to stdout to be easily repackaged as a plugin, by modifying the skeleton program plugin.cc. This could be used, for example, in plugins that can read new file formats or for controlling a scanner. The stream pipe mechanism used in imal is very fast, and does not require an intricate interface with shared memory or error-prone function calls to internal routines in imal.
Another advantage is that an unlimited amount of data can be sent to the plugin or returned to imal. If additional parameters, such as user-selected filenames using the graphical file selector, are desired, the plugin can be incorporated into a imal macro.
strcpy(display_message, "Your error message\n\
up to 1024 characters here");
Warning: Incorrectly setting parameters in the Image struct can cause your plugin to crash, or crash imal.
Notes on plugins:
Example - scanner plugin
A typical use for a plugin would be to control a new scanner. This example demonstrates the preferred method for setting up a scanner plugin for interactive scanning in preview and image scan mode.
Assume the plugin is named ``myscan'' and that the plugin was written to take command-line arguments as follows:
The scanner plugin could be incorporated into imal by the following procedure:
It is also possible to instead use the line button Preview macro pluginmacro. This macro could contain the lines
windows(1); ( = Sets separate-window mode on)
executeplugin(plugin,0,75,0,0,640,825); ( = executes plugin)
windows(0);(= Sets separate-window mode off)
myplugin,1,300,100,100,200,200;
The functionality of the scanner plugin will now be transparent to the user. If the user clicks the Preview button, the plugin is automatically executed, scans an image at 75 dpi, and the preview image appears in the main imal window. The user then uses the mouse to select the desired scan region and clicks the Myscan button. This executes the plugin again, which scans the desired area at 300 dpi.
Here is a concrete example of a minimal plugin that takes the current image (ci), and subtracts its pixel values from 255. If the image was an 8 bit grayscale image, this would create a photographic negative of the image.
int f,i,j, bytesperline;
//// Calculate number of bytes in a scan line based on bits/pixel
bytesperline = z[ci].xsize * g->off[z[ci].bpp];
for(f=0; f<z[ci].frames; f++)
for(j=0; j<z[ci].ysize; j++)
for(i=0; i<bytesperline; i++)
z[ci].image[f][j][i] = 255 - z[ci].image[f][j][i];
//// Set g->changed so parent redraws the image
g->changed[ci] = 1;
//// Set `touched' so user is prompted to save image before quitting
z[ci].touched = 1;
//// Change the title of the image
strcpy(z[ci].name, "Modified");
The actual mechanics of transferring the data to and from the parent are handled by the skeleton program, plugin.cc, in which this code would be placed.
Testing plug-ins
To debug a plug-in, create a macro containing the following line:
executeplugin( plugin_name , 0)
or equivalently,
testplugin( plugin_name )
The `0' causes the plugin to be executed in `debug mode', i.e., its stdout is not redirected, whereas `1' causes it to be executed normally.
Note: It is advisable not to move the mouse, open other menus, or execute other commands while the plugin is executing.
Supplied plugins
`Plugin'
This is an example plugin that simply creates a small test image.
`Readtif'
This plugin uses the Leffler libtiff library from ftp.sgi.com . It was not feasible to use the libtiff routines in imal, because the libtiff library does not support many of the TIFF subtypes commonly found in the laboratory, including 16 bit grayscale, 15 and 16 bit color, and cannot read Image Quant files. Some well-known image viewers incorporating libtiff will even crash when trying to read such files. On the other hand, some TIFF subtypes not supported by imal (such as Fax images) may be readable by this plugin. Also note that readtif expands all images to 32 bits/pixel. The user should down-convert the image to the desired pixel depth before saving it. Enterprising users who strongly prefer libtiff (you know who you are) can even uncomment the libtiff code in xmtnimage11.cc. The include file (tiffio.h) must be present on your system.
`Readhdf'
Readhdf reads images in NCSA's HDF format. The HDF format is extremely complex and can contain a variety of data types along with or instead of images. HDF files not containing images are not readable by readhdf. Users are encouraged to adapt the readhdf code to read other data types. Feel free to send any working plugins to the imal site (by email only - the incoming directory has been closed). If your plugin is useful, it may be of great benefit to some other user.
Compiling plugins
gcc -O2 -o plugin plugin.cc
Compiling and installing the HDF image format plugin
ftp://ftp.ncsa.uiuc.edu/HDF/HDF_Current
A Fortran compiler is not necessary to compile this library provided that the defaults in the Makefile.in and Makefile files in the distribution are changed to FC = NONE.
gcc -o readhdf readhdf.cc libdf.a libmfhdf.a libjpeg.a libz.a -I../src
changing the last parameter if necessary to reflect the location of the HDF include files (hdf.h et al.).