summaryrefslogtreecommitdiffstats
path: root/plug-ins/common/curve-bend.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/common/curve-bend.c')
-rw-r--r--plug-ins/common/curve-bend.c3402
1 files changed, 3402 insertions, 0 deletions
diff --git a/plug-ins/common/curve-bend.c b/plug-ins/common/curve-bend.c
new file mode 100644
index 0000000..dfa9ff7
--- /dev/null
+++ b/plug-ins/common/curve-bend.c
@@ -0,0 +1,3402 @@
+/* curve_bend plugin for GIMP */
+
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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/>.
+ */
+
+/* Revision history
+ * (2004/02/08) v1.3.18 hof: #133244 exit with execution error if there is
+ * an empty selection
+ * (2003/08/24) v1.3.18 hof: #119937 show busy cursor when recalculating
+ * preview
+ * (2002/09/xx) v1.1.18 mitch and gsr: clean interface
+ * (2000/02/16) v1.1.17b hof: added spinbuttons for rotate entry
+ * (2000/02/16) v1.1.17 hof: undo bugfix (#6012)
+ * don't call gimp_undo_push_group_end
+ * after gimp_displays_flush
+ * (1999/09/13) v1.01 hof: PDB-calls updated for gimp 1.1.9
+ * (1999/05/10) v1.0 hof: first public release
+ * (1999/04/23) v0.0 hof: coding started,
+ * splines and dialog parts are similar to curves.c
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Defines */
+#define PLUG_IN_PROC "plug-in-curve-bend"
+#define PLUG_IN_BINARY "curve-bend"
+#define PLUG_IN_ROLE "gimp-curve-bend"
+#define PLUG_IN_VERSION "v1.3.18 (2003/08/26)"
+#define PLUG_IN_IMAGE_TYPES "RGB*, GRAY*"
+#define PLUG_IN_AUTHOR "Wolfgang Hofer (hof@hotbot.com)"
+#define PLUG_IN_COPYRIGHT "Wolfgang Hofer"
+
+#define PLUG_IN_ITER_NAME "plug_in_curve_bend_Iterator"
+#define PLUG_IN_DATA_ITER_FROM "plug_in_curve_bend_ITER_FROM"
+#define PLUG_IN_DATA_ITER_TO "plug_in_curve_bend_ITER_TO"
+
+#define KEY_POINTFILE "POINTFILE_CURVE_BEND"
+#define KEY_POINTS "POINTS"
+#define KEY_VAL_Y "VAL_Y"
+
+#define MIDDLE 127
+
+#define MIX_CHANNEL(a, b, m) (((a * m) + (b * (255 - m))) / 255)
+
+#define UP_GRAPH 0x1
+#define UP_PREVIEW_EXPOSE 0x4
+#define UP_PREVIEW 0x8
+#define UP_DRAW 0x10
+#define UP_ALL 0xFF
+
+#define GRAPH_WIDTH 256
+#define GRAPH_HEIGHT 256
+#define PREVIEW_SIZE_X 256
+#define PREVIEW_SIZE_Y 256
+#define PV_IMG_WIDTH 128
+#define PV_IMG_HEIGHT 128
+#define RADIUS 3
+#define MIN_DISTANCE 8
+#define PREVIEW_BPP 4
+
+#define SMOOTH 0
+#define GFREE 1
+
+#define GRAPH_MASK GDK_EXPOSURE_MASK | \
+ GDK_POINTER_MOTION_MASK | \
+ GDK_POINTER_MOTION_HINT_MASK | \
+ GDK_ENTER_NOTIFY_MASK | \
+ GDK_BUTTON_PRESS_MASK | \
+ GDK_BUTTON_RELEASE_MASK | \
+ GDK_BUTTON1_MOTION_MASK
+
+
+#define OUTLINE_UPPER 0
+#define OUTLINE_LOWER 1
+
+typedef struct _BenderValues BenderValues;
+struct _BenderValues
+{
+ guchar curve[2][256]; /* for curve_type freehand mode 0 <= curve <= 255 */
+ gdouble points[2][17][2]; /* for curve_type smooth mode 0.0 <= points <= 1.0 */
+
+ gint curve_type;
+
+ gint smoothing;
+ gint antialias;
+ gint work_on_copy;
+ gdouble rotation;
+
+ gint32 total_steps;
+ gdouble current_step;
+};
+
+typedef struct _Curves Curves;
+
+struct _Curves
+{
+ int x, y; /* coords for last mouse click */
+};
+
+typedef struct _BenderDialog BenderDialog;
+
+struct _BenderDialog
+{
+ GtkWidget *shell;
+ GtkWidget *outline_menu;
+ GtkWidget *pv_widget;
+ GtkWidget *graph;
+ GtkAdjustment *rotate_data;
+ GdkPixmap *pixmap;
+ GtkWidget *filechooser;
+
+ GdkCursor *cursor_busy;
+
+ gint32 drawable_id;
+ int color;
+ int outline;
+ gint preview;
+
+ int grab_point;
+ int last;
+ int leftmost;
+ int rightmost;
+ int curve_type;
+ gdouble points[2][17][2]; /* 0.0 <= points <= 1.0 */
+ guchar curve[2][256]; /* 0 <= curve <= 255 */
+ gint32 *curve_ptr[2]; /* 0 <= curve_ptr <= src_drawable_width
+ * both arrays are allocated dynamic,
+ * depending on drawable width
+ */
+ gint32 min2[2];
+ gint32 max2[2];
+ gint32 zero2[2];
+
+ gboolean show_progress;
+ gboolean smoothing;
+ gboolean antialias;
+ gboolean work_on_copy;
+ gdouble rotation;
+
+ gint32 preview_image_id;
+ gint32 preview_layer_id1;
+ gint32 preview_layer_id2;
+
+ BenderValues *bval_from;
+ BenderValues *bval_to;
+ BenderValues *bval_curr;
+
+ gboolean run;
+};
+
+/* points Coords:
+ *
+ * 1.0 +----+----+----+----+
+ * | . | | |
+ * +--.-+--.-+----+----+
+ * . | . | |
+ * 0.5 +----+----+-.--+----+
+ * | | | . .
+ * +----+----+----+-.-.+
+ * | | | | |
+ * 0.0 +----+----+----+----+
+ * 0.0 0.5 1.0
+ *
+ * curve Coords:
+ *
+ * OUTLINE_UPPER OUTLINE_LOWER
+ *
+ * 255 +----+----+----+----+ 255 +----+----+----+----+
+ * | . | | | --- max | . | | | --- max
+ * +--.-+--.-+----+----+ +--.-+--.-+----+----+
+ * . | . | | zero ___ . | . | |
+ * +----+----+-.--+----+ +----+----+-.--+----+
+ * | | | . . --- zero | | | . .
+ * +----+----+----+-.-.+ ___ min +----+----+----+-.-.+ ___ min
+ * | | | | | | | | | |
+ * 0 +----+----+----+----+ 0 +----+----+----+----+
+ * 0 255 0 255
+ */
+
+typedef double CRMatrix[4][4];
+
+typedef struct
+{
+ guint32 drawable_id;
+ gint width;
+ gint height;
+ GeglBuffer *buffer;
+ const Babl *format;
+ gint x1;
+ gint y1;
+ gint x2;
+ gint y2;
+ gint index_alpha; /* 0 == no alpha, 1 == GREYA, 3 == RGBA */
+ gint bpp;
+ gint tile_width;
+ gint tile_height;
+} t_GDRW;
+
+typedef struct
+{
+ gint32 y;
+ guchar color[4];
+} t_Last;
+
+
+/* curves action functions */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static BenderDialog * bender_new_dialog (gint32 drawable_id);
+static void bender_update (BenderDialog *,
+ int);
+static void bender_plot_curve (BenderDialog *,
+ int,
+ int,
+ int,
+ int,
+ gint32,
+ gint32,
+ gint);
+static void bender_calculate_curve (BenderDialog *,
+ gint32,
+ gint32,
+ gint);
+static void bender_rotate_adj_callback (GtkAdjustment *,
+ gpointer);
+static void bender_border_callback (GtkWidget *,
+ gpointer);
+static void bender_type_callback (GtkWidget *,
+ gpointer);
+static void bender_reset_callback (GtkWidget *,
+ gpointer);
+static void bender_copy_callback (GtkWidget *,
+ gpointer);
+static void bender_copy_inv_callback (GtkWidget *,
+ gpointer);
+static void bender_swap_callback (GtkWidget *,
+ gpointer);
+static void bender_response (GtkWidget *,
+ gint,
+ BenderDialog *);
+static void bender_smoothing_callback (GtkWidget *,
+ gpointer);
+static void bender_antialias_callback (GtkWidget *,
+ gpointer);
+static void bender_work_on_copy_callback (GtkWidget *,
+ gpointer);
+static void bender_preview_update (GtkWidget *,
+ gpointer);
+static void bender_preview_update_once (GtkWidget *,
+ gpointer);
+static void bender_load_callback (GtkWidget *,
+ BenderDialog *);
+static void bender_save_callback (GtkWidget *,
+ BenderDialog *);
+static gint bender_graph_events (GtkWidget *,
+ GdkEvent *,
+ BenderDialog *);
+static void bender_CR_compose (CRMatrix,
+ CRMatrix,
+ CRMatrix);
+static void bender_init_min_max (BenderDialog *,
+ gint32);
+static BenderDialog * do_dialog (gint32 drawable_id);
+static void p_init_gdrw (t_GDRW *gdrw,
+ gint32 drawable_id,
+ int shadow);
+static void p_end_gdrw (t_GDRW *gdrw);
+static gint32 p_main_bend (BenderDialog *cd,
+ guint32,
+ gint);
+static gint32 p_create_pv_image (gint32 src_drawable_id,
+ gint32 *layer_id);
+static void p_render_preview (BenderDialog *cd,
+ gint32 layer_id);
+static void p_get_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *pixel);
+static void p_put_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *pixel);
+static void p_put_mix_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *color,
+ gint32 nb_curvy,
+ gint32 nb2_curvy,
+ gint32 curvy);
+static void p_stretch_curves (BenderDialog *cd,
+ gint32 xmax,
+ gint32 ymax);
+static void p_cd_to_bval (BenderDialog *cd,
+ BenderValues *bval);
+static void p_cd_from_bval (BenderDialog *cd,
+ BenderValues *bval);
+static void p_store_values (BenderDialog *cd);
+static void p_retrieve_values (BenderDialog *cd);
+static void p_bender_calculate_iter_curve (BenderDialog *cd,
+ gint32 xmax,
+ gint32 ymax);
+static void p_delta_gdouble (double *val,
+ double val_from,
+ double val_to,
+ gint32 total_steps,
+ gdouble current_step);
+static void p_delta_gint32 (gint32 *val,
+ gint32 val_from,
+ gint32 val_to,
+ gint32 total_steps,
+ gdouble current_step);
+static void p_copy_points (BenderDialog *cd,
+ int outline,
+ int xy,
+ int argc,
+ gdouble *floatarray);
+static void p_copy_yval (BenderDialog *cd,
+ int outline,
+ int argc,
+ guint8 *int8array);
+static int p_save_pointfile (BenderDialog *cd,
+ const gchar *filename);
+
+
+/* Global Variables */
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+static CRMatrix CR_basis =
+{
+ { -0.5, 1.5, -1.5, 0.5 },
+ { 1.0, -2.5, 2.0, -0.5 },
+ { -0.5, 0.0, 0.5, 0.0 },
+ { 0.0, 1.0, 0.0, 0.0 },
+};
+
+static int gb_debug = FALSE;
+
+/* Functions */
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX PDB_STUFF XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+#ifdef ROTATE_OPTIMIZE
+
+/* ============================================================================
+ * p_pdb_procedure_available
+ * if requested procedure is available in the PDB return the number of args
+ * (0 upto n) that are needed to call the procedure.
+ * if not available return -1
+ * ============================================================================
+ */
+
+static gint
+p_pdb_procedure_available (const gchar *proc_name)
+{
+ gint nparams;
+ gint nreturn_vals;
+ GimpPDBProcType proc_type;
+ gchar *proc_blurb;
+ gchar *proc_help;
+ gchar *proc_author;
+ gchar *proc_copyright;
+ gchar *proc_date;
+ GimpParamDef *params;
+ GimpParamDef *return_vals;
+ gint rc;
+
+ rc = 0;
+
+ /* Query the gimp application's procedural database
+ * regarding a particular procedure.
+ */
+ if (gimp_procedural_db_proc_info (proc_name,
+ &proc_blurb,
+ &proc_help,
+ &proc_author,
+ &proc_copyright,
+ &proc_date,
+ &proc_type,
+ &nparams, &nreturn_vals,
+ &params, &return_vals))
+ {
+ /* procedure found in PDB */
+ return nparams;
+ }
+
+ g_printerr ("Warning: Procedure %s not found.\n", proc_name);
+ return -1;
+}
+
+#endif /* ROTATE_OPTIMIZE */
+
+static gint
+p_gimp_rotate (gint32 image_id,
+ gint32 drawable_id,
+ gint32 interpolation,
+ gdouble angle_deg)
+{
+ gdouble angle_rad;
+ gint rc;
+
+#ifdef ROTATE_OPTIMIZE
+ static gchar *rotate_proc = "plug-in-rotate";
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 angle_step;
+ gint nparams;
+
+ if (angle_deg == 90.0) { angle_step = 1; }
+ else if(angle_deg == 180.0) { angle_step = 2; }
+ else if(angle_deg == 270.0) { angle_step = 3; }
+ else { angle_step = 0; }
+
+ if (angle_step != 0)
+ {
+ nparams = p_pdb_procedure_available (rotate_proc);
+ if (nparams == 5)
+ {
+ /* use faster rotate plugin on multiples of 90 degrees */
+ return_vals = gimp_run_procedure (rotate_proc,
+ &nreturn_vals,
+ GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+ GIMP_PDB_IMAGE, image_id,
+ GIMP_PDB_DRAWABLE, drawable_id,
+ GIMP_PDB_INT32, angle_step,
+ GIMP_PDB_INT32, FALSE, /* don't rotate the whole image */
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ return 0;
+ }
+ }
+ }
+#endif /* ROTATE_OPTIMIZE */
+
+ angle_rad = (angle_deg * G_PI) / 180.0;
+
+ gimp_context_push ();
+ if (! interpolation)
+ gimp_context_set_interpolation (GIMP_INTERPOLATION_NONE);
+ gimp_context_set_transform_resize (GIMP_TRANSFORM_RESIZE_ADJUST);
+ rc = gimp_item_transform_rotate (drawable_id,
+ angle_rad,
+ TRUE /*auto_center*/,
+ -1.0 /*center_x*/,
+ -1.0 /*center_y*/);
+ gimp_context_pop ();
+
+ if (rc == -1)
+ g_printerr ("Error: gimp_drawable_transform_rotate_default call failed\n");
+
+ return rc;
+}
+
+/* ============================================================================
+ * p_if_selection_float_it
+ * ============================================================================
+ */
+
+static gint32
+p_if_selection_float_it (gint32 image_id,
+ gint32 layer_id)
+{
+ if (! gimp_layer_is_floating_sel (layer_id))
+ {
+ gint32 sel_channel_id;
+ gint32 x1, x2, y1, y2;
+ gint32 non_empty;
+
+ /* check and see if we have a selection mask */
+ sel_channel_id = gimp_image_get_selection (image_id);
+
+ gimp_selection_bounds (image_id, &non_empty, &x1, &y1, &x2, &y2);
+
+ if (non_empty && sel_channel_id >= 0)
+ {
+ /* selection is TRUE, make a layer (floating selection) from
+ the selection */
+ if (gimp_edit_copy (layer_id))
+ {
+ layer_id = gimp_edit_paste (layer_id, FALSE);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ }
+
+ return layer_id;
+}
+
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX END_PDB_STUFF XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+/*
+ * M M AAAAA IIIIII N N
+ * M M M M A A II NN N
+ * M M M AAAAAAA II N N N
+ * M M A A II N NN
+ * M M A A IIIIII N N
+ */
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }"},
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (must be a layer without layermask)"},
+ { GIMP_PDB_FLOAT, "rotation", "Direction {angle 0 to 360 degree } of the bend effect"},
+ { GIMP_PDB_INT32, "smoothing", "Smoothing { TRUE, FALSE }"},
+ { GIMP_PDB_INT32, "antialias", "Antialias { TRUE, FALSE }"},
+ { GIMP_PDB_INT32, "work-on-copy", "{ TRUE, FALSE } TRUE: copy the drawable and bend the copy"},
+ { GIMP_PDB_INT32, "curve-type", " { 0, 1 } 0 == smooth (use 17 points), 1 == freehand (use 256 val_y) "},
+ { GIMP_PDB_INT32, "argc-upper-point-x", "{2 <= argc <= 17} "},
+ { GIMP_PDB_FLOATARRAY, "upper-point-x", "array of 17 x point_koords { 0.0 <= x <= 1.0 or -1 for unused point }"},
+ { GIMP_PDB_INT32, "argc-upper-point-y", "{2 <= argc <= 17} "},
+ { GIMP_PDB_FLOATARRAY, "upper-point-y", "array of 17 y point_koords { 0.0 <= y <= 1.0 or -1 for unused point }"},
+ { GIMP_PDB_INT32, "argc-lower_point-x", "{2 <= argc <= 17} "},
+ { GIMP_PDB_FLOATARRAY, "lower-point-x", "array of 17 x point_koords { 0.0 <= x <= 1.0 or -1 for unused point }"},
+ { GIMP_PDB_INT32, "argc-lower-point-y", "{2 <= argc <= 17} "},
+ { GIMP_PDB_FLOATARRAY, "lower_point_y", "array of 17 y point_koords { 0.0 <= y <= 1.0 or -1 for unused point }"},
+ { GIMP_PDB_INT32, "argc-upper-val-y", "{ 256 } "},
+ { GIMP_PDB_INT8ARRAY, "upper-val-y", "array of 256 y freehand koord { 0 <= y <= 255 }"},
+ { GIMP_PDB_INT32, "argc-lower-val-y", "{ 256 } "},
+ { GIMP_PDB_INT8ARRAY, "lower-val-y", "array of 256 y freehand koord { 0 <= y <= 255 }"}
+ };
+
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_LAYER, "bent-layer", "the handled layer" }
+ };
+
+ static const GimpParamDef args_iter[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_INT32, "total-steps", "total number of steps (# of layers-1 to apply the related plug-in)" },
+ { GIMP_PDB_FLOAT, "current-step", "current (for linear iterations this is the layerstack position, otherwise some value in between)" },
+ { GIMP_PDB_INT32, "len-struct", "length of stored data structure with id is equal to the plug_in proc_name" },
+ };
+
+ /* the actual installation of the bend plugin */
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Bend the image using two control curves"),
+ "This plug-in does bend the active layer "
+ "If there is a current selection it is copied to "
+ "floating selection and the curve_bend distortion "
+ "is done on the floating selection. If "
+ "work_on_copy parameter is TRUE, the curve_bend "
+ "distortion is done on a copy of the active layer "
+ "(or floating selection). The upper and lower edges "
+ "are bent in shape of 2 spline curves. both (upper "
+ "and lower) curves are determined by upto 17 points "
+ "or by 256 Y-Values if curve_type == 1 (freehand "
+ "mode) If rotation is not 0, the layer is rotated "
+ "before and rotated back after the bend operation. "
+ "This enables bending in other directions than "
+ "vertical. bending usually changes the size of "
+ "the handled layer. this plug-in sets the offsets "
+ "of the handled layer to keep its center at the "
+ "same position",
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ PLUG_IN_VERSION,
+ N_("_Curve Bend..."),
+ PLUG_IN_IMAGE_TYPES,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_vals),
+ args,
+ return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Distorts");
+
+ /* the installation of the Iterator procedure for the bend plugin */
+ gimp_install_procedure (PLUG_IN_ITER_NAME,
+ "This procedure calculates the modified values "
+ "for one iterationstep for the call of "
+ "plug_in_curve_bend",
+ "",
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ PLUG_IN_VERSION,
+ NULL, /* do not appear in menus */
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args_iter), 0,
+ args_iter, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ const gchar *env;
+ BenderDialog *cd;
+ gint32 active_drawable_id = -1;
+ gint32 image_id = -1;
+ gint32 layer_id = -1;
+ GError *error = NULL;
+
+ /* Get the runmode from the in-parameters */
+ GimpRunMode run_mode = param[0].data.d_int32;
+
+ /* status variable, use it to check for errors in invocation usually only
+ during non-interactive calling */
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ /*always return at least the status to the caller. */
+ static GimpParam values[2];
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ cd = NULL;
+
+ env = g_getenv ("BEND_DEBUG");
+ if (env != NULL)
+ {
+ if ((*env != 'n') && (*env != 'N')) gb_debug = 1;
+ }
+
+ if (gb_debug) g_printerr ("\n\nDEBUG: run %s\n", name);
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ values[1].type = GIMP_PDB_LAYER;
+ values[1].data.d_int32 = -1;
+
+ *nreturn_vals = 2;
+ *return_vals = values;
+
+ if (strcmp (name, PLUG_IN_ITER_NAME) == 0)
+ {
+ gint32 len_struct;
+ gint32 total_steps;
+ gdouble current_step;
+ BenderValues bval; /* current values while iterating */
+ BenderValues bval_from, bval_to; /* start and end values */
+
+ /* Iterator procedure for animated calls is usually called from
+ * "plug_in_gap_layers_run_animfilter"
+ * (always run noninteractive)
+ */
+ if ((run_mode == GIMP_RUN_NONINTERACTIVE) && (nparams == 4))
+ {
+ total_steps = param[1].data.d_int32;
+ current_step = param[2].data.d_float;
+ len_struct = param[3].data.d_int32;
+
+ if (len_struct == sizeof (bval))
+ {
+ /* get _FROM and _TO data,
+ * This data was stored by plug_in_gap_layers_run_animfilter
+ */
+ gimp_get_data (PLUG_IN_DATA_ITER_FROM, &bval_from);
+ gimp_get_data (PLUG_IN_DATA_ITER_TO, &bval_to);
+ bval = bval_from;
+
+ p_delta_gdouble (&bval.rotation, bval_from.rotation,
+ bval_to.rotation, total_steps, current_step);
+ /* note: iteration of curve and points arrays would not
+ * give useful results. (there might be different
+ * number of points in the from/to bender values )
+ * the iteration is done later, (see
+ * p_bender_calculate_iter_curve) when the curve
+ * is calculated.
+ */
+
+ bval.total_steps = total_steps;
+ bval.current_step = current_step;
+
+ gimp_set_data (PLUG_IN_PROC, &bval, sizeof (bval));
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ values[0].data.d_status = status;
+ return;
+ }
+
+ /* get image and drawable */
+ image_id = param[1].data.d_int32;
+ layer_id = param[2].data.d_drawable;
+
+ if (! gimp_item_is_layer (layer_id))
+ {
+ g_set_error (&error, 0, 0, "%s",
+ _("Can operate on layers only "
+ "(but was called on channel or mask)."));
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* check for layermask */
+ if (gimp_layer_get_mask (layer_id) > 0)
+ {
+ g_set_error (&error, 0, 0, "%s",
+ _("Cannot operate on layers with masks."));
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* if there is a selection, make it the floating selection layer */
+ active_drawable_id = p_if_selection_float_it (image_id, layer_id);
+ if (active_drawable_id < 0)
+ {
+ /* could not float the selection because selection rectangle
+ * is completely empty return GIMP_PDB_EXECUTION_ERROR
+ */
+ g_set_error (&error, 0, 0, "%s",
+ _("Cannot operate on empty selections."));
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* how are we running today? */
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* how are we running today? */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data from a previous run */
+ /* gimp_get_data (PLUG_IN_PROC, &g_bndvals); */
+
+ /* Get information from the dialog */
+ cd = do_dialog (active_drawable_id);
+ cd->show_progress = TRUE;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* check to see if invoked with the correct number of parameters */
+ if (nparams >= 20)
+ {
+ cd = g_new (BenderDialog, 1);
+ cd->run = TRUE;
+ cd->show_progress = TRUE;
+ cd->drawable_id = active_drawable_id;
+
+ cd->rotation = param[3].data.d_float;
+ cd->smoothing = param[4].data.d_int32 != 0;
+ cd->antialias = param[5].data.d_int32 != 0;
+ cd->work_on_copy = param[6].data.d_int32 != 0;
+ cd->curve_type = param[7].data.d_int32 != 0;
+
+ p_copy_points (cd, OUTLINE_UPPER, 0,
+ param[8].data.d_int32,
+ param[9].data.d_floatarray);
+ p_copy_points (cd, OUTLINE_UPPER, 1,
+ param[10].data.d_int32,
+ param[11].data.d_floatarray);
+ p_copy_points (cd, OUTLINE_LOWER, 0,
+ param[12].data.d_int32,
+ param[13].data.d_floatarray);
+ p_copy_points (cd, OUTLINE_LOWER, 1,
+ param[14].data.d_int32,
+ param[15].data.d_floatarray);
+
+ p_copy_yval (cd, OUTLINE_UPPER,
+ param[16].data.d_int32,
+ param[17].data.d_int8array);
+ p_copy_yval (cd, OUTLINE_LOWER,
+ param[18].data.d_int32,
+ param[19].data.d_int8array);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ cd = g_new (BenderDialog, 1);
+ cd->run = TRUE;
+ cd->show_progress = TRUE;
+ cd->drawable_id = active_drawable_id;
+ p_retrieve_values (cd); /* Possibly retrieve data from a previous run */
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (! cd)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Run the main function */
+
+ if (cd->run)
+ {
+ gint32 bent_layer_id;
+
+ gimp_image_undo_group_start (image_id);
+
+ bent_layer_id = p_main_bend (cd, cd->drawable_id,
+ cd->work_on_copy);
+
+ gimp_image_undo_group_end (image_id);
+
+ /* Store variable states for next run */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ p_store_values (cd);
+ }
+
+ /* return the id of handled layer */
+ values[1].data.d_int32 = bent_layer_id;
+ }
+ else
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static int
+p_save_pointfile (BenderDialog *cd,
+ const gchar *filename)
+{
+ FILE *fp;
+ gint j;
+
+ fp = g_fopen(filename, "w+b");
+ if (! fp)
+ {
+ g_message (_("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ fprintf (fp, "%s\n", KEY_POINTFILE);
+ fprintf (fp, "VERSION 1.0\n\n");
+
+ fprintf (fp, "# points for upper and lower smooth curve (0.0 <= pt <= 1.0)\n");
+ fprintf (fp, "# there are upto 17 points where unused points are set to -1\n");
+ fprintf (fp, "# UPPERX UPPERY LOWERX LOWERY\n");
+ fprintf (fp, "\n");
+
+ for(j = 0; j < 17; j++)
+ {
+ fprintf (fp, "%s %+.6f %+.6f %+.6f %+.6f\n", KEY_POINTS,
+ (float)cd->points[OUTLINE_UPPER][j][0],
+ (float)cd->points[OUTLINE_UPPER][j][1],
+ (float)cd->points[OUTLINE_LOWER][j][0],
+ (float)cd->points[OUTLINE_LOWER][j][1] );
+ }
+
+ fprintf (fp, "\n");
+ fprintf (fp, "# y values for upper/lower freehand curve (0 <= y <= 255) \n");
+ fprintf (fp, "# there must be exactly 256 y values \n");
+ fprintf (fp, "# UPPER_Y LOWER_Y\n");
+ fprintf (fp, "\n");
+
+ for (j = 0; j < 256; j++)
+ {
+ fprintf (fp, "%s %3d %3d\n", KEY_VAL_Y,
+ (int)cd->curve[OUTLINE_UPPER][j],
+ (int)cd->curve[OUTLINE_LOWER][j]);
+ }
+
+ fclose (fp);
+
+ return 0;
+}
+
+static int
+p_load_pointfile (BenderDialog *cd,
+ const gchar *filename)
+{
+ gint pi, ci, n, len;
+ FILE *fp;
+ char buff[2000];
+ float fux, fuy, flx, fly;
+ gint iuy, ily ;
+
+ fp = g_fopen(filename, "rb");
+ if (! fp)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ pi = 0;
+ ci = 0;
+
+ if (! fgets (buff, 2000 - 1, fp))
+ {
+ g_message (_("Error while reading '%s': %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ fclose (fp);
+ return -1;
+ }
+
+ if (strncmp (buff, KEY_POINTFILE, strlen(KEY_POINTFILE)) == 0)
+ {
+ while (NULL != fgets (buff, 2000-1, fp))
+ {
+ len = strlen(KEY_POINTS);
+
+ if (strncmp (buff, KEY_POINTS, len) == 0)
+ {
+ n = sscanf (&buff[len],
+ "%f %f %f %f", &fux, &fuy, &flx, &fly);
+
+ if ((n == 4) && (pi < 17))
+ {
+ cd->points[OUTLINE_UPPER][pi][0] = fux;
+ cd->points[OUTLINE_UPPER][pi][1] = fuy;
+ cd->points[OUTLINE_LOWER][pi][0] = flx;
+ cd->points[OUTLINE_LOWER][pi][1] = fly;
+ pi++;
+ }
+ else
+ {
+ g_printf("warning: BAD points[%d] in file %s are ignored\n", pi, filename);
+ }
+ }
+
+ len = strlen (KEY_VAL_Y);
+
+ if (strncmp (buff, KEY_VAL_Y, len) == 0)
+ {
+ n = sscanf (&buff[len], "%d %d", &iuy, &ily);
+
+ if ((n == 2) && (ci < 256))
+ {
+ cd->curve[OUTLINE_UPPER][ci] = iuy;
+ cd->curve[OUTLINE_LOWER][ci] = ily;
+ ci++;
+ }
+ else
+ {
+ g_printf("warning: BAD y_vals[%d] in file %s are ignored\n", ci, filename);
+ }
+ }
+ }
+ }
+
+ fclose (fp);
+
+ return 0;
+}
+
+static void
+p_cd_to_bval (BenderDialog *cd,
+ BenderValues *bval)
+{
+ gint i, j;
+
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < 256; j++)
+ {
+ bval->curve[i][j] = cd->curve[i][j];
+ }
+
+ for (j = 0; j < 17; j++)
+ {
+ bval->points[i][j][0] = cd->points[i][j][0]; /* x */
+ bval->points[i][j][1] = cd->points[i][j][1]; /* y */
+ }
+ }
+
+ bval->curve_type = cd->curve_type;
+ bval->smoothing = cd->smoothing;
+ bval->antialias = cd->antialias;
+ bval->work_on_copy = cd->work_on_copy;
+ bval->rotation = cd->rotation;
+
+ bval->total_steps = 0;
+ bval->current_step = 0.0;
+}
+
+static void
+p_cd_from_bval (BenderDialog *cd,
+ BenderValues *bval)
+{
+ gint i, j;
+
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < 256; j++)
+ {
+ cd->curve[i][j] = bval->curve[i][j];
+ }
+
+ for (j = 0; j < 17; j++)
+ {
+ cd->points[i][j][0] = bval->points[i][j][0]; /* x */
+ cd->points[i][j][1] = bval->points[i][j][1]; /* y */
+ }
+ }
+
+ cd->curve_type = bval->curve_type;
+ cd->smoothing = bval->smoothing;
+ cd->antialias = bval->antialias;
+ cd->work_on_copy = bval->work_on_copy;
+ cd->rotation = bval->rotation;
+}
+
+static void
+p_store_values (BenderDialog *cd)
+{
+ BenderValues bval;
+
+ p_cd_to_bval (cd, &bval);
+ gimp_set_data (PLUG_IN_PROC, &bval, sizeof (bval));
+}
+
+static void
+p_retrieve_values (BenderDialog *cd)
+{
+ BenderValues bval;
+
+ bval.total_steps = 0;
+ bval.current_step = -444.4; /* init with an invalid dummy value */
+
+ gimp_get_data (PLUG_IN_PROC, &bval);
+
+ if (bval.total_steps == 0)
+ {
+ cd->bval_from = NULL;
+ cd->bval_to = NULL;
+ if(bval.current_step != -444.4)
+ {
+ /* last_value data was retrieved (and dummy value was overwritten) */
+ p_cd_from_bval(cd, &bval);
+ }
+ }
+ else
+ {
+ cd->bval_from = g_new (BenderValues, 1);
+ cd->bval_to = g_new (BenderValues, 1);
+ cd->bval_curr = g_new (BenderValues, 1);
+ *cd->bval_curr = bval;
+
+ /* it seems that we are called from GAP with "Varying Values" */
+ gimp_get_data(PLUG_IN_DATA_ITER_FROM, cd->bval_from);
+ gimp_get_data(PLUG_IN_DATA_ITER_TO, cd->bval_to);
+ *cd->bval_curr = bval;
+ p_cd_from_bval(cd, cd->bval_curr);
+ cd->work_on_copy = FALSE;
+ }
+}
+
+static void
+p_delta_gdouble (double *val,
+ double val_from,
+ double val_to,
+ gint32 total_steps,
+ gdouble current_step)
+{
+ double delta;
+
+ if (total_steps < 1)
+ return;
+
+ delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step);
+ *val = val_from + delta;
+}
+
+static void
+p_delta_gint32 (gint32 *val,
+ gint32 val_from,
+ gint32 val_to,
+ gint32 total_steps,
+ gdouble current_step)
+{
+ double delta;
+
+ if (total_steps < 1)
+ return;
+
+ delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step);
+ *val = val_from + delta;
+}
+
+static void
+p_copy_points (BenderDialog *cd,
+ int outline,
+ int xy,
+ int argc,
+ gdouble *floatarray)
+{
+ int j;
+
+ for (j = 0; j < 17; j++)
+ {
+ cd->points[outline][j][xy] = -1;
+ }
+
+ for (j = 0; j < argc; j++)
+ {
+ cd->points[outline][j][xy] = floatarray[j];
+ }
+}
+
+static void
+p_copy_yval (BenderDialog *cd,
+ int outline,
+ int argc,
+ guint8 *int8array)
+{
+ int j;
+ guchar fill;
+
+ fill = MIDDLE;
+
+ for (j = 0; j < 256; j++)
+ {
+ if (j < argc)
+ {
+ fill = cd->curve[outline][j] = int8array[j];
+ }
+ else
+ {
+ cd->curve[outline][j] = fill;
+ }
+ }
+}
+
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+/* curves machinery */
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+static BenderDialog *
+do_dialog (gint32 drawable_id)
+{
+ BenderDialog *cd;
+
+ /* Init GTK */
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ /* The curve_bend dialog */
+ cd = bender_new_dialog (drawable_id);
+
+ /* create temporary image (with a small copy of drawable) for the preview */
+ cd->preview_image_id = p_create_pv_image (drawable_id,
+ &cd->preview_layer_id1);
+ cd->preview_layer_id2 = -1;
+
+ if (! gtk_widget_get_visible (cd->shell))
+ gtk_widget_show (cd->shell);
+
+ bender_update (cd, UP_GRAPH | UP_DRAW | UP_PREVIEW_EXPOSE);
+
+ gtk_main ();
+ gdk_flush ();
+
+ gimp_image_delete(cd->preview_image_id);
+ cd->preview_image_id = -1;
+ cd->preview_layer_id1 = -1;
+ cd->preview_layer_id2 = -1;
+
+ return cd;
+}
+
+/**************************/
+/* Select Curves dialog */
+/**************************/
+
+static BenderDialog *
+bender_new_dialog (gint32 drawable_id)
+{
+ BenderDialog *cd;
+ GtkWidget *main_hbox;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *vbox2;
+ GtkWidget *abox;
+ GtkWidget *frame;
+ GtkWidget *upper, *lower;
+ GtkWidget *smooth, *freew;
+ GtkWidget *toggle;
+ GtkWidget *button;
+ GtkWidget *spinbutton;
+ GtkWidget *label;
+ GdkDisplay *display;
+ gint i, j;
+
+ cd = g_new (BenderDialog, 1);
+
+ cd->preview = FALSE;
+ cd->curve_type = SMOOTH;
+ cd->pixmap = NULL;
+ cd->filechooser = NULL;
+ cd->outline = OUTLINE_UPPER;
+ cd->show_progress = FALSE;
+ cd->smoothing = TRUE;
+ cd->antialias = TRUE;
+ cd->work_on_copy = FALSE;
+ cd->rotation = 0.0; /* vertical bend */
+
+ cd->drawable_id = drawable_id;
+
+ cd->color = gimp_drawable_is_rgb (drawable_id);
+
+ cd->run = FALSE;
+ cd->bval_from = NULL;
+ cd->bval_to = NULL;
+ cd->bval_curr = NULL;
+
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 256; j++)
+ cd->curve[i][j] = MIDDLE;
+
+ cd->grab_point = -1;
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < 17; j++)
+ {
+ cd->points[i][j][0] = -1;
+ cd->points[i][j][1] = -1;
+ }
+ cd->points[i][0][0] = 0.0; /* x */
+ cd->points[i][0][1] = 0.5; /* y */
+ cd->points[i][16][0] = 1.0; /* x */
+ cd->points[i][16][1] = 0.5; /* y */
+ }
+
+ p_retrieve_values(cd); /* Possibly retrieve data from a previous run */
+
+ /* The shell and main vbox */
+ cd->shell = gimp_dialog_new (_("Curve Bend"), 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 (cd->shell),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (cd->shell));
+
+ g_signal_connect (cd->shell, "response",
+ G_CALLBACK (bender_response),
+ cd);
+
+ /* busy cursor */
+ display = gtk_widget_get_display (cd->shell);
+ cd->cursor_busy = gdk_cursor_new_for_display (display, GDK_WATCH);
+
+ /* The main hbox */
+ main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (cd->shell))),
+ main_hbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_hbox);
+
+ /* Left side column */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (main_hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ /* Preview area, top of column */
+ frame = gimp_frame_new (_("Preview"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox2);
+ gtk_widget_show (vbox2);
+
+ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox2), abox, FALSE, FALSE, 0);
+ gtk_widget_show (abox);
+
+ /* The range drawing area */
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (abox), frame);
+ gtk_widget_show (frame);
+
+ cd->pv_widget = gimp_preview_area_new ();
+ gtk_widget_set_size_request (cd->pv_widget,
+ PREVIEW_SIZE_X, PREVIEW_SIZE_Y);
+ gtk_container_add (GTK_CONTAINER (frame), cd->pv_widget);
+ gtk_widget_show (cd->pv_widget);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_end (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* The preview button */
+ button = gtk_button_new_with_mnemonic (_("_Preview Once"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_preview_update_once),
+ cd);
+
+ /* The preview toggle */
+ toggle = gtk_check_button_new_with_mnemonic (_("Automatic pre_view"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->preview);
+ gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (bender_preview_update),
+ cd);
+
+ /* Options area, bottom of column */
+ frame = gimp_frame_new (_("Options"));
+ gtk_box_pack_end (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ /* Render Options */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* Rotate spinbutton */
+ label = gtk_label_new_with_mnemonic (_("Rotat_e:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ cd->rotate_data = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0.0, 360.0, 1, 45, 0));
+ gtk_adjustment_set_value (cd->rotate_data, cd->rotation);
+
+ spinbutton = gimp_spin_button_new (cd->rotate_data, 0.5, 1);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+
+ g_signal_connect (cd->rotate_data, "value-changed",
+ G_CALLBACK (bender_rotate_adj_callback),
+ cd);
+
+ /* The smoothing toggle */
+ toggle = gtk_check_button_new_with_mnemonic (_("Smoo_thing"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->smoothing);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (bender_smoothing_callback),
+ cd);
+
+ /* The antialiasing toggle */
+ toggle = gtk_check_button_new_with_mnemonic (_("_Antialiasing"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->antialias);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (bender_antialias_callback),
+ cd);
+
+ /* The work_on_copy toggle */
+ toggle = gtk_check_button_new_with_mnemonic (_("Work on cop_y"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->work_on_copy);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (bender_work_on_copy_callback),
+ cd);
+
+ /* The curves graph */
+ frame = gimp_frame_new (_("Modify Curves"));
+ gtk_box_pack_start (GTK_BOX (main_hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), abox, FALSE, FALSE, 0);
+ gtk_widget_show (abox);
+
+ cd->graph = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (cd->graph,
+ GRAPH_WIDTH + RADIUS * 2,
+ GRAPH_HEIGHT + RADIUS * 2);
+ gtk_widget_set_events (cd->graph, GRAPH_MASK);
+ gtk_container_add (GTK_CONTAINER (abox), cd->graph);
+ gtk_widget_show (cd->graph);
+
+ g_signal_connect (cd->graph, "event",
+ G_CALLBACK (bender_graph_events),
+ cd);
+
+ 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, _("Curve for Border"),
+ G_CALLBACK (bender_border_callback),
+ &cd->outline, cd->outline,
+
+ C_("curve-border", "_Upper"), OUTLINE_UPPER, &upper,
+ C_("curve-border", "_Lower"), OUTLINE_LOWER, &lower,
+
+ NULL);
+
+ g_object_set_data (G_OBJECT (upper), "cd", cd);
+ g_object_set_data (G_OBJECT (lower), "cd", cd);
+
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Curve Type"),
+ G_CALLBACK (bender_type_callback),
+ &cd->curve_type, cd->curve_type,
+
+ _("Smoot_h"), SMOOTH, &smooth,
+ _("_Free"), GFREE, &freew,
+
+ NULL);
+ g_object_set_data (G_OBJECT (smooth), "cd", cd);
+ g_object_set_data (G_OBJECT (freew), "cd", cd);
+
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ /* hbox for curve options */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* The Copy button */
+ button = gtk_button_new_with_mnemonic (_("_Copy"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Copy the active curve to the other border"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_copy_callback),
+ cd);
+
+ /* The CopyInv button */
+ button = gtk_button_new_with_mnemonic (_("_Mirror"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Mirror the active curve to the other border"),
+ NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_copy_inv_callback),
+ cd);
+
+ /* The Swap button */
+ button = gtk_button_new_with_mnemonic (_("S_wap"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Swap the two curves"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_swap_callback),
+ cd);
+
+ /* The Reset button */
+ button = gtk_button_new_with_mnemonic (_("_Reset"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Reset the active curve"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_reset_callback),
+ cd);
+
+ /* hbox for curve load and save */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* The Load button */
+ button = gtk_button_new_with_mnemonic (_("_Open"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Load the curves from a file"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_load_callback),
+ cd);
+
+ /* The Save button */
+ button = gtk_button_new_with_mnemonic (_("_Save"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Save the curves to a file"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_save_callback),
+ cd);
+
+ gtk_widget_show (main_hbox);
+
+ return cd;
+}
+
+static void
+bender_update (BenderDialog *cd,
+ int update)
+{
+ GtkStyle *graph_style = gtk_widget_get_style (cd->graph);
+ gint i;
+ gint other;
+
+ if (update & UP_PREVIEW)
+ {
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (cd->shell)),
+ cd->cursor_busy);
+ gdk_flush ();
+
+ if (cd->preview_layer_id2 >= 0)
+ gimp_image_remove_layer(cd->preview_image_id, cd->preview_layer_id2);
+
+ cd->preview_layer_id2 = p_main_bend(cd, cd->preview_layer_id1, TRUE /* work_on_copy*/ );
+ p_render_preview(cd, cd->preview_layer_id2);
+
+ if (update & UP_DRAW)
+ gtk_widget_queue_draw (cd->pv_widget);
+
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (cd->shell)),
+ NULL);
+ }
+ if (update & UP_PREVIEW_EXPOSE)
+ {
+ /* on expose just redraw cd->preview_layer_id2
+ * that holds the bent version of the preview (if there is one)
+ */
+ if (cd->preview_layer_id2 < 0)
+ cd->preview_layer_id2 = p_main_bend(cd, cd->preview_layer_id1, TRUE /* work_on_copy*/ );
+ p_render_preview(cd, cd->preview_layer_id2);
+
+ if (update & UP_DRAW)
+ gtk_widget_queue_draw (cd->pv_widget);
+ }
+ if ((update & UP_GRAPH) && (update & UP_DRAW) && cd->pixmap != NULL)
+ {
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (gtk_widget_get_window (cd->graph));
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_translate (cr, 0.5, 0.5);
+
+ /* Clear the background */
+ gdk_cairo_set_source_color (cr, &graph_style->bg[GTK_STATE_NORMAL]);
+ cairo_paint (cr);
+
+ /* Draw the grid lines */
+ for (i = 0; i < 5; i++)
+ {
+ cairo_move_to (cr, RADIUS, i * (GRAPH_HEIGHT / 4) + RADIUS);
+ cairo_line_to (cr, GRAPH_WIDTH + RADIUS, i * (GRAPH_HEIGHT / 4) + RADIUS);
+
+ cairo_move_to (cr, i * (GRAPH_WIDTH / 4) + RADIUS, RADIUS);
+ cairo_line_to (cr, i * (GRAPH_WIDTH / 4) + RADIUS, GRAPH_HEIGHT + RADIUS);
+ }
+
+ gdk_cairo_set_source_color (cr, &graph_style->dark[GTK_STATE_NORMAL]);
+ cairo_stroke (cr);
+
+ /* Draw the other curve */
+ other = (cd->outline == 0) ? 1 : 0;
+
+ cairo_move_to (cr, RADIUS, 255 - cd->curve[other][0] + RADIUS);
+
+ for (i = 1; i < 256; i++)
+ {
+ cairo_line_to (cr, i + RADIUS, 255 - cd->curve[other][i] + RADIUS);
+ }
+
+ gdk_cairo_set_source_color (cr, &graph_style->dark[GTK_STATE_NORMAL]);
+ cairo_stroke (cr);
+
+ /* Draw the active curve */
+ cairo_move_to (cr, RADIUS, 255 - cd->curve[cd->outline][0] + RADIUS);
+
+ for (i = 1; i < 256; i++)
+ {
+ cairo_line_to (cr, i + RADIUS, 255 - cd->curve[cd->outline][i] + RADIUS);
+ }
+
+ /* Draw the points */
+ if (cd->curve_type == SMOOTH)
+ {
+ for (i = 0; i < 17; i++)
+ {
+ if (cd->points[cd->outline][i][0] != -1)
+ {
+ cairo_new_sub_path (cr);
+ cairo_arc (cr,
+ (cd->points[cd->outline][i][0] * 255.0) + RADIUS,
+ 255 - (cd->points[cd->outline][i][1] * 255.0) + RADIUS,
+ RADIUS,
+ 0, 2 * G_PI);
+ }
+ }
+ }
+
+ gdk_cairo_set_source_color (cr, &graph_style->black);
+ cairo_stroke (cr);
+
+ cairo_destroy (cr);
+ }
+}
+
+static void
+bender_plot_curve (BenderDialog *cd,
+ int p1,
+ int p2,
+ int p3,
+ int p4,
+ gint32 xmax,
+ gint32 ymax,
+ gint fix255)
+{
+ CRMatrix geometry;
+ CRMatrix tmp1, tmp2;
+ CRMatrix deltas;
+ double x, dx, dx2, dx3;
+ double y, dy, dy2, dy3;
+ double d, d2, d3;
+ int lastx, lasty;
+ gint32 newx, newy;
+ gint32 ntimes;
+ gint32 i;
+
+ /* construct the geometry matrix from the segment */
+ for (i = 0; i < 4; i++)
+ {
+ geometry[i][2] = 0;
+ geometry[i][3] = 0;
+ }
+
+ geometry[0][0] = (cd->points[cd->outline][p1][0] * xmax);
+ geometry[1][0] = (cd->points[cd->outline][p2][0] * xmax);
+ geometry[2][0] = (cd->points[cd->outline][p3][0] * xmax);
+ geometry[3][0] = (cd->points[cd->outline][p4][0] * xmax);
+
+ geometry[0][1] = (cd->points[cd->outline][p1][1] * ymax);
+ geometry[1][1] = (cd->points[cd->outline][p2][1] * ymax);
+ geometry[2][1] = (cd->points[cd->outline][p3][1] * ymax);
+ geometry[3][1] = (cd->points[cd->outline][p4][1] * ymax);
+
+ /* subdivide the curve ntimes (1000) times */
+ ntimes = 4 * xmax;
+ /* ntimes can be adjusted to give a finer or coarser curve */
+ d = 1.0 / ntimes;
+ d2 = d * d;
+ d3 = d * d * d;
+
+ /* construct a temporary matrix for determining the forward differencing deltas */
+ tmp2[0][0] = 0; tmp2[0][1] = 0; tmp2[0][2] = 0; tmp2[0][3] = 1;
+ tmp2[1][0] = d3; tmp2[1][1] = d2; tmp2[1][2] = d; tmp2[1][3] = 0;
+ tmp2[2][0] = 6*d3; tmp2[2][1] = 2*d2; tmp2[2][2] = 0; tmp2[2][3] = 0;
+ tmp2[3][0] = 6*d3; tmp2[3][1] = 0; tmp2[3][2] = 0; tmp2[3][3] = 0;
+
+ /* compose the basis and geometry matrices */
+ bender_CR_compose (CR_basis, geometry, tmp1);
+
+ /* compose the above results to get the deltas matrix */
+ bender_CR_compose (tmp2, tmp1, deltas);
+
+ /* extract the x deltas */
+ x = deltas[0][0];
+ dx = deltas[1][0];
+ dx2 = deltas[2][0];
+ dx3 = deltas[3][0];
+
+ /* extract the y deltas */
+ y = deltas[0][1];
+ dy = deltas[1][1];
+ dy2 = deltas[2][1];
+ dy3 = deltas[3][1];
+
+ lastx = CLAMP (x, 0, xmax);
+ lasty = CLAMP (y, 0, ymax);
+
+
+ if (fix255)
+ {
+ cd->curve[cd->outline][lastx] = lasty;
+ }
+ else
+ {
+ cd->curve_ptr[cd->outline][lastx] = lasty;
+ if(gb_debug) g_printf("bender_plot_curve xmax:%d ymax:%d\n",
+ (int)xmax, (int)ymax);
+ }
+
+ /* loop over the curve */
+ for (i = 0; i < ntimes; i++)
+ {
+ /* increment the x values */
+ x += dx;
+ dx += dx2;
+ dx2 += dx3;
+
+ /* increment the y values */
+ y += dy;
+ dy += dy2;
+ dy2 += dy3;
+
+ newx = CLAMP ((ROUND (x)), 0, xmax);
+ newy = CLAMP ((ROUND (y)), 0, ymax);
+
+ /* if this point is different than the last one...then draw it */
+ if ((lastx != newx) || (lasty != newy))
+ {
+ if (fix255)
+ {
+ /* use fixed array size (for the curve graph) */
+ cd->curve[cd->outline][newx] = newy;
+ }
+ else
+ {
+ /* use dynamic allocated curve_ptr (for the real curve) */
+ cd->curve_ptr[cd->outline][newx] = newy;
+
+ if(gb_debug) g_printf("outline: %d cX: %d cY: %d\n",
+ (int)cd->outline, (int)newx, (int)newy);
+ }
+ }
+
+ lastx = newx;
+ lasty = newy;
+ }
+}
+
+static void
+bender_calculate_curve (BenderDialog *cd,
+ gint32 xmax,
+ gint32 ymax,
+ gint fix255)
+{
+ int i;
+ int points[17];
+ int num_pts;
+ int p1, p2, p3, p4;
+ int xmid;
+ int yfirst, ylast;
+
+ switch (cd->curve_type)
+ {
+ case GFREE:
+ break;
+
+ case SMOOTH:
+ /* cycle through the curves */
+ num_pts = 0;
+ for (i = 0; i < 17; i++)
+ if (cd->points[cd->outline][i][0] != -1)
+ points[num_pts++] = i;
+
+ xmid = xmax / 2;
+ /* Initialize boundary curve points */
+ if (num_pts != 0)
+ {
+ if (fix255)
+ {
+ for (i = 0; i < (cd->points[cd->outline][points[0]][0] * 255); i++)
+ cd->curve[cd->outline][i] =
+ (cd->points[cd->outline][points[0]][1] * 255);
+
+ for (i = (cd->points[cd->outline][points[num_pts - 1]][0] * 255); i < 256; i++)
+ cd->curve[cd->outline][i] =
+ (cd->points[cd->outline][points[num_pts - 1]][1] * 255);
+ }
+ else
+ {
+ yfirst = cd->points[cd->outline][points[0]][1] * ymax;
+ ylast = cd->points[cd->outline][points[num_pts - 1]][1] * ymax;
+
+ for (i = 0; i < xmid; i++)
+ {
+ cd->curve_ptr[cd->outline][i] = yfirst;
+ }
+
+ for (i = xmid; i <= xmax; i++)
+ {
+ cd->curve_ptr[cd->outline][i] = ylast;
+ }
+ }
+ }
+
+ for (i = 0; i < num_pts - 1; i++)
+ {
+ p1 = (i == 0) ? points[i] : points[(i - 1)];
+ p2 = points[i];
+ p3 = points[(i + 1)];
+ p4 = (i == (num_pts - 2)) ? points[(num_pts - 1)] : points[(i + 2)];
+
+ bender_plot_curve (cd, p1, p2, p3, p4, xmax, ymax, fix255);
+ }
+ break;
+ }
+}
+
+static void
+bender_rotate_adj_callback (GtkAdjustment *adjustment,
+ gpointer client_data)
+{
+ BenderDialog *cd = client_data;
+
+ if (gtk_adjustment_get_value (adjustment) != cd->rotation)
+ {
+ cd->rotation = gtk_adjustment_get_value (adjustment);
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+ }
+}
+
+static void
+bender_border_callback (GtkWidget *widget,
+ gpointer data)
+{
+ BenderDialog *cd;
+
+ gimp_radio_button_update (widget, data);
+ cd = g_object_get_data (G_OBJECT (widget), "cd");
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+}
+
+static void
+bender_type_callback (GtkWidget *widget,
+ gpointer data)
+{
+ BenderDialog *cd;
+
+ gimp_radio_button_update (widget, data);
+
+ cd = g_object_get_data (G_OBJECT (widget), "cd");
+ if (! cd)
+ return;
+
+ if (cd->curve_type == SMOOTH)
+ {
+ gint i;
+
+ /* pick representative points from the curve and make them control points */
+ for (i = 0; i <= 8; i++)
+ {
+ gint index = CLAMP ((i * 32), 0, 255);
+ cd->points[cd->outline][i * 2][0] = (gdouble)index / 255.0;
+ cd->points[cd->outline][i * 2][1] = (gdouble)cd->curve[cd->outline][index] / 255.0;
+ }
+
+ bender_calculate_curve (cd, 255, 255, TRUE);
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+ }
+ else
+ {
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ }
+}
+
+static void
+bender_reset_callback (GtkWidget *widget,
+ gpointer client_data)
+{
+ BenderDialog *cd = (BenderDialog *) client_data;
+ gint i;
+
+ /* Initialize the values */
+ for (i = 0; i < 256; i++)
+ cd->curve[cd->outline][i] = MIDDLE;
+
+ cd->grab_point = -1;
+ for (i = 0; i < 17; i++)
+ {
+ cd->points[cd->outline][i][0] = -1;
+ cd->points[cd->outline][i][1] = -1;
+ }
+ cd->points[cd->outline][0][0] = 0.0; /* x */
+ cd->points[cd->outline][0][1] = 0.5; /* y */
+ cd->points[cd->outline][16][0] = 1.0; /* x */
+ cd->points[cd->outline][16][1] = 0.5; /* y */
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+bender_copy_callback (GtkWidget *widget,
+ gpointer client_data)
+{
+ BenderDialog *cd = (BenderDialog *) client_data;
+ int i;
+ int other;
+
+ other = (cd->outline) ? 0 : 1;
+
+ for (i = 0; i < 17; i++)
+ {
+ cd->points[other][i][0] = cd->points[cd->outline][i][0];
+ cd->points[other][i][1] = cd->points[cd->outline][i][1];
+ }
+
+ for (i= 0; i < 256; i++)
+ {
+ cd->curve[other][i] = cd->curve[cd->outline][i];
+ }
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+bender_copy_inv_callback (GtkWidget *widget,
+ gpointer client_data)
+{
+ BenderDialog *cd = (BenderDialog*) client_data;
+ int i;
+ int other;
+
+ other = (cd->outline) ? 0 : 1;
+
+ for (i = 0; i < 17; i++)
+ {
+ cd->points[other][i][0] = cd->points[cd->outline][i][0]; /* x */
+ cd->points[other][i][1] = 1.0 - cd->points[cd->outline][i][1]; /* y */
+ }
+
+ for (i= 0; i < 256; i++)
+ {
+ cd->curve[other][i] = 255 - cd->curve[cd->outline][i];
+ }
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+
+static void
+bender_swap_callback (GtkWidget *widget,
+ gpointer client_data)
+{
+#define SWAP_VALUE(a, b, h) { h=a; a=b; b=h; }
+ BenderDialog *cd = (BenderDialog*) client_data;
+ int i;
+ int other;
+ gdouble hd;
+ guchar hu;
+
+ other = (cd->outline) ? 0 : 1;
+
+ for (i = 0; i < 17; i++)
+ {
+ SWAP_VALUE(cd->points[other][i][0], cd->points[cd->outline][i][0], hd); /* x */
+ SWAP_VALUE(cd->points[other][i][1], cd->points[cd->outline][i][1], hd); /* y */
+ }
+
+ for (i= 0; i < 256; i++)
+ {
+ SWAP_VALUE(cd->curve[other][i], cd->curve[cd->outline][i], hu);
+ }
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+bender_response (GtkWidget *widget,
+ gint response_id,
+ BenderDialog *cd)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ cd->run = TRUE;
+
+ gtk_widget_destroy (GTK_WIDGET (cd->shell));
+ gtk_main_quit ();
+}
+
+static void
+bender_preview_update (GtkWidget *widget,
+ gpointer data)
+{
+ BenderDialog *cd = (BenderDialog*) data;
+
+ cd->preview = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ if(cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+bender_preview_update_once (GtkWidget *widget,
+ gpointer data)
+{
+ BenderDialog *cd = (BenderDialog*) data;
+
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+p_points_save_to_file_response (GtkWidget *dialog,
+ gint response_id,
+ BenderDialog *cd)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ p_save_pointfile (cd, filename);
+
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+p_points_load_from_file_response (GtkWidget *dialog,
+ gint response_id,
+ BenderDialog *cd)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ p_load_pointfile (cd, filename);
+ bender_update (cd, UP_ALL);
+
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+bender_load_callback (GtkWidget *w,
+ BenderDialog *cd)
+{
+ if (! cd->filechooser)
+ {
+ cd->filechooser =
+ gtk_file_chooser_dialog_new (_("Load Curve Points from File"),
+ GTK_WINDOW (gtk_widget_get_toplevel (w)),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (cd->filechooser),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (cd->filechooser),
+ GTK_RESPONSE_OK);
+
+ g_signal_connect (cd->filechooser, "response",
+ G_CALLBACK (p_points_load_from_file_response),
+ cd);
+ g_signal_connect (cd->filechooser, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &cd->filechooser);
+ }
+
+ gtk_window_present (GTK_WINDOW (cd->filechooser));
+}
+
+static void
+bender_save_callback (GtkWidget *w,
+ BenderDialog *cd)
+{
+ if (! cd->filechooser)
+ {
+ cd->filechooser =
+ gtk_file_chooser_dialog_new (_("Save Curve Points to File"),
+ GTK_WINDOW (gtk_widget_get_toplevel (w)),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ g_signal_connect (cd->filechooser, "response",
+ G_CALLBACK (p_points_save_to_file_response),
+ cd);
+ g_signal_connect (cd->filechooser, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &cd->filechooser);
+
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (cd->filechooser),
+ "curve_bend.points");
+ }
+
+ gtk_window_present (GTK_WINDOW (cd->filechooser));
+}
+
+static void
+bender_smoothing_callback (GtkWidget *w,
+ gpointer data)
+{
+ BenderDialog *cd = (BenderDialog*) data;
+
+ cd->smoothing = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w));
+
+ if(cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+bender_antialias_callback (GtkWidget *w,
+ gpointer data)
+{
+ BenderDialog *cd = (BenderDialog*) data;
+
+ cd->antialias = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w));
+
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+bender_work_on_copy_callback (GtkWidget *w,
+ gpointer data)
+{
+ BenderDialog *cd = (BenderDialog*) data;
+
+ cd->work_on_copy = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w));
+}
+
+static gboolean
+bender_graph_events (GtkWidget *widget,
+ GdkEvent *event,
+ BenderDialog *cd)
+{
+ static GdkCursorType cursor_type = GDK_TOP_LEFT_ARROW;
+ GdkCursorType new_type;
+ GdkEventMotion *mevent;
+ int i;
+ int tx, ty;
+ int x, y;
+ int closest_point;
+ int distance;
+ int x1, x2, y1, y2;
+
+ new_type = GDK_X_CURSOR;
+ closest_point = 0;
+
+ /* get the pointer position */
+ gdk_window_get_pointer (gtk_widget_get_window (cd->graph), &tx, &ty, NULL);
+ x = CLAMP ((tx - RADIUS), 0, 255);
+ y = CLAMP ((ty - RADIUS), 0, 255);
+
+ distance = G_MAXINT;
+ for (i = 0; i < 17; i++)
+ {
+ if (cd->points[cd->outline][i][0] != -1)
+ if (abs ((int) (x - (cd->points[cd->outline][i][0] * 255.0))) < distance)
+ {
+ distance = abs ((int) (x - (cd->points[cd->outline][i][0] * 255.0)));
+ closest_point = i;
+ }
+ }
+ if (distance > MIN_DISTANCE)
+ closest_point = (x + 8) / 16;
+
+ switch (event->type)
+ {
+ case GDK_EXPOSE:
+ if (cd->pixmap == NULL)
+ cd->pixmap = gdk_pixmap_new (gtk_widget_get_window (cd->graph),
+ GRAPH_WIDTH + RADIUS * 2,
+ GRAPH_HEIGHT + RADIUS * 2, -1);
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ break;
+
+ case GDK_BUTTON_PRESS:
+ new_type = GDK_TCROSS;
+
+ switch (cd->curve_type)
+ {
+ case SMOOTH:
+ /* determine the leftmost and rightmost points */
+ cd->leftmost = -1;
+ for (i = closest_point - 1; i >= 0; i--)
+ if (cd->points[cd->outline][i][0] != -1)
+ {
+ cd->leftmost = (cd->points[cd->outline][i][0] * 255.0);
+ break;
+ }
+ cd->rightmost = 256;
+ for (i = closest_point + 1; i < 17; i++)
+ if (cd->points[cd->outline][i][0] != -1)
+ {
+ cd->rightmost = (cd->points[cd->outline][i][0] * 255.0);
+ break;
+ }
+
+ cd->grab_point = closest_point;
+ cd->points[cd->outline][cd->grab_point][0] = (gdouble)x / 255.0;
+ cd->points[cd->outline][cd->grab_point][1] = (gdouble)(255 - y) / 255.0;
+
+ bender_calculate_curve (cd, 255, 255, TRUE);
+ break;
+
+ case GFREE:
+ cd->curve[cd->outline][x] = 255 - y;
+ cd->grab_point = x;
+ cd->last = y;
+ break;
+ }
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ new_type = GDK_FLEUR;
+ cd->grab_point = -1;
+
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ mevent = (GdkEventMotion *) event;
+
+ if (mevent->is_hint)
+ {
+ mevent->x = tx;
+ mevent->y = ty;
+ }
+
+ switch (cd->curve_type)
+ {
+ case SMOOTH:
+ /* If no point is grabbed... */
+ if (cd->grab_point == -1)
+ {
+ if (cd->points[cd->outline][closest_point][0] != -1)
+ new_type = GDK_FLEUR;
+ else
+ new_type = GDK_TCROSS;
+ }
+ /* Else, drag the grabbed point */
+ else
+ {
+ new_type = GDK_TCROSS;
+
+ cd->points[cd->outline][cd->grab_point][0] = -1;
+
+ if (x > cd->leftmost && x < cd->rightmost)
+ {
+ closest_point = (x + 8) / 16;
+ if (cd->points[cd->outline][closest_point][0] == -1)
+ cd->grab_point = closest_point;
+ cd->points[cd->outline][cd->grab_point][0] = (gdouble)x / 255.0;
+ cd->points[cd->outline][cd->grab_point][1] = (gdouble)(255 - y) / 255.0;
+ }
+
+ bender_calculate_curve (cd, 255, 255, TRUE);
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ }
+ break;
+
+ case GFREE:
+ if (cd->grab_point != -1)
+ {
+ if (cd->grab_point > x)
+ {
+ x1 = x;
+ x2 = cd->grab_point;
+ y1 = y;
+ y2 = cd->last;
+ }
+ else
+ {
+ x1 = cd->grab_point;
+ x2 = x;
+ y1 = cd->last;
+ y2 = y;
+ }
+
+ if (x2 != x1)
+ for (i = x1; i <= x2; i++)
+ cd->curve[cd->outline][i] = 255 - (y1 + ((y2 - y1) * (i - x1)) / (x2 - x1));
+ else
+ cd->curve[cd->outline][x] = 255 - y;
+
+ cd->grab_point = x;
+ cd->last = y;
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ }
+
+ if (mevent->state & GDK_BUTTON1_MASK)
+ new_type = GDK_TCROSS;
+ else
+ new_type = GDK_PENCIL;
+ break;
+ }
+
+ if (new_type != cursor_type)
+ {
+ cursor_type = new_type;
+ /* change_win_cursor (gtk_widget_get_window (cd->graph), cursor_type); */
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+bender_CR_compose (CRMatrix a,
+ CRMatrix b,
+ CRMatrix ab)
+{
+ gint i, j;
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ ab[i][j] = (a[i][0] * b[0][j] +
+ a[i][1] * b[1][j] +
+ a[i][2] * b[2][j] +
+ a[i][3] * b[3][j]);
+ }
+ }
+}
+
+static void
+p_render_preview (BenderDialog *cd,
+ gint32 layer_id)
+{
+ guchar pixel[4];
+ guchar *buf, *ptr;
+ gint x, y;
+ gint ofx, ofy;
+ gint width, height;
+ t_GDRW l_gdrw;
+ t_GDRW *gdrw;
+
+ width = gimp_drawable_width (layer_id);
+ height = gimp_drawable_height (layer_id);
+
+ ptr = buf = g_new (guchar, PREVIEW_BPP * PREVIEW_SIZE_X * PREVIEW_SIZE_Y);
+ gdrw = &l_gdrw;
+ p_init_gdrw(gdrw, layer_id, FALSE);
+
+ /* offsets to set bend layer to preview center */
+ ofx = (width / 2) - (PREVIEW_SIZE_X / 2);
+ ofy = (height / 2) - (PREVIEW_SIZE_Y / 2);
+
+ /* render preview */
+ for (y = 0; y < PREVIEW_SIZE_Y; y++)
+ {
+ for (x = 0; x < PREVIEW_SIZE_X; x++)
+ {
+ p_get_pixel (gdrw, x + ofx, y + ofy, &pixel[0]);
+
+ if (cd->color)
+ {
+ ptr[0] = pixel[0];
+ ptr[1] = pixel[1];
+ ptr[2] = pixel[2];
+ }
+ else
+ {
+ ptr[0] = pixel[0];
+ ptr[1] = pixel[0];
+ ptr[2] = pixel[0];
+ }
+
+ ptr[3] = pixel[gdrw->index_alpha];
+
+ ptr += PREVIEW_BPP;
+ }
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (cd->pv_widget),
+ 0, 0, PREVIEW_SIZE_X, PREVIEW_SIZE_Y,
+ GIMP_RGBA_IMAGE,
+ buf,
+ PREVIEW_BPP * PREVIEW_SIZE_X);
+ g_free (buf);
+
+ p_end_gdrw(gdrw);
+}
+
+/* ===================================================== */
+/* curve_bend worker procedures */
+/* ===================================================== */
+
+static void
+p_stretch_curves (BenderDialog *cd,
+ gint32 xmax,
+ gint32 ymax)
+{
+ gint32 x1, x2;
+ gdouble ya, yb;
+ gdouble rest;
+ int outline;
+
+ for (outline = 0; outline < 2; outline++)
+ {
+ for(x1 = 0; x1 <= xmax; x1++)
+ {
+ x2 = (x1 * 255) / xmax;
+
+ if ((xmax <= 255) && (x2 < 255))
+ {
+ cd->curve_ptr[outline][x1] =
+ ROUND ((cd->curve[outline][x2] * ymax) / 255);
+ }
+ else
+ {
+ /* interpolate */
+ rest = (((gdouble)x1 * 255.0) / (gdouble)xmax) - x2;
+ ya = cd->curve[outline][x2]; /* y of this point */
+ yb = cd->curve[outline][x2 +1]; /* y of next point */
+
+ cd->curve_ptr[outline][x1] =
+ ROUND (((ya + ((yb -ya) * rest)) * ymax) / 255);
+ }
+ }
+ }
+}
+
+static void
+bender_init_min_max (BenderDialog *cd,
+ gint32 xmax)
+{
+ int i, j;
+
+ for (i = 0; i < 2; i++)
+ {
+ cd->min2[i] = 65000;
+ cd->max2[i] = 0;
+
+ for (j = 0; j <= xmax; j++)
+ {
+ if(cd->curve_ptr[i][j] > cd->max2[i])
+ {
+ cd->max2[i] = cd->curve_ptr[i][j];
+ }
+
+ if(cd->curve_ptr[i][j] < cd->min2[i])
+ {
+ cd->min2[i] = cd->curve_ptr[i][j];
+ }
+ }
+ }
+
+ /* for UPPER outline : y-zero line is assumed at the min leftmost or
+ * rightmost point
+ */
+ cd->zero2[OUTLINE_UPPER] = MIN (cd->curve_ptr[OUTLINE_UPPER][0],
+ cd->curve_ptr[OUTLINE_UPPER][xmax]);
+
+ /* for LOWER outline : y-zero line is assumed at the min leftmost or
+ * rightmost point
+ */
+ cd->zero2[OUTLINE_LOWER] = MAX (cd->curve_ptr[OUTLINE_LOWER][0],
+ cd->curve_ptr[OUTLINE_LOWER][xmax]);
+}
+
+static gint32
+p_curve_get_dy (BenderDialog *cd,
+ gint32 x,
+ gint32 drawable_width,
+ gint32 total_steps,
+ gdouble current_step)
+{
+ /* get y values of both upper and lower curve,
+ * and return the iterated value in between
+ */
+ gdouble y1, y2;
+ gdouble delta;
+
+ y1 = cd->zero2[OUTLINE_UPPER] - cd->curve_ptr[OUTLINE_UPPER][x];
+ y2 = cd->zero2[OUTLINE_LOWER] - cd->curve_ptr[OUTLINE_LOWER][x];
+
+ delta = ((double)(y2 - y1) / (double)(total_steps - 1)) * current_step;
+
+ return SIGNED_ROUND (y1 + delta);
+}
+
+static gint32
+p_upper_curve_extend (BenderDialog *cd,
+ gint32 drawable_width,
+ gint32 drawable_height)
+{
+ gint32 y1, y2;
+
+ y1 = cd->max2[OUTLINE_UPPER] - cd->zero2[OUTLINE_UPPER];
+ y2 = (cd->max2[OUTLINE_LOWER] - cd->zero2[OUTLINE_LOWER]) - drawable_height;
+
+ return MAX (y1, y2);
+}
+
+static gint32
+p_lower_curve_extend (BenderDialog *cd,
+ gint32 drawable_width,
+ gint32 drawable_height)
+{
+ gint32 y1, y2;
+
+ y1 = cd->zero2[OUTLINE_LOWER] - cd->min2[OUTLINE_LOWER];
+ y2 = (cd->zero2[OUTLINE_UPPER] - cd->min2[OUTLINE_UPPER]) - drawable_height;
+
+ return MAX (y1, y2);
+}
+
+static void
+p_end_gdrw (t_GDRW *gdrw)
+{
+ g_object_unref (gdrw->buffer);
+}
+
+static void
+p_init_gdrw (t_GDRW *gdrw,
+ gint32 drawable_id,
+ int shadow)
+{
+ gint w, h;
+
+ gdrw->drawable_id = drawable_id;
+
+ if (shadow)
+ gdrw->buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+ else
+ gdrw->buffer = gimp_drawable_get_buffer (drawable_id);
+
+ gdrw->width = gimp_drawable_width (gdrw->drawable_id);
+ gdrw->height = gimp_drawable_height (gdrw->drawable_id);
+
+ gdrw->tile_width = gimp_tile_width ();
+ gdrw->tile_height = gimp_tile_height ();
+
+ if (! gimp_drawable_mask_intersect (gdrw->drawable_id,
+ &gdrw->x1, &gdrw->y1, &w, &h))
+ {
+ w = 0;
+ h = 0;
+ }
+
+ gdrw->x2 = gdrw->x1 + w;
+ gdrw->y2 = gdrw->y1 + h;
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ gdrw->format = babl_format ("R'G'B'A u8");
+ else
+ gdrw->format = babl_format ("R'G'B' u8");
+
+ gdrw->bpp = babl_format_get_bytes_per_pixel (gdrw->format);
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ {
+ /* index of the alpha channelbyte {1|3} */
+ gdrw->index_alpha = gdrw->bpp - 1;
+ }
+ else
+ {
+ gdrw->index_alpha = 0; /* there is no alpha channel */
+ }
+}
+
+/* get pixel value
+ * return light transparent black gray pixel if out of bounds
+ * (should occur in the previews only)
+ */
+static void
+p_get_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *pixel)
+{
+ pixel[1] = 255;
+ pixel[3] = 255; /* simulate full visible alpha channel */
+
+ gegl_buffer_sample (gdrw->buffer, x, y, NULL, pixel, gdrw->format,
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+}
+
+static void
+p_put_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *pixel)
+{
+ gegl_buffer_set (gdrw->buffer, GEGL_RECTANGLE (x, y, 1, 1), 0,
+ gdrw->format, pixel, GEGL_AUTO_ROWSTRIDE);
+}
+
+static void
+p_put_mix_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *color,
+ gint32 nb_curvy,
+ gint32 nb2_curvy,
+ gint32 curvy)
+{
+ guchar pixel[4];
+ guchar mixmask;
+ gint idx;
+ gint diff;
+
+ mixmask = 255 - 96;
+ diff = abs(nb_curvy - curvy);
+
+ if (diff == 0)
+ {
+ mixmask = 255 - 48;
+ diff = abs(nb2_curvy - curvy);
+
+ if (diff == 0)
+ {
+ /* last 2 neighbours were not shifted against current pixel, do not mix */
+ p_put_pixel(gdrw, x, y, color);
+ return;
+ }
+ }
+
+ /* get left neighbour pixel */
+ p_get_pixel(gdrw, x-1, y, &pixel[0]);
+
+ if (pixel[gdrw->index_alpha] < 10)
+ {
+ /* neighbour is (nearly or full) transparent, do not mix */
+ p_put_pixel(gdrw, x, y, color);
+ return;
+ }
+
+ for (idx = 0; idx < gdrw->index_alpha ; idx++)
+ {
+ /* mix in left neighbour color */
+ pixel[idx] = MIX_CHANNEL(color[idx], pixel[idx], mixmask);
+ }
+
+ pixel[gdrw->index_alpha] = color[gdrw->index_alpha];
+ p_put_pixel(gdrw, x, y, &pixel[0]);
+}
+
+/* ============================================================================
+ * p_create_pv_image
+ * ============================================================================
+ */
+static gint32
+p_create_pv_image (gint32 src_drawable_id,
+ gint32 *layer_id)
+{
+ gint32 new_image_id;
+ guint new_width;
+ guint new_height;
+ GimpImageType type;
+ guint x, y;
+ double scale;
+ guchar pixel[4];
+ t_GDRW src_gdrw;
+ t_GDRW dst_gdrw;
+ gint src_width;
+ gint src_height;
+
+ src_width = gimp_drawable_width (src_drawable_id);
+ src_height = gimp_drawable_height (src_drawable_id);
+
+ new_image_id = gimp_image_new (PREVIEW_SIZE_X, PREVIEW_SIZE_Y,
+ gimp_image_base_type (gimp_item_get_image (src_drawable_id)));
+ gimp_image_undo_disable (new_image_id);
+
+ type = gimp_drawable_type (src_drawable_id);
+ if (src_height > src_width)
+ {
+ new_height = PV_IMG_HEIGHT;
+ new_width = (src_width * new_height) / src_height;
+ scale = (float) src_height / PV_IMG_HEIGHT;
+ }
+ else
+ {
+ new_width = PV_IMG_WIDTH;
+ new_height = (src_height * new_width) / src_width;
+ scale = (float) src_width / PV_IMG_WIDTH;
+ }
+
+ *layer_id = gimp_layer_new(new_image_id, "preview_original",
+ new_width, new_height,
+ type,
+ 100.0, /* opacity */
+ 0); /* mode NORMAL */
+ if (! gimp_drawable_has_alpha(*layer_id))
+ {
+ /* always add alpha channel */
+ gimp_layer_add_alpha(*layer_id);
+ }
+
+ gimp_image_insert_layer(new_image_id, *layer_id, -1, 0);
+
+ p_init_gdrw (&src_gdrw, src_drawable_id, FALSE);
+ p_init_gdrw (&dst_gdrw, *layer_id, FALSE);
+
+ for (y = 0; y < new_height; y++)
+ {
+ for (x = 0; x < new_width; x++)
+ {
+ p_get_pixel(&src_gdrw, x * scale, y * scale, &pixel[0]);
+ p_put_pixel(&dst_gdrw, x, y, &pixel[0]);
+ }
+ }
+
+ p_end_gdrw (&src_gdrw);
+ p_end_gdrw (&dst_gdrw);
+
+ return new_image_id;
+}
+
+/* ============================================================================
+ * p_add_layer
+ * ============================================================================
+ */
+static gint32
+p_add_layer (gint width,
+ gint height,
+ gint32 src_drawable_id)
+{
+ GimpImageType type;
+ gint32 new_layer_id;
+ char *name;
+ char *name2;
+ gdouble opacity;
+ GimpLayerMode mode;
+ gint visible;
+ gint32 image_id;
+ gint stack_position;
+
+ image_id = gimp_item_get_image (src_drawable_id);
+ stack_position = 0; /* TODO: should be same as src_layer */
+
+ /* copy type, name, opacity and mode from src_drawable */
+ type = gimp_drawable_type (src_drawable_id);
+ visible = gimp_item_get_visible (src_drawable_id);
+
+ name2 = gimp_item_get_name (src_drawable_id);
+ name = g_strdup_printf ("%s_b", name2);
+ g_free (name2);
+
+ mode = gimp_layer_get_mode (src_drawable_id);
+ opacity = gimp_layer_get_opacity (src_drawable_id); /* full opacity */
+
+ new_layer_id = gimp_layer_new (image_id, name,
+ width, height,
+ type,
+ opacity,
+ mode);
+
+ g_free (name);
+ if (!gimp_drawable_has_alpha (new_layer_id))
+ {
+ /* always add alpha channel */
+ gimp_layer_add_alpha (new_layer_id);
+ }
+
+ /* add the copied layer to the temp. working image */
+ gimp_image_insert_layer (image_id, new_layer_id, -1, stack_position);
+
+ /* copy visibility state */
+ gimp_item_set_visible (new_layer_id, visible);
+
+ return new_layer_id;
+}
+
+/* ============================================================================
+ * p_bender_calculate_iter_curve
+ * ============================================================================
+ */
+
+static void
+p_bender_calculate_iter_curve (BenderDialog *cd,
+ gint32 xmax,
+ gint32 ymax)
+{
+ gint x;
+ gint outline;
+ BenderDialog *cd_from;
+ BenderDialog *cd_to;
+
+ outline = cd->outline;
+
+ if ((cd->bval_from == NULL) ||
+ (cd->bval_to == NULL) ||
+ (cd->bval_curr == NULL))
+ {
+ if(gb_debug) g_printf("p_bender_calculate_iter_curve NORMAL1\n");
+ if (cd->curve_type == SMOOTH)
+ {
+ cd->outline = OUTLINE_UPPER;
+ bender_calculate_curve (cd, xmax, ymax, FALSE);
+ cd->outline = OUTLINE_LOWER;
+ bender_calculate_curve (cd, xmax, ymax, FALSE);
+ }
+ else
+ {
+ p_stretch_curves(cd, xmax, ymax);
+ }
+ }
+ else
+ {
+ /* compose curves by iterating between FROM/TO values */
+ if(gb_debug) g_printf ("p_bender_calculate_iter_curve ITERmode 1\n");
+
+ /* init FROM curves */
+ cd_from = g_new (BenderDialog, 1);
+ p_cd_from_bval (cd_from, cd->bval_from);
+ cd_from->curve_ptr[OUTLINE_UPPER] = g_new (gint32, 1+xmax);
+ cd_from->curve_ptr[OUTLINE_LOWER] = g_new (gint32, 1+xmax);
+
+ /* init TO curves */
+ cd_to = g_new (BenderDialog, 1);
+ p_cd_from_bval (cd_to, cd->bval_to);
+ cd_to->curve_ptr[OUTLINE_UPPER] = g_new (gint32, 1+xmax);
+ cd_to->curve_ptr[OUTLINE_LOWER] = g_new (gint32, 1+xmax);
+
+ if (cd_from->curve_type == SMOOTH)
+ {
+ /* calculate FROM curves */
+ cd_from->outline = OUTLINE_UPPER;
+ bender_calculate_curve (cd_from, xmax, ymax, FALSE);
+ cd_from->outline = OUTLINE_LOWER;
+ bender_calculate_curve (cd_from, xmax, ymax, FALSE);
+ }
+ else
+ {
+ p_stretch_curves (cd_from, xmax, ymax);
+ }
+
+ if (cd_to->curve_type == SMOOTH)
+ {
+ /* calculate TO curves */
+ cd_to->outline = OUTLINE_UPPER;
+ bender_calculate_curve (cd_to, xmax, ymax, FALSE);
+ cd_to->outline = OUTLINE_LOWER;
+ bender_calculate_curve (cd_to, xmax, ymax, FALSE);
+ }
+ else
+ {
+ p_stretch_curves (cd_to, xmax, ymax);
+ }
+
+ /* MIX Y-koords of the curves according to current iteration step */
+ for (x = 0; x <= xmax; x++)
+ {
+ p_delta_gint32 (&cd->curve_ptr[OUTLINE_UPPER][x],
+ cd_from->curve_ptr[OUTLINE_UPPER][x],
+ cd_to->curve_ptr[OUTLINE_UPPER][x],
+ cd->bval_curr->total_steps,
+ cd->bval_curr->current_step);
+
+ p_delta_gint32 (&cd->curve_ptr[OUTLINE_LOWER][x],
+ cd_from->curve_ptr[OUTLINE_LOWER][x],
+ cd_to->curve_ptr[OUTLINE_LOWER][x],
+ cd->bval_curr->total_steps,
+ cd->bval_curr->current_step);
+ }
+
+ g_free (cd_from->curve_ptr[OUTLINE_UPPER]);
+ g_free (cd_from->curve_ptr[OUTLINE_LOWER]);
+
+ g_free (cd_from);
+ g_free (cd_to);
+ }
+
+ cd->outline = outline;
+}
+
+/* ============================================================================
+ * p_vertical_bend
+ * ============================================================================
+ */
+
+static void
+p_vertical_bend (BenderDialog *cd,
+ t_GDRW *src_gdrw,
+ t_GDRW *dst_gdrw)
+{
+ gint32 row, col;
+ gint32 first_row, first_col, last_row, last_col;
+ gint32 x, y;
+ gint32 x2, y2;
+ gint32 curvy, nb_curvy, nb2_curvy;
+ gint32 desty, othery;
+ gint32 miny, maxy;
+ gint32 sign, dy, diff;
+ gint32 topshift;
+ float progress_step;
+ float progress_max;
+ float progress;
+
+ t_Last *last_arr;
+ t_Last *first_arr;
+ guchar color[4];
+ guchar mixcolor[4];
+ gint alias_dir;
+ guchar mixmask;
+
+ topshift = p_upper_curve_extend (cd,
+ src_gdrw->width,
+ src_gdrw->height);
+ diff = curvy = nb_curvy = nb2_curvy= miny = maxy = 0;
+
+ /* allocate array of last values (one element foreach x coordinate) */
+ last_arr = g_new (t_Last, src_gdrw->x2);
+ first_arr = g_new (t_Last, src_gdrw->x2);
+
+ /* ------------------------------------------------
+ * foreach pixel in the SAMPLE_drawable:
+ * ------------------------------------------------
+ * the inner loops (x/y) are designed to process
+ * all pixels of one tile in the sample drawable, the outer loops (row/col) do step
+ * to the next tiles. (this was done to reduce tile swapping)
+ */
+
+ first_row = src_gdrw->y1 / src_gdrw->tile_height;
+ last_row = (src_gdrw->y2 / src_gdrw->tile_height);
+ first_col = src_gdrw->x1 / src_gdrw->tile_width;
+ last_col = (src_gdrw->x2 / src_gdrw->tile_width);
+
+ /* init progress */
+ progress_max = (1 + last_row - first_row) * (1 + last_col - first_col);
+ progress_step = 1.0 / progress_max;
+ progress = 0.0;
+ if (cd->show_progress)
+ gimp_progress_init ( _("Curve Bend"));
+
+ for (row = first_row; row <= last_row; row++)
+ {
+ for (col = first_col; col <= last_col; col++)
+ {
+ if (col == first_col)
+ x = src_gdrw->x1;
+ else
+ x = col * src_gdrw->tile_width;
+ if (col == last_col)
+ x2 = src_gdrw->x2;
+ else
+ x2 = (col +1) * src_gdrw->tile_width;
+
+ if (cd->show_progress)
+ gimp_progress_update (progress += progress_step);
+
+ for( ; x < x2; x++)
+ {
+ if (row == first_row)
+ y = src_gdrw->y1;
+ else
+ y = row * src_gdrw->tile_height;
+
+ if (row == last_row)
+ y2 = src_gdrw->y2;
+ else
+ y2 = (row +1) * src_gdrw->tile_height ;
+
+ for( ; y < y2; y++)
+ {
+ /* ---------- copy SRC_PIXEL to curve position ------ */
+
+ p_get_pixel (src_gdrw, x, y, color);
+
+ curvy = p_curve_get_dy (cd, x,
+ (gint32)src_gdrw->width,
+ (gint32)src_gdrw->height,
+ (gdouble)y);
+ desty = y + topshift + curvy;
+
+ /* ----------- SMOOTHING ------------------ */
+ if (cd->smoothing && (x > 0))
+ {
+ nb_curvy = p_curve_get_dy (cd, x -1,
+ (gint32)src_gdrw->width,
+ (gint32)src_gdrw->height,
+ (gdouble)y);
+ if ((nb_curvy == curvy) && (x > 1))
+ {
+ nb2_curvy = p_curve_get_dy (cd, x -2,
+ (gint32)src_gdrw->width,
+ (gint32)src_gdrw->height,
+ (gdouble)y);
+ }
+ else
+ {
+ nb2_curvy = nb_curvy;
+ }
+
+ p_put_mix_pixel (dst_gdrw, x, desty, color, nb_curvy, nb2_curvy, curvy);
+ }
+ else
+ {
+ p_put_pixel (dst_gdrw, x, desty, color);
+ }
+
+ /* ----------- render ANTIALIAS ------------------ */
+
+ if (cd->antialias)
+ {
+ othery = desty;
+
+ if (y == src_gdrw->y1) /* Upper outline */
+ {
+ first_arr[x].y = curvy;
+ memcpy (first_arr[x].color, color,
+ dst_gdrw->bpp);
+
+ if (x > 0)
+ {
+ memcpy (mixcolor, first_arr[x-1].color,
+ dst_gdrw->bpp);
+
+ diff = abs(first_arr[x - 1].y - curvy) +1;
+ miny = MIN(first_arr[x - 1].y, curvy) -1;
+ maxy = MAX(first_arr[x - 1].y, curvy) +1;
+
+ othery = (src_gdrw->y2 -1)
+ + topshift
+ + p_curve_get_dy(cd, x,
+ (gint32)src_gdrw->width,
+ (gint32)src_gdrw->height,
+ (gdouble)(src_gdrw->y2 -1));
+ }
+ }
+
+ if (y == src_gdrw->y2 - 1) /* Lower outline */
+ {
+ if (x > 0)
+ {
+ memcpy (mixcolor, last_arr[x-1].color,
+ dst_gdrw->bpp);
+
+ diff = abs (last_arr[x - 1].y - curvy) +1;
+ maxy = MAX (last_arr[x - 1].y, curvy) +1;
+ miny = MIN (last_arr[x - 1].y, curvy) -1;
+ }
+
+ othery = (src_gdrw->y1)
+ + topshift
+ + p_curve_get_dy(cd, x,
+ (gint32)src_gdrw->width,
+ (gint32)src_gdrw->height,
+ (gdouble)(src_gdrw->y1));
+ }
+
+ if(desty < othery) { alias_dir = 1; } /* fade to transp. with descending dy */
+ else if(desty > othery) { alias_dir = -1; } /* fade to transp. with ascending dy */
+ else { alias_dir = 0; } /* no antialias at curve crossing point(s) */
+
+ if (alias_dir != 0)
+ {
+ guchar alpha_lo = 20;
+
+ if (gimp_drawable_has_alpha (src_gdrw->drawable_id))
+ {
+ alpha_lo = MIN (20, mixcolor[src_gdrw->index_alpha]);
+ }
+
+ for (dy = 0; dy < diff; dy++)
+ {
+ /* iterate for fading alpha channel */
+ mixmask = 255 * ((gdouble)(dy + 1) / (gdouble) (diff+1));
+ mixcolor[dst_gdrw->index_alpha] = MIX_CHANNEL(color[dst_gdrw->index_alpha], alpha_lo, mixmask);
+
+ if(alias_dir > 0)
+ {
+ p_put_pixel (dst_gdrw, x -1, y + topshift + miny + dy, mixcolor);
+ }
+ else
+ {
+ p_put_pixel (dst_gdrw, x -1, y + topshift + (maxy - dy), mixcolor);
+ }
+
+ }
+ }
+ }
+
+ /* ------------------ FILL HOLES ------------------ */
+
+ if (y == src_gdrw->y1)
+ {
+ diff = 0;
+ sign = 1;
+ }
+ else
+ {
+ diff = last_arr[x].y - curvy;
+ if (diff < 0)
+ {
+ diff = 0 - diff;
+ sign = -1;
+ }
+ else
+ {
+ sign = 1;
+ }
+
+ memcpy (mixcolor, color, dst_gdrw->bpp);
+ }
+
+ for (dy = 1; dy <= diff; dy++)
+ {
+ /* y differs more than 1 pixel from last y in the
+ * destination drawable. So we have to fill the empty
+ * space between using a mixed color
+ */
+
+ if (cd->smoothing)
+ {
+ /* smoothing is on, so we are using a mixed color */
+ gulong alpha1 = last_arr[x].color[3];
+ gulong alpha2 = color[3];
+ gulong alpha;
+
+ mixmask = 255 * ((gdouble)(dy) / (gdouble)(diff+1));
+ alpha = alpha1 * mixmask + alpha2 * (255 - mixmask);
+ mixcolor[3] = alpha/255;
+ if (mixcolor[3])
+ {
+ mixcolor[0] = (alpha1 * mixmask * last_arr[x].color[0]
+ + alpha2 * (255 - mixmask) * color[0])/alpha;
+ mixcolor[1] = (alpha1 * mixmask * last_arr[x].color[1]
+ + alpha2 * (255 - mixmask) * color[1])/alpha;
+ mixcolor[2] = (alpha1 * mixmask * last_arr[x].color[2]
+ + alpha2 * (255 - mixmask) * color[2])/alpha;
+ /*mixcolor[2] = MIX_CHANNEL(last_arr[x].color[2], color[2], mixmask);*/
+ }
+ }
+ else
+ {
+ /* smoothing is off, so we are using this color or
+ the last color */
+ if (dy < diff / 2)
+ {
+ memcpy (mixcolor, color,
+ dst_gdrw->bpp);
+ }
+ else
+ {
+ memcpy (mixcolor, last_arr[x].color,
+ dst_gdrw->bpp);
+ }
+ }
+
+ if (cd->smoothing)
+ {
+ p_put_mix_pixel (dst_gdrw, x,
+ desty + (dy * sign),
+ mixcolor,
+ nb_curvy, nb2_curvy, curvy );
+ }
+ else
+ {
+ p_put_pixel (dst_gdrw, x,
+ desty + (dy * sign), mixcolor);
+ }
+ }
+
+ /* store y and color */
+ last_arr[x].y = curvy;
+ memcpy (last_arr[x].color, color, dst_gdrw->bpp);
+ }
+ }
+ }
+ }
+
+ gimp_progress_update (1.0);
+
+ g_free (last_arr);
+ g_free (first_arr);
+}
+
+/* ============================================================================
+ * p_main_bend
+ * ============================================================================
+ */
+
+static gint32
+p_main_bend (BenderDialog *cd,
+ guint32 original_drawable_id,
+ gint work_on_copy)
+{
+ t_GDRW src_gdrw;
+ t_GDRW dst_gdrw;
+ gint32 src_drawable_id;
+ gint32 dst_drawable_id;
+ gint src_width;
+ gint src_height;
+ gint32 dst_height;
+ gint32 image_id;
+ gint32 tmp_layer_id;
+ gint32 interpolation;
+ gint offset_x, offset_y;
+ gint center_x, center_y;
+ gint32 xmax, ymax;
+
+ interpolation = cd->smoothing;
+ image_id = gimp_item_get_image (original_drawable_id);
+ gimp_drawable_offsets(original_drawable_id, &offset_x, &offset_y);
+
+ center_x = offset_x + (gimp_drawable_width (original_drawable_id) / 2 );
+ center_y = offset_y + (gimp_drawable_height (original_drawable_id) / 2 );
+
+ /* always copy original_drawable to a tmp src_layer */
+ tmp_layer_id = gimp_layer_copy(original_drawable_id);
+ /* set layer invisible and dummyname and
+ * add at top of the image while working
+ * (for the case of undo GIMP must know,
+ * that the layer was part of the image)
+ */
+ gimp_image_insert_layer (image_id, tmp_layer_id, -1, 0);
+ gimp_item_set_visible (tmp_layer_id, FALSE);
+ gimp_item_set_name (tmp_layer_id, "curve_bend_dummylayer");
+
+ if(gb_debug) g_printf("p_main_bend tmp_layer_id %d\n", (int)tmp_layer_id);
+
+ if (cd->rotation != 0.0)
+ {
+ if(gb_debug) g_printf("p_main_bend rotate: %f\n", (float)cd->rotation);
+ p_gimp_rotate(image_id, tmp_layer_id, interpolation, cd->rotation);
+ }
+
+ src_drawable_id = tmp_layer_id;
+
+ src_width = gimp_drawable_width (tmp_layer_id);
+ src_height = gimp_drawable_height (tmp_layer_id);
+
+ xmax = ymax = src_width -1;
+ cd->curve_ptr[OUTLINE_UPPER] = g_new (gint32, 1+xmax);
+ cd->curve_ptr[OUTLINE_LOWER] = g_new (gint32, 1+xmax);
+
+ p_bender_calculate_iter_curve(cd, xmax, ymax);
+ bender_init_min_max(cd, xmax);
+
+ dst_height = src_height
+ + p_upper_curve_extend(cd, src_width, src_height)
+ + p_lower_curve_extend(cd, src_width, src_height);
+
+ if(gb_debug) g_printf("p_main_bend: dst_height:%d\n", dst_height);
+
+ if (work_on_copy)
+ {
+ dst_drawable_id = p_add_layer (src_width, dst_height,
+ src_drawable_id);
+ if (gb_debug) g_printf("p_main_bend: DONE add layer\n");
+ }
+ else
+ {
+ /* work on the original */
+ gimp_layer_resize (original_drawable_id,
+ src_width,
+ dst_height,
+ offset_x, offset_y);
+ if (gb_debug) g_printf("p_main_bend: DONE layer resize\n");
+ if (! gimp_drawable_has_alpha (original_drawable_id))
+ {
+ /* always add alpha channel */
+ gimp_layer_add_alpha (original_drawable_id);
+ }
+
+ dst_drawable_id = original_drawable_id;
+ }
+
+ gimp_drawable_fill (dst_drawable_id, GIMP_FILL_TRANSPARENT);
+
+ p_init_gdrw (&src_gdrw, src_drawable_id, FALSE);
+ p_init_gdrw (&dst_gdrw, dst_drawable_id, FALSE);
+
+ p_vertical_bend (cd, &src_gdrw, &dst_gdrw);
+
+ if(gb_debug) g_printf("p_main_bend: DONE vertical bend\n");
+
+ p_end_gdrw (&src_gdrw);
+ p_end_gdrw (&dst_gdrw);
+
+ if (cd->rotation != 0.0)
+ {
+ p_gimp_rotate (image_id, dst_drawable_id,
+ interpolation, (gdouble)(360.0 - cd->rotation));
+
+ /* TODO: here we should crop dst_drawable to cut off full transparent borderpixels */
+ }
+
+ /* set offsets of the resulting new layer
+ *(center == center of original_drawable)
+ */
+ offset_x = center_x - (gimp_drawable_width (dst_drawable_id) / 2 );
+ offset_y = center_y - (gimp_drawable_height (dst_drawable_id) / 2 );
+ gimp_layer_set_offsets (dst_drawable_id, offset_x, offset_y);
+
+ /* delete the temp layer */
+ gimp_image_remove_layer (image_id, tmp_layer_id);
+
+ g_free (cd->curve_ptr[OUTLINE_UPPER]);
+ g_free (cd->curve_ptr[OUTLINE_LOWER]);
+
+ if (gb_debug) g_printf("p_main_bend: DONE bend main\n");
+
+ return dst_drawable_id;
+}