GimpPreview Reference Manual | |||
---|---|---|---|
<<< Previous Page | Home | Up | Next Page >>> |
This is a short tutorial on how the GimpPreview widget could be integrated in your plug-in. In the description it is assumed that you already have an existing plug-in that you would like to convert. Most of the comments should be applicable to the case where you are developing a new plug-in from scratch.
In most of this tutorial it is assumed that your plug-in cannot easily scale its outputs, and that the plug-in will leave the scaling to the preview. This is probably the case with most plug-ins and is also easier for lazy plug-in authors. This tutorial also contains advice about using the preview with scale independent plug-ins.
Somewhere in the plug-in you will have a function that renders the effects of the plug-in to a GimpDrawable. One of the design goals for this preview was that it should be possible to implement a single function that could render both to the preview and to the final drawable.
The rendering function should have an interface that is similar to the following:
static void render (GimpDrawable *src_drawable, GimpDrawable *dest_drawable, gint x, gint y, gint width, gint height, RenderParams *params, GimpPreviewProgressUpdater * progress_updater) |
This is probably more complicated than your current interface. What is the meaning of all these arguments:
This is always the original drawable on which the plug-in was invoked.
This is the drawable that the render function should modify. When the render function is rendering only to the preview this should be a scratch drawable, when the render function is called to do the real job this should be the same as src_drawable.
These arguments describe the area within the original drawable for which the render function should render a new image.
These are the other parameters that the render function needs to do its job. It has been declared here as a single pointer but you can replace it with any set of arguments that you want.
This is an object that the render function should use to inform the rest of the world about its progress. Don't call gimp_progress_update in your render function, use gimp_preview_progress_updater_set_fraction() with this object instead (and don't forget to check its return value).
Most of the conversions to an existing render function should be straight-forward. Actually, with a decent render function there should be only one difficult point, but let's start with the easy ones:
You probably already use two distinct GimpPixelRgn's: one source pixel region and a destination pixel region. The source pixel region should be changed to use the src_drawable and the destination pixel region should be changed to use the dest_drawable.
Convert the calls to gimp_progress_update(fraction) into gimp_preview_progress_updater_set_fraction(progress_updater, fraction). You should call this function at regular intervals. Always check its return value. When it returns FALSE the render function should terminate immediately, because its results won't be used.
When you are using gimp_drawable_mask_bounds it should be used on the dest_drawable.
Never use the current values of the preview size and location, but always use the supplied arguments. The reason is that the current values of the preview may have been changed in the meantime.
The only thing that might cause some real head aches with the conversion are caused by the possible difference between the coordinates in the src_drawable and dest_drawable. The root of this problem is the fact that in the GIMP all pixel coordinates in a drawable are always relative to the origin of the drawable and not relative to the origin of the image to which they belong. In other word the coordinates of the top leftmost pixel are always (0,0) and they are completely independent from the position of the drawable within the image. For most purposes this is a reasonable choice, but in our case it has some nasty consequences.
One of the design decisions that was made during the development of this preview was that the render function should not modify the original drawable while it was rendering only for the preview. In theory it would have been possible to let the render function always modify the original drawable and then to undo the modifications to the drawable after they were shown in the preview, but this appeared to be a complicated and error-prone solution. So it was decided that the render function should render to a scratch copy of the original drawable.
In most cases the preview will only show a small part of the total image, so it would have been wasteful when the scratch copy was a full copy of the entire original drawable. Therefore, the scratch copy only consists of the area that is needed for the preview. So when the render function is rendering for the preview, the dest_drawable will be a scratch drawable with dimensions width x height.
But now there is a problem with respect to the coordinates in the dest_drawable. When the render function is rendering to the final drawable, the dest_drawable and the src_drawable obvious have identical coordinate systems. But when the render function is rendering to the scratch drawable, these coordinate systems are in general different. To be more precise, the pixel with coordinates (0,0) in the dest_drawable corresponds to the pixel with coordinates (x, y) in the src_drawable. This is one of the reasons that x and y must be passed as arguments to the render function.
This difference in the coordinate systems is nasty, but with the current version of the GIMP, there appears to be no more elegant solution.
But even though this is not very pleasant, it need not be a big problem. The main point that you should keep in mind is that for all coordinates in your function it must be very clear if they are relative to the src_drawable or to the dest_drawable. It is easy to convert between the two coordinate systems by adding or subtracting a standard offset. I suggest that you add code to the start of your render function that is similar to the following:
const gint offset_x = (src_drawable == dest_drawable) ? 0 : x; const gint offset_y = (src_drawable == dest_drawable) ? 0 : y; |
Now when you want to convert a coordinate in the source drawable to a coordinate in the dest_drawable you should add (offset_x, offset_y). To do the conversion the other way, you should subtract the offsets.
When the preview want the plug-in to render a new image it will call a callback function in your plug-in. In most cases you should be able to use the following:
static void render_callback (GtkWidget * widget, GimpPreviewUpdateEvent * event, gpointer data) { GimpPreview *preview = GIMP_PREVIEW (widget); GimpDrawable *drawable = (GimpDrawable *) data; GimpDrawable *dest_drawable; GimpPreviewProgressUpdater preview_progress_updater; /* Get a scratch drawable */ dest_drawable = gimp_preview_get_temp_drawable (drawable, event->image_x, event->image_y, event->image_width, event->image_height); /* Initialize the GimpPreviewProgressUpdater */ gimp_preview_progress_updater_init_for_preview (&preview_progress_updater, preview); /* Render to the scratch drawable */ render (drawable, dest_drawable, event->image_x, event->image_y, event->image_width, event->image_height, &render_params, &preview_progress_updater); /* Draw the scratch drawable on the preview */ gimp_preview_draw_unscaled_drawable (preview, dest_drawable); /* Delete the scratch drawable */ gimp_preview_free_temp_drawable (dest_drawable); } |
It is assumed in this code that there exists a global struct render_params that contains the other parameters for your render function. Using global variables is not very elegant, it might be better to let data point to a struct that contains both a reference to the drawable and to the other params, but that would somewhat spoil the educational simplicity of this example.
Somewhere in your plug-in there is a call to the render function to do the real work. Modifying it should be trivial. You could use something similar to the following:
gint x, x2, y, y2, width, height; GimpPreviewProgressUpdater gimp_progress_updater; /* Get the area that must be updated */ gimp_drawable_mask_bounds (drawable->drawable_id, &x, &y, &x2, &y2); /* Compute its dimensions */ width = x2 - x; height = y2 - y; /* Initialize the GimpPreviewProgressUpdater */ gimp_progress_init (_("Rendering...")); gimp_preview_progress_updater_init_for_gimp (&gimp_progress_updater); /* Call the render function */ render (drawable, drawable, x, y, width, height, &render_params, &gimp_progress_updater); |
The final step in this tutorial is creating and connecting the preview. Like most of the other steps this is not difficult. The preview widget is a traditional GTK widget and you should handle it similar to other widgets in your dialog. The preview must be connected with your callback function. A simple example:
GtkWidget *preview; /* Create a new preview with default parameters */ preview = gimp_preview_new (drawable); /* Connect the "update-preview" signal handler. * You might want to change the fourth argument to other * data that should be passed when the handler is invoked. */ g_signal_connect (G_OBJECT (preview), "update-preview", G_CALLBACK (render_callback), drawable); |
Certain plug-ins are scale-independent, in the sense that their effect on a scaled image gives the same result as scaling the plug-in's output from an unscaled image. In general most plug-ins for which the output pixel is determined only by the corresponding source pixel (but not by its neighbors) are scale independent.
Adding a preview to scale-independent plug-ins is easy. The render function in this case should look similar to:
static void render (GimpDrawable *drawable, RenderParams *params, GimpPreviewProgressUpdater * progress_updater) |
The only change that you will probably need to make is to include a GimpPreviewProgressUpdater as extra argument.
In most cases you should be able to use the following as the callback function:
static void render_callback (GtkWidget * widget, GimpPreviewUpdateEvent * event, gpointer data) { GimpPreview *preview = GIMP_PREVIEW (widget); GimpDrawable *drawable = (GimpDrawable *) data; GimpDrawable *dest_drawable; GimpPreviewProgressUpdater preview_progress_updater; /* Get a scaled scratch drawable */ dest_drawable = gimp_preview_get_scaled_temp_drawable (drawable, event->scale, event->preview_x, event->preview_y, event->preview_width, event->preview_height); /* Initialize the GimpPreviewProgressUpdater */ gimp_preview_progress_updater_init_for_preview (&preview_progress_updater, preview); /* Render to the scratch drawable */ render (dest_drawable, &render_params, &preview_progress_updater); /* Draw the scratch drawable on the preview */ gimp_preview_draw_scaled_drawable (preview, dest_drawable); /* Delete the scratch drawable */ gimp_preview_free_temp_drawable (dest_drawable); } |
Because this is so easy, some developers might be tempted to treat their algorithms as though they are scale-independent, even when this is not actually true. In these cases you should be strong and resist this temptation. Treating non scale-independent algorithms in this way will create funny artifacts while the user is zooming, and the preview will not accurately reflect the effects of the plug-in.