First we need some type definitions and constants out of gimp.h
.
What we always have to check is the image type. Therefore exists the
following enum:
typedef enum {
RGB_IMAGE = 0,
RGBA_IMAGE,
GRAY_IMAGE,
GRAYA_IMAGE,
INDEXED_IMAGE,
INDEXEDA_IMAGE,
UNKNOWN_IMAGE,
} ImageType;
Before we can handle an image we need an image typedef:
typedef struct _Image *Image;
struct _Image
is defined in gimp.c
but we don't have to care
about it. All information about an image could be determined by calling
corresponding functions.
Right, let's start with our programming. We have to
#include "gimp.h"
for the communication with The GIMP main program. Our filter function
newfilter ()
has to be prototyped:
static void newfilter (Image, Image);
The GIMP provides a mechanism to handle both source and destination image with different pointers. We just have to define two pointers, one for the input and one for the output image:
static Image input,
output;
That's all for the global declarations. Let's begin with the main()
function:
int
main (argc, argv)
int argc;
char **argv;
{
if (!gimp_init (argc, argv))
return 0;
/* do something */
gimp_quit ();
return 0;
}
Of course, this function does nothing but initializing the connection to
The GIMP and exiting from it. The image handles input
and output
have to be defined (right after gimp_init
):
if (!(input = gimp_get_input_image (0)))
return 0;
if (!(output = gimp_get_input_image (0)))
{
gimp_free_image (input);
return 0;
}
As there are three types of images (RGB, gray and indexed) we have to determine whether our filter calculation may start or not. It depends on what the filter function is doing. In most cases we only operate on RGB and grayscaled images, so that we could write:
select (gimp_image_type (input))
{
case RGB_IMAGE :
case GRAY_IMAGE :
newfilter (input, output);
gimp_update_image (output);
break;
case INDEXED_IMAGE:
gimp_message ("NewFilter: Can't operate on indexed image.");
break;
case UNKNOWN_IMAGE:
gimp_message ("NewFilter: Can't operate on unknown image.");
break;
}
See table tab.imagetypes and section sec.datatypes for further explanation about image types.
Before we call gimp_quit ()
(see above) we should free some memory:
gimp_free_image (input);
gimp_free_image (output);
You could use this code piece as a template for your own plug-ins.
We expand that main ()
-code later when we add dialogs to it.
But the code structure is almost the same.
We have defined a prototype of the newfilter ()
function above
(see Section
sec.init). The function starts with some variable
declarations:
static void
newfilter (linput, loutput)
Image linput, loutput;
{
long width, height;
long channels, rowstride;
unsigned char *src_row, *dest_row;
unsigned char *src, *dest;
short row, col;
int x1, y1, x2, y2;
See table tab.identifiers for the meaning of an identifier.
Table tab.identifiers: Identifiers of the filter function.
width width of image
height height of image
channels color depth (bytes)
rowstride length of a row
src_row current row of source image
dest_row current row of destination image
src current column of source image
dest current column of destination image
row row counter
col column counter
x1 most left ordinate of selection
y1 most upper ordinate of selection
x2 most right ordinate of selection
y2 most lower ordinate of selection
To fill the identifiers with correct values we can use some ``standard'' code:
gimp_image_area (linput, &x1, &y1, &x2, &y2);
width = gimp_image_width (linput);
height = gimp_image_height (linput);
channels = gimp_image_channels (linput);
rowstride = width * channels;
src_row = gimp_image_data (linput);
dest_row = gimp_image_data (loutput);
x1 *= channels;
x2 *= channels;
src_row += rowstride * y1 + x1;
dest_row += rowstride * y1 + x1;
I think most of it is self-explaining. The reason we multiply x1
and
x2
with channels
is because the value of width
gives the
image width in pixel, but each pixel needs channels
bytes of memory.
Both src_row
and dest_row
point at the first row of the
selection. Now we can start some loops to increment the positions:
for (row = y1; row < y2; row++)
{
src = src_row;
dest = dest_row;
for (col = x1; col < x2; )
{
int ch;
for (ch = 0; ch < channels; ch++)
{
col++;
*dest++ = (unsigned char)*src++;
}
}
src_row += rowstride;
dest_row += rowstride;
}
}
The first started loop counts from the most upper row to the most lower
row. The current column pointers src
and dest
point at the
first column of the current row. The second loop counts from the most left to
the most right column. Now we have to go through all channels in the next loop
and just have to set and increment both dest
and src
.
At this point-when you are setting dest
-begins the actual filter
calculation. This is just an example that does nothing but copying the
input to output. Remember to increment both dest
and src
in the
inner loop!
After the second loop both src_row
and dest_row
are incremented by rowstride
.
That's it.
It is very easy to create a new image out of a plug-in. The function
Image gimp_new_image (char *TITLE,
long WIDTH,
long HEIGHT,
ImageType TYPE)
produces a new image with the name TITLE
. Its geometry is
WIDTH
* HEIGHT
pixels and its type is TYPE
. It
returns a image handle of type Image
.
Example: If you want to create a 512 * 256 pixel true color image, you have to write:
...
Image newimage;
...
newimage = gimp_new_image ("New Image", 512, 256, RGB_IMAGE);
...
Ok, now there is a new image, but it is still hidden. To make an image visible just call:
gimp_display_image (newimage);
To get more information about an image use the gimp_image_*
functions.
Needs work...
The GIMP provides a large library of dialog functions and widgets. Every
widget in a dialog needs a widget ID of type int
. We have to declare
an identifier for each ID of the dialog. First the dialog itself needs
an ID:
int dlg_ID;
To create a new dialog we use the function gimp_new_dialog ()
:
dlg_ID = gimp_new_dialog ("Another Plug-In");
There exist several function for creating new widgets, for separating and grouping widgets and for other needs (see tables tab.creating, tab.grouping and tab.other).
Table tab.creating: Functions to create dialog widgets.
Function Description
------------------------ -------------------------
gimp_new_check_button () Create check button.
gimp_new_dialog () Create dialog.
gimp_new_image_menu () Create image choose box.
gimp_new_label () Create label of a widget.
gimp_new_push_button () Create push button.
gimp_new_radio_button () Create radio button.
gimp_new_scale () Create scale.
gimp_new_text () Create text widget.
Table tab.grouping: Functions to group dialog widgets.
Function Description
------------------------ ------------------------------------------------
gimp_new_column_group () Create group box, members are shown in a column.
gimp_new_frame () Create frame.
gimp_new_row_group () Create group box, members are shown in a row.
Table tab.other: Functions for additional dialog handling.
Function Description
---------------------- -----------
gimp_add_callback () Add a callback function to a widget.
gimp_cancel_item_id () Returns handle of cancel button.
gimp_change_item () Change a widgets value.
gimp_close_dialog () Exit the dialog.
gimp_delete_item () Delete an item of a dialog.
gimp_hide_item () Make a widget invisible.
gimp_ok_item_id () Returns handle of ok button.
gimp_show_dialog () Runs dialog and waits on exit.
gimp_show_item () Make a widget visible.
First some identifiers for that example:
int DialogID, OtherGroupID;
int RadioGroupID, FirstRadioID, SecndRadioID;
int first, second;
You can add a group box containing radio buttons to a dialog by simply
switching the type
parameter of either the gimp_new_row_group ()
or the gimp_new_column_group ()
function to RADIO
:
RadioGroupID = gimp_new_row_group (DialogID, OtherGroupID, RADIO, "");
FirstRadioID = gimp_new_radio_button (DialogID, RadioGroupID, "First");
SecndRadioID = gimp_new_radio_button (DialogID, RadioGroupID, "Second");
...
It is recommended to initialize the radio widgets, so that the dialog represents the internal values correctly to the user. Note that only one member of a radio button group has to have a true value (1), all other members have to be zero:
first = 1;
second = 0;
gimp_change_item (DialogID, FirstRadioID, sizeof (first), &first);
gimp_change_item (DialogID, SecndRadioID, sizeof (second), &second);
...
Add a callback function for every radio button:
gimp_add_callback (DialogID, FirstRadioID, radio_callback, &first);
gimp_add_callback (DialogID, SecndRadioID, radio_callback, &second);
...
Last you have to define the callback function (don't forget prototyping):
static void
radio_callback (int item_ID, void *client_data, void *call_data)
{
*((long*) client_data) = *((long*) call_data);
}
As it would be nice to turn on and off the preview we insert a check-box
into the dialog. For this we need an int
as handle and a long
as value:
static long aapply;
static int aapply_ID;
To restore the original picture, we need some memory and a pointer for it:
static unsigned char *saved;
Of course there are some lines of prototypes:
static void toggle_callback (int, void *, void *);
static void saveimage(void);
static void freshen(void);
The saveimage ()
function should be called after we checked that
we are able to calculate anything (i.e. after gimp-init and image-checks):
saveimage ();
To add the check-box to the dialog, the following lines have to be at a good place between dialog-init and -exit:
aapply_ID = gimp_new_check_button (dialog_ID, group_ID,
"Auto Apply");
aapply = 1;
gimp_change_item (dialog_ID, aapply_ID,
sizeof (aapply), &aapply);
gimp_add_callback (dialog_ID, aapply_ID,
toggle_callback, &aapply);
So we need the callback function. If we are in preview-mode the filter will be calculated with the current values. If we are not in preview-mode the original image will be restored.
static void
toggle_callback (item_ID, client_data, call_data)
int item_ID;
void *client_data;
void *call_data;
{
*((long*) client_data) = *((long*) call_data);
if (aapply)
{
filter (input, output); /* our filter function */
gimp_update_image (output);
}
else
{
freshen();
gimp_update_image (output);
}
}
Finally, we need the saveimage ()
and freshen ()
functions:
static void saveimage(void)
{
saved = (unsigned char *) malloc(gimp_image_width(input) *
gimp_image_height(input) *
gimp_image_channels(input));
memcpy(saved, gimp_image_data(input),
gimp_image_width(input)*
gimp_image_height(input)*
gimp_image_channels(input));
}
static void freshen(void)
{
memcpy(gimp_image_data(output), saved,
gimp_image_width(input)*
gimp_image_height(input)*
gimp_image_channels(input));
}
To apply the preview ``on demand'' for each change of values (e.g. slider-values, radio- or check-buttons) we have to extend the callbacks in the following way. If preview-mode is turned on and values had changed we have to update the preview, else we just set the new values.
static void
scale_callback (item_ID, client_data, call_data)
int item_ID;
void *client_data;
void *call_data;
{
if (aapply &&
(*((long*) client_data) != *((long*) call_data)))
{
*((long*) client_data) = *((long*) call_data);
filter (input, output);
gimp_update_image (output);
}
*((long*) client_data) = *((long*) call_data);
}
When calculating your filter function you have to set a pointer
to the source data. I have made the experience that if you use
gimp_get_input_image (0)
for that pointer you will get
``ugly'' results, because you get the data from the last calculated
destination image.
Imagine the source pointer identifier is called src_row
.
Then you won't use
src_row = gimp_get_input_image (0);
but
src_row = saved;
so that you get the correct data for the next preview.
To add a progress bar to your plug-in use the functions
gimp_init_progress ()
and gimp_do_progress ()
.
Initialize it right after gimp_show_dialog ()
:
if (gimp_show_dialog (dialog_ID)
{
gimp_init_progress ("New Filter");
newfilter (input, output);
}
Define two identifiers for the current progress and the maximum progress value which is the height of the selected area almost:
int progress;
int max_progress;
...
progress = 0;
max_progress = y2 - y1;
Increase progress
after each calculated row and do an update of the
progress bar after some rows:
progress++
if (progress % 5 == 0)
gimp_do_progress (progress, max_progress);
The value of max_progess
depends on what your plug-in is doing.
I think the example shown here will fit for most filters that walk straight
through the image.
To close the current progress dialog you have to do
gimp_do_progress (1, 1);
See the Lighting plug-in (Tom Bech and Federico Mena Quintero) and the Lensflare plug-in (Marc Bless) for examples.
Needs work...
Needs work...
You can let the gimp executable store and recover your data of any size.
The usage of the functions gimp_get_params ()
and gimp_set_params ()
is very easy and will be explained with the source code of the gauss_recurse.c
plug-in (Spencer Kimbal and Peter Mattis).
This plug-in creates a dialog with an entry field for a real value (double) and two check buttons (long), so this structure is used:
typedef struct
{
double radius;
long horizontal;
long vertical;
} BlurValues;
BlurValues vals;
As soon as all image consistency at startup is done, vals
should be filled
with either default or previously saved values:
void *data;
data = gimp_get_params ();
if (data)
vals = *((BlurValues *) data);
else
{
vals.radius = 5.0;
vals.horizontal = 1;
vals.vertical = 1;
}
Now the dialog widgets will be defined:
char buf[16];
sprintf (buf, "%0.2f", vals.radius");
text_ID = gimp_new_text (dialog_ID, temp_ID, buf);
horz_ID = gimp_new_check_button (dialog_ID, temp_ID, "Horizontal");
vert_ID = gimp_new_check_button (dialog_ID, temp_ID, "Vertical");
gimp_change_item (dialog_ID, horz_ID, sizeof (vals.horizontal), &vals.horizontal);
gimp_change_item (dialog_ID, vert_ID, sizeof (vals.vertical), &vals.vertical);
gimp_add_callback (dialog_ID, text_ID, text_callback, &vals.radius);
gimp_add_callback (dialog_ID, horz_ID, toggle_callback, &vals.horizontal);
gimp_add_callback (dialog_ID, vert_ID, toggle_callback, &vals.vertical);
If the dialog ends with "Ok" the structure is given to the Gimp:
if (gimp_show_dialog (dialog_ID))
{
gimp_set_params (sizeof (BlurValues), &vals);
/* do all stuff ... */
}
To be a little complete, here are the callbacks:
static void *text_callback (int item_ID, void *client_data, void *call_data)
{
*((double *) client_data) = atof (call_data);
}
static void *toggle_callback (int item_ID, void *client_data, void *call_data)
{
*((long *) client_data) = *((long *) call_data);
}