diff options
Diffstat (limited to '')
-rw-r--r-- | plug-ins/common/van-gogh-lic.c | 904 |
1 files changed, 904 insertions, 0 deletions
diff --git a/plug-ins/common/van-gogh-lic.c b/plug-ins/common/van-gogh-lic.c new file mode 100644 index 0000000..9bb5dc8 --- /dev/null +++ b/plug-ins/common/van-gogh-lic.c @@ -0,0 +1,904 @@ +/* LIC 0.14 -- image filter plug-in for GIMP + * Copyright (C) 1996 Tom Bech + * + * E-mail: tomb@gimp.org + * You can contact the original GIMP authors at gimp@xcf.berkeley.edu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * In other words, you can't sue me for whatever happens while using this ;) + * + * Changes (post 0.10): + * -> 0.11: Fixed a bug in the convolution kernels (Tom). + * -> 0.12: Added Quartic's bilinear interpolation stuff (Tom). + * -> 0.13 Changed some UI stuff causing trouble with the 0.60 release, added + * the (GIMP) tags and changed random() calls to rand() (Tom) + * -> 0.14 Ported to 0.99.11 (Tom) + * + * This plug-in implements the Line Integral Convolution (LIC) as described in + * Cabral et al. "Imaging vector fields using line integral convolution" in the + * Proceedings of ACM SIGGRAPH 93. Publ. by ACM, New York, NY, USA. p. 263-270. + * (See http://www8.cs.umu.se/kurser/TDBD13/VT00/extra/p263-cabral.pdf) + * + * Some of the code is based on code by Steinar Haugen (thanks!), the Perlin + * noise function is practically ripped as is :) + */ + +#include "config.h" + +#include <libgimp/gimp.h> +#include <libgimp/gimpui.h> + +#include "libgimp/stdplugins-intl.h" + + +/************/ +/* Typedefs */ +/************/ + +#define numx 40 /* Pseudo-random vector grid size */ +#define numy 40 + +#define PLUG_IN_PROC "plug-in-lic" +#define PLUG_IN_BINARY "van-gogh-lic" +#define PLUG_IN_ROLE "gimp-van-gogh-lic" + +typedef enum +{ + LIC_HUE, + LIC_SATURATION, + LIC_BRIGHTNESS +} LICEffectChannel; + + +/*****************************/ +/* Global variables and such */ +/*****************************/ + +static gdouble G[numx][numy][2]; + +typedef struct +{ + gdouble filtlen; + gdouble noisemag; + gdouble intsteps; + gdouble minv; + gdouble maxv; + gint effect_channel; + gint effect_operator; + gint effect_convolve; + gint32 effect_image_id; +} LicValues; + +static LicValues licvals; + +static gdouble l = 10.0; +static gdouble dx = 2.0; +static gdouble dy = 2.0; +static gdouble minv = -2.5; +static gdouble maxv = 2.5; +static gdouble isteps = 20.0; + +static gboolean source_drw_has_alpha = FALSE; + +static gint effect_width, effect_height; +static gint border_x, border_y, border_w, border_h; + +static GtkWidget *dialog; + +/************************/ +/* Convenience routines */ +/************************/ + +static void +peek (GeglBuffer *buffer, + gint x, + gint y, + GimpRGB *color) +{ + gegl_buffer_sample (buffer, x, y, NULL, + color, babl_format ("R'G'B'A double"), + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); +} + +static void +poke (GeglBuffer *buffer, + gint x, + gint y, + GimpRGB *color) +{ + gegl_buffer_set (buffer, GEGL_RECTANGLE (x, y, 1, 1), 0, + babl_format ("R'G'B'A double"), color, + GEGL_AUTO_ROWSTRIDE); +} + +static gint +peekmap (const guchar *image, + gint x, + gint y) +{ + while (x < 0) + x += effect_width; + x %= effect_width; + + while (y < 0) + y += effect_height; + y %= effect_height; + + return (gint) image[x + effect_width * y]; +} + +/*************/ +/* Main part */ +/*************/ + +/***************************************************/ +/* Compute the derivative in the x and y direction */ +/* We use these convolution kernels: */ +/* |1 0 -1| | 1 2 1| */ +/* DX: |2 0 -2| DY: | 0 0 0| */ +/* |1 0 -1| | -1 -2 -1| */ +/* (It's a variation of the Sobel kernels, really) */ +/***************************************************/ + +static gint +gradx (const guchar *image, + gint x, + gint y) +{ + gint val = 0; + + val = val + peekmap (image, x-1, y-1); + val = val - peekmap (image, x+1, y-1); + + val = val + 2 * peekmap (image, x-1, y); + val = val - 2 * peekmap (image, x+1, y); + + val = val + peekmap (image, x-1, y+1); + val = val - peekmap (image, x+1, y+1); + + return val; +} + +static gint +grady (const guchar *image, + gint x, + gint y) +{ + gint val = 0; + + val = val + peekmap (image, x-1, y-1); + val = val + 2 * peekmap (image, x, y-1); + val = val + peekmap (image, x+1, y-1); + + val = val - peekmap (image, x-1, y+1); + val = val - 2 * peekmap (image, x, y+1); + val = val - peekmap (image, x+1, y+1); + + return val; +} + +/************************************/ +/* A nice 2nd order cubic spline :) */ +/************************************/ + +static gdouble +cubic (gdouble t) +{ + gdouble at = fabs (t); + + return (at < 1.0) ? at * at * (2.0 * at - 3.0) + 1.0 : 0.0; +} + +static gdouble +omega (gdouble u, + gdouble v, + gint i, + gint j) +{ + while (i < 0) + i += numx; + + while (j < 0) + j += numy; + + i %= numx; + j %= numy; + + return cubic (u) * cubic (v) * (G[i][j][0]*u + G[i][j][1]*v); +} + +/*************************************************************/ +/* The noise function (2D variant of Perlins noise function) */ +/*************************************************************/ + +static gdouble +noise (gdouble x, + gdouble y) +{ + gint i, sti = (gint) floor (x / dx); + gint j, stj = (gint) floor (y / dy); + + gdouble sum = 0.0; + + /* Calculate the gdouble sum */ + /* ======================== */ + + for (i = sti; i <= sti + 1; i++) + for (j = stj; j <= stj + 1; j++) + sum += omega ((x - (gdouble) i * dx) / dx, + (y - (gdouble) j * dy) / dy, + i, j); + + return sum; +} + +/*************************************************/ +/* Generates pseudo-random vectors with length 1 */ +/*************************************************/ + +static void +generatevectors (void) +{ + gdouble alpha; + gint i, j; + GRand *gr; + + gr = g_rand_new(); + + for (i = 0; i < numx; i++) + { + for (j = 0; j < numy; j++) + { + alpha = g_rand_double_range (gr, 0, 2) * G_PI; + G[i][j][0] = cos (alpha); + G[i][j][1] = sin (alpha); + } + } + + g_rand_free (gr); +} + +/* A simple triangle filter */ +/* ======================== */ + +static gdouble +filter (gdouble u) +{ + gdouble f = 1.0 - fabs (u) / l; + + return (f < 0.0) ? 0.0 : f; +} + +/******************************************************/ +/* Compute the Line Integral Convolution (LIC) at x,y */ +/******************************************************/ + +static gdouble +lic_noise (gint x, + gint y, + gdouble vx, + gdouble vy) +{ + gdouble i = 0.0; + gdouble f1 = 0.0, f2 = 0.0; + gdouble u, step = 2.0 * l / isteps; + gdouble xx = (gdouble) x, yy = (gdouble) y; + gdouble c, s; + + /* Get vector at x,y */ + /* ================= */ + + c = vx; + s = vy; + + /* Calculate integral numerically */ + /* ============================== */ + + f1 = filter (-l) * noise (xx + l * c , yy + l * s); + + for (u = -l + step; u <= l; u += step) + { + f2 = filter (u) * noise ( xx - u * c , yy - u * s); + i += (f1 + f2) * 0.5 * step; + f1 = f2; + } + + i = (i - minv) / (maxv - minv); + + i = CLAMP (i, 0.0, 1.0); + + i = (i / 2.0) + 0.5; + + return i; +} + +static void +getpixel (GeglBuffer *buffer, + GimpRGB *p, + gdouble u, + gdouble v) +{ + register gint x1, y1, x2, y2; + gint width, height; + static GimpRGB pp[4]; + + width = border_w; + height = border_h; + + x1 = (gint)u; + y1 = (gint)v; + + if (x1 < 0) + x1 = width - (-x1 % width); + else + x1 = x1 % width; + + if (y1 < 0) + y1 = height - (-y1 % height); + else + y1 = y1 % height; + + x2 = (x1 + 1) % width; + y2 = (y1 + 1) % height; + + peek (buffer, x1, y1, &pp[0]); + peek (buffer, x2, y1, &pp[1]); + peek (buffer, x1, y2, &pp[2]); + peek (buffer, x2, y2, &pp[3]); + + if (source_drw_has_alpha) + *p = gimp_bilinear_rgba (u, v, pp); + else + *p = gimp_bilinear_rgb (u, v, pp); +} + +static void +lic_image (GeglBuffer *buffer, + gint x, + gint y, + gdouble vx, + gdouble vy, + GimpRGB *color) +{ + gdouble u, step = 2.0 * l / isteps; + gdouble xx = (gdouble) x, yy = (gdouble) y; + gdouble c, s; + GimpRGB col = { 0, 0, 0, 0 }; + GimpRGB col1, col2, col3; + + /* Get vector at x,y */ + /* ================= */ + + c = vx; + s = vy; + + /* Calculate integral numerically */ + /* ============================== */ + + getpixel (buffer, &col1, xx + l * c, yy + l * s); + + if (source_drw_has_alpha) + gimp_rgba_multiply (&col1, filter (-l)); + else + gimp_rgb_multiply (&col1, filter (-l)); + + for (u = -l + step; u <= l; u += step) + { + getpixel (buffer, &col2, xx - u * c, yy - u * s); + + if (source_drw_has_alpha) + { + gimp_rgba_multiply (&col2, filter (u)); + + col3 = col1; + gimp_rgba_add (&col3, &col2); + gimp_rgba_multiply (&col3, 0.5 * step); + gimp_rgba_add (&col, &col3); + } + else + { + gimp_rgb_multiply (&col2, filter (u)); + + col3 = col1; + gimp_rgb_add (&col3, &col2); + gimp_rgb_multiply (&col3, 0.5 * step); + gimp_rgb_add (&col, &col3); + } + col1 = col2; + } + if (source_drw_has_alpha) + gimp_rgba_multiply (&col, 1.0 / l); + else + gimp_rgb_multiply (&col, 1.0 / l); + gimp_rgb_clamp (&col); + + *color = col; +} + +static guchar * +rgb_to_hsl (gint32 drawable_ID, + LICEffectChannel effect_channel) +{ + GeglBuffer *buffer; + guchar *themap, data[4]; + gint x, y; + GimpRGB color; + GimpHSL color_hsl; + gdouble val = 0.0; + glong maxc, index = 0; + GRand *gr; + + gr = g_rand_new (); + + maxc = gimp_drawable_width (drawable_ID) * gimp_drawable_height (drawable_ID); + + buffer = gimp_drawable_get_buffer (drawable_ID); + + themap = g_new (guchar, maxc); + + for (y = 0; y < border_h; y++) + { + for (x = 0; x < border_w; x++) + { + data[3] = 255; + + gegl_buffer_sample (buffer, x, y, NULL, + data, babl_format ("R'G'B'A u8"), + GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); + + gimp_rgba_set_uchar (&color, data[0], data[1], data[2], data[3]); + gimp_rgb_to_hsl (&color, &color_hsl); + + switch (effect_channel) + { + case LIC_HUE: + val = color_hsl.h * 255; + break; + case LIC_SATURATION: + val = color_hsl.s * 255; + break; + case LIC_BRIGHTNESS: + val = color_hsl.l * 255; + break; + } + + /* add some random to avoid unstructured areas. */ + val += g_rand_double_range (gr, -1.0, 1.0); + + themap[index++] = (guchar) CLAMP0255 (RINT (val)); + } + } + + g_object_unref (buffer); + + g_rand_free (gr); + + return themap; +} + + +static void +compute_lic (gint32 drawable_ID, + const guchar *scalarfield, + gboolean rotate) +{ + GeglBuffer *src_buffer; + GeglBuffer *dest_buffer; + gint xcount, ycount; + GimpRGB color; + gdouble vx, vy, tmp; + + src_buffer = gimp_drawable_get_buffer (drawable_ID); + dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID); + + for (ycount = 0; ycount < border_h; ycount++) + { + for (xcount = 0; xcount < border_w; xcount++) + { + /* Get derivative at (x,y) and normalize it */ + /* ============================================================== */ + + vx = gradx (scalarfield, xcount, ycount); + vy = grady (scalarfield, xcount, ycount); + + /* Rotate if needed */ + if (rotate) + { + tmp = vy; + vy = -vx; + vx = tmp; + } + + tmp = sqrt (vx * vx + vy * vy); + if (tmp >= 0.000001) + { + tmp = 1.0 / tmp; + vx *= tmp; + vy *= tmp; + } + + /* Convolve with the LIC at (x,y) */ + /* ============================== */ + + if (licvals.effect_convolve == 0) + { + peek (src_buffer, xcount, ycount, &color); + + tmp = lic_noise (xcount, ycount, vx, vy); + + if (source_drw_has_alpha) + gimp_rgba_multiply (&color, tmp); + else + gimp_rgb_multiply (&color, tmp); + } + else + { + lic_image (src_buffer, xcount, ycount, vx, vy, &color); + } + + poke (dest_buffer, xcount, ycount, &color); + } + + gimp_progress_update ((gfloat) ycount / (gfloat) border_h); + } + + g_object_unref (src_buffer); + g_object_unref (dest_buffer); + + gimp_progress_update (1.0); +} + +static void +compute_image (gint32 drawable_ID) +{ + guchar *scalarfield = NULL; + + /* Get some useful info on the input drawable */ + /* ========================================== */ + if (! gimp_drawable_mask_intersect (drawable_ID, + &border_x, &border_y, + &border_w, &border_h)) + return; + + gimp_progress_init (_("Van Gogh (LIC)")); + + if (licvals.effect_convolve == 0) + generatevectors (); + + if (licvals.filtlen < 0.1) + licvals.filtlen = 0.1; + + l = licvals.filtlen; + dx = dy = licvals.noisemag; + minv = licvals.minv / 10.0; + maxv = licvals.maxv / 10.0; + isteps = licvals.intsteps; + + source_drw_has_alpha = gimp_drawable_has_alpha (drawable_ID); + + effect_width = gimp_drawable_width (licvals.effect_image_id); + effect_height = gimp_drawable_height (licvals.effect_image_id); + + switch (licvals.effect_channel) + { + case 0: + scalarfield = rgb_to_hsl (licvals.effect_image_id, LIC_HUE); + break; + case 1: + scalarfield = rgb_to_hsl (licvals.effect_image_id, LIC_SATURATION); + break; + case 2: + scalarfield = rgb_to_hsl (licvals.effect_image_id, LIC_BRIGHTNESS); + break; + } + + compute_lic (drawable_ID, scalarfield, licvals.effect_operator); + + g_free (scalarfield); + + /* Update image */ + /* ============ */ + + gimp_drawable_merge_shadow (drawable_ID, TRUE); + gimp_drawable_update (drawable_ID, border_x, border_y, border_w, border_h); + + gimp_displays_flush (); +} + +/**************************/ +/* Below is only UI stuff */ +/**************************/ + +static gboolean +effect_image_constrain (gint32 image_id, + gint32 drawable_id, + gpointer data) +{ + return gimp_drawable_is_rgb (drawable_id); +} + +static gboolean +create_main_dialog (void) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *frame; + GtkWidget *table; + GtkWidget *combo; + GtkObject *scale_data; + gint row; + gboolean run; + + gimp_ui_init (PLUG_IN_BINARY, TRUE); + + dialog = gimp_dialog_new (_("Van Gogh (LIC)"), PLUG_IN_ROLE, + NULL, 0, + gimp_standard_help_func, PLUG_IN_PROC, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_OK"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gimp_window_set_transient (GTK_WINDOW (dialog)); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + vbox, TRUE, TRUE, 0); + gtk_widget_show (vbox); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + frame = gimp_int_radio_group_new (TRUE, _("Effect Channel"), + G_CALLBACK (gimp_radio_button_update), + &licvals.effect_channel, + licvals.effect_channel, + + _("_Hue"), 0, NULL, + _("_Saturation"), 1, NULL, + _("_Brightness"), 2, NULL, + + NULL); + gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + frame = gimp_int_radio_group_new (TRUE, _("Effect Operator"), + G_CALLBACK (gimp_radio_button_update), + &licvals.effect_operator, + licvals.effect_operator, + + _("_Derivative"), 0, NULL, + _("_Gradient"), 1, NULL, + + NULL); + gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + frame = gimp_int_radio_group_new (TRUE, _("Convolve"), + G_CALLBACK (gimp_radio_button_update), + &licvals.effect_convolve, + licvals.effect_convolve, + + _("_With white noise"), 0, NULL, + _("W_ith source image"), 1, NULL, + + NULL); + gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + /* Effect image menu */ + table = gtk_table_new (1, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + gtk_widget_show (table); + + combo = gimp_drawable_combo_box_new (effect_image_constrain, NULL); + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), + licvals.effect_image_id, + G_CALLBACK (gimp_int_combo_box_get_active), + &licvals.effect_image_id); + + gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, + _("_Effect image:"), 0.0, 0.5, combo, 2, TRUE); + + table = gtk_table_new (5, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + gtk_widget_show (table); + + row = 0; + + scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++, + _("_Filter length:"), 0, 6, + licvals.filtlen, 0.1, 64, 1.0, 8.0, 1, + TRUE, 0, 0, + NULL, NULL); + g_signal_connect (scale_data, "value-changed", + G_CALLBACK (gimp_double_adjustment_update), + &licvals.filtlen); + + scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++, + _("_Noise magnitude:"), 0, 6, + licvals.noisemag, 1, 5, 0.1, 1.0, 1, + TRUE, 0, 0, + NULL, NULL); + g_signal_connect (scale_data, "value-changed", + G_CALLBACK (gimp_double_adjustment_update), + &licvals.noisemag); + + scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++, + _("In_tegration steps:"), 0, 6, + licvals.intsteps, 1, 40, 1.0, 5.0, 1, + TRUE, 0, 0, + NULL, NULL); + g_signal_connect (scale_data, "value-changed", + G_CALLBACK (gimp_double_adjustment_update), + &licvals.intsteps); + + scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++, + _("_Minimum value:"), 0, 6, + licvals.minv, -100, 0, 1, 10, 1, + TRUE, 0, 0, + NULL, NULL); + g_signal_connect (scale_data, "value-changed", + G_CALLBACK (gimp_double_adjustment_update), + &licvals.minv); + + scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++, + _("M_aximum value:"), 0, 6, + licvals.maxv, 0, 100, 1, 10, 1, + TRUE, 0, 0, + NULL, NULL); + g_signal_connect (scale_data, "value-changed", + G_CALLBACK (gimp_double_adjustment_update), + &licvals.maxv); + + gtk_widget_show (dialog); + + run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK); + + gtk_widget_destroy (dialog); + + return run; +} + +/*************************************/ +/* Set parameters to standard values */ +/*************************************/ + +static void +set_default_settings (void) +{ + licvals.filtlen = 5; + licvals.noisemag = 2; + licvals.intsteps = 25; + licvals.minv = -25; + licvals.maxv = 25; + licvals.effect_channel = 2; + licvals.effect_operator = 1; + licvals.effect_convolve = 1; + licvals.effect_image_id = 0; +} + +static void +query (void) +{ + static const GimpParamDef args[] = + { + { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" }, + { GIMP_PDB_IMAGE, "image", "Input image" }, + { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" } + }; + + gimp_install_procedure (PLUG_IN_PROC, + N_("Special effects that nobody understands"), + "No help yet", + "Tom Bech & Federico Mena Quintero", + "Tom Bech & Federico Mena Quintero", + "Version 0.14, September 24 1997", + N_("_Van Gogh (LIC)..."), + "RGB*", + GIMP_PLUGIN, + G_N_ELEMENTS (args), 0, + args, NULL); + + gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Artistic"); +} + +static void +run (const gchar *name, + gint nparams, + const GimpParam *param, + gint *nreturn_vals, + GimpParam **return_vals) +{ + static GimpParam values[1]; + GimpRunMode run_mode; + gint32 drawable_ID; + GimpPDBStatusType status = GIMP_PDB_SUCCESS; + + INIT_I18N (); + gegl_init (NULL, NULL); + + *nreturn_vals = 1; + *return_vals = values; + + values[0].type = GIMP_PDB_STATUS; + values[0].data.d_status = status; + + /* Set default values */ + /* ================== */ + + set_default_settings (); + + /* Possibly retrieve data */ + /* ====================== */ + + gimp_get_data (PLUG_IN_PROC, &licvals); + + run_mode = param[0].data.d_int32; + drawable_ID = param[2].data.d_drawable; + + if (status == GIMP_PDB_SUCCESS) + { + /* Make sure that the drawable is RGBA or RGB color */ + /* ================================================ */ + + if (gimp_drawable_is_rgb (drawable_ID)) + { + switch (run_mode) + { + case GIMP_RUN_INTERACTIVE: + if (create_main_dialog ()) + compute_image (drawable_ID); + + gimp_set_data (PLUG_IN_PROC, &licvals, sizeof (LicValues)); + break; + + case GIMP_RUN_WITH_LAST_VALS: + compute_image (drawable_ID); + break; + + default: + break; + } + } + else + { + status = GIMP_PDB_EXECUTION_ERROR; + } + } + + values[0].data.d_status = status; +} + +const GimpPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +MAIN () |