Previous Next Table of Contents

4. Programming Plug-Ins

4.1 Datatypes and Constants

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.

4.2 Initializing All Stuff

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.

4.3 The Filter Function

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.

4.4 Creating A New Image

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.

4.5 Color Functions

Needs work...

4.6 Adding Dialogs

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.

4.7 Groups of Radio Buttons

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);
  }

4.8 Showing Previews

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.

4.9 Progress Bars

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);

4.10 Creating Additional Dialogs

See the Lighting plug-in (Tom Bech and Federico Mena Quintero) and the Lensflare plug-in (Marc Bless) for examples.

Needs work...

4.11 File-Plug-Ins

Needs work...

4.12 Storing And Recovering Dialog Values

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);
}


Previous Next Table of Contents