A plug-in is of limited use if it has nothing to do with the image. In this section, we explore the exciting world of the GIMP image hierarchy, and learn how to manipulate it.
In the beginning, there was Wilber, Wilber the gimp. The graphic was without form and void, and darkness was upon the face of the desktop, and the Spirit of Wilber was moving over the face of the bitstream.
And Wilber said, "<Toolbox>/File/New," and there was an image. And Wilber saw that the image was good, and Wilber separated the image into drawables. And Wilber looked down at what he had wrought, and Wilber said, "Oh golly." For Wilber had made the drawables of the layer according to their kinds, and the drawables of the channel according to their kinds, and the drawables of the mask according to their kinds …
… or you could look at it the other way around. We have all sorts of silly things like masks, channels, and layers, but they're all just a bunch of pixels that can be drawn on, so we treat them much the same and lump them all in to the category of "drawables". And an image, then, is just what you get when you put some drawables together.
Most plug-ins care suprisingly little about these images.
After all, it's the bunch of pixels on the drawable most of them
are playing with. In any case, the most complex data structure
a plug-in uses for an image is an integer (gint32
),
a simple ID by which the GIMP knows that image.
Drawables make life much more exciting. You have to stay
on your toes about which data type a function uses, a
gint32
for the drawable's ID, or a
GimpDrawable
, or a pointer to one. Here's the
GimpDrawable
type (from gimp.h):
Figure 3-1. GimpDrawable Definition
struct _GimpDrawable { gint32 id; /* drawable ID */ guint width; /* width of drawble */ guint height; /* height of drawble */ guint bpp; /* bytes per pixel of drawable */ guint ntile_rows; /* # of tile rows */ guint ntile_cols; /* # of tile columns */ GimpTile *tiles; /* the normal tiles */ GimpTile *shadow_tiles; /* the shadow tiles */ };
(Don't worry about the tile fields, you shouldn't have to deal with those directly.)
One may obtain a GDrawable
from a drawable's
ID by the call
GimpDrawable* gimp_drawable_get
(gint32
drawable_ID);Note: When you see a "layer ID" or somesuch, this is really a drawable ID which happens to belong to a layer. You may use it anywhere a drawable ID is called for.
If you were looking at a spot on the GIMP image, you might choose to describe its location as the distance from the origin of the image, from the origin of the layer, or from the origin of the current selection.
GIMP chooses to measure coordinates from the upper-left corner of the drawable (the drawable is usually a layer). If you want to know about the location of the selection or the layer offset, use the following calls (gimp.h):
Figure 3-2. Functions to obtain coordinates.
/* Find the bounding box of the current selection in relation to the * specified drawable. Returns TRUE if there is a selection. */ gint gimp_drawable_mask_bounds (gint32 drawable_ID, gint *x1, gint *y1, gint *x2, gint *y2); /* Returns the offsets of the drawable. */ void gimp_drawable_offsets (gint32 drawable_ID, gint *offset_x, gint *offset_y);
The method through which a plug-in accesses a drawable is by way of what's known as a pixel region.
Before working with a pixel region, initialize it with the following call:
void
(GimpPixelRgn* pr, GimpDrawable* drawable, int x, int y, int width, int height, int dirty, int shadow);gimp_pixel_rgn_init
A "dirty" tile is one that
has been changed. Tiles that are not dirty won't be written
back to GIMP, whereas dirty ones will be. Initializing a
pixel region as "dirty" indicates to
gimp_pixel_rgns_process
that it should
treat tiles in that region as if you've dirtied them.
"Shadow tiles are merely an indication of the desire to use a temporary buffer for writing in to," says Peter. "The advantage of using shadow tiles are that A) you won't muck up the original image, B) undo is handled properly, and C) the modifications to the image are correctly masked by the selection."
In short, if you're writing to a pixel region, the dirty and shadow flags should be TRUE, TRUE. If you need a clean copy to read from, use FALSE, FALSE. TRUE, FALSE can be used for writing directly to the image (not recommended), and FALSE, TRUE could be used to read from shadow tiles that you've just written to (possibly when using a multi-pass algorithm of some sort, as in gauss_iir.c).
There are calls for pixel_rgn_get_
pixel
, row
,
col
, and rect
, which
grab data from the image and dump it into a buffer that you've
pre-allocated. And there are set calls to match. Look for "Pixel
Regions" in gimp.h.
Note that these calls are relatively slow, they can easily
be the slowest thing in your plug-in. Do not get (or set) pixels
one at a time using
pixel_rgn_[get|set]_pixel
if there is any
other way. Tips for improving efficiency are in the next chapter.
The data in your buffer depends on the image type. Grayscale is an array of values. Grayscale-alpha has gray value, alpha value, gray, alpha, etc. RGB and RGBA are just that.
Once your plug-in is through munging the drawable, it has to go through the process of making sure GIMP has been brought up to date. That sequence typically goes something like this:
Example 3-1. Tidying up dirty drawables.
GimpDrawable *drawable; /* . . . */ /* Ensure any dirty tiles are flushed to GIMP. */ gimp_drawable_flush (drawable); /* Merge in what you've written to the shadow tiles. If the second arguement is TRUE, the action will be undoable. */ /* Without this, anything you've written on the shadow tiles may be lost, and drawable will be filled with uninitialized memory instead. */ gimp_drawable_merge_shadow (drawable->id, TRUE); /* Update a portion of a drawable you've modified. Updates the displays and drawable previews. */ gimp_drawable_update (drawable->id, x1, y1, width, height); /* Flush the updates to the on-screen displays. */ /* (Not always desirable when called non-interactively.) */ gimp_displays_flush(); /* And if you're all done with the drawable, free the memory allocated by gimp_drawable_get, and any tiles the drawable was using. */ gimp_drawable_detach(drawable);
Selections are drawables too.
gimp_image_get_selection(image_ID)
; returns
the drawable ID of the selection mask.
FIXME: Hmm, selection mask's 0,0 is drawable_mask_bounds' x1,y1, izit? Also find out what feathered/anti-aliased selections mean to us.
See also: Appendix A: Premultiplied Alpha
There are many joyous functions for the handling of
selections and layers and whatnot, which you may find by reading
gimp.h or using the db_browser. One that I should warn you about
is adding new layers to images: A layer created with
gimp_layer_new
must still be added to the
image with gimp_image_add_layer
before it
will do any good. Be sure you pass the same image ID to both
functions. And don't create layers without an alpha channel that
aren't background layers if you're not looking for
trouble.