diff options
Diffstat (limited to 'plug-ins/common/curve-bend.c')
-rw-r--r-- | plug-ins/common/curve-bend.c | 3402 |
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, + ¶ms, &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; +} |