/* curve_bend plugin for the GIMP */ /* The GIMP -- an 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* 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 #include #include #include #include #include #include "libgimp/stdplugins-intl.h" /* Defines */ #define PLUG_IN_NAME "plug_in_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_DESCRIPTION "Bends a layer using 2 spline-curves" #define HELP_ID "plug-in-curve-bend" #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 SIGNED_ROUND(x) ((int) (((x < 0) ? (x) - 0.5 : (x) + 0.5) )) #define MIX_CHANNEL(a, b, m) (((a * m) + (b * (255 - m))) / 255) #define UP_GRAPH 0x1 #define UP_XRANGE_TOP 0x2 #define UP_PREVIEW_EXPOSE 0x4 #define UP_PREVIEW 0x8 #define UP_DRAW 0x10 #define UP_ALL 0xFF #define ENTRY_WIDTH 50 #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 */ int 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; GimpDrawable *drawable; 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]; gint show_progress; gint smoothing; gint antialias; gint 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 { GimpDrawable *drawable; gint x1; gint y1; gint x2; gint y2; gint index_alpha; /* 0 == no alpha, 1 == GREYA, 3 == RGBA */ gint bpp; GimpPixelFetcher *pft; 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 (GimpDrawable *); 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 (GimpDrawable *); static void p_init_gdrw (t_GDRW *gdrw, GimpDrawable *drawable, int dirty, int shadow); static void p_end_gdrw (t_GDRW *gdrw); static gint32 p_main_bend (BenderDialog *, GimpDrawable *, gint); static gint32 p_create_pv_image (GimpDrawable *src_drawable, 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, gint8 *int8array); static int p_save_pointfile (BenderDialog *cd, const gchar *filename); /* Global Variables */ 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 */ /* ============================================================================ * 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 l_nparams; gint l_nreturn_vals; GimpPDBProcType l_proc_type; gchar *l_proc_blurb; gchar *l_proc_help; gchar *l_proc_author; gchar *l_proc_copyright; gchar *l_proc_date; GimpParamDef *l_params; GimpParamDef *l_return_vals; gint l_rc; l_rc = 0; /* Query the gimp application's procedural database * regarding a particular procedure. */ if (gimp_procedural_db_proc_info (proc_name, &l_proc_blurb, &l_proc_help, &l_proc_author, &l_proc_copyright, &l_proc_date, &l_proc_type, &l_nparams, &l_nreturn_vals, &l_params, &l_return_vals)) { /* procedure found in PDB */ return l_nparams; } g_printerr ("Warning: Procedure %s not found.\n", proc_name); return -1; } static gint p_gimp_rotate (gint32 image_id, gint32 drawable_id, gint32 interpolation, gdouble angle_deg) { static gchar *l_rotate_proc = "gimp_rotate"; GimpParam *return_vals; gint nreturn_vals; gdouble l_angle_rad; gint l_nparams; gint l_rc; #ifdef ROTATE_OPTIMIZE static gchar *l_rotate_proc2 = "plug_in_rotate"; gint32 l_angle_step; if (angle_deg == 90.0) { l_angle_step = 1; } else if(angle_deg == 180.0) { l_angle_step = 2; } else if(angle_deg == 270.0) { l_angle_step = 3; } else { l_angle_step = 0; } if (l_angle_step != 0) { l_nparams = p_pdb_procedure_available (l_rotate_proc2); if (l_nparams == 5) { /* use faster rotate plugin on multiples of 90 degrees */ return_vals = gimp_run_procedure (l_rotate_proc2, &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, image_id, GIMP_PDB_DRAWABLE, drawable_id, GIMP_PDB_INT32, l_angle_step, GIMP_PDB_INT32, FALSE, /* dont rotate the whole image */ GIMP_PDB_END); if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS) { return 0; } } } #endif l_rc = -1; l_angle_rad = (angle_deg * G_PI) / 180.0; l_nparams = p_pdb_procedure_available (l_rotate_proc); if (l_nparams >= 0) { /* use the new Interface (Gimp 1.1 style) * (1.1 knows the image_id where the drawable belongs to) */ return_vals = gimp_run_procedure (l_rotate_proc, &nreturn_vals, GIMP_PDB_DRAWABLE, drawable_id, GIMP_PDB_INT32, interpolation, GIMP_PDB_FLOAT, l_angle_rad, GIMP_PDB_END); if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS) { l_rc = 0; } else { g_printerr ("Error: %s call failed %d\n", l_rotate_proc, return_vals[0].data.d_status); } gimp_destroy_params (return_vals, nreturn_vals); } else { g_printerr ("Error: Procedure %s not found.\n", l_rotate_proc); } return l_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 l_sel_channel_id; gint32 l_x1, l_x2, l_y1, l_y2; gint32 non_empty; /* check and see if we have a selection mask */ l_sel_channel_id = gimp_image_get_selection (image_id); gimp_selection_bounds (image_id, &non_empty, &l_x1, &l_y1, &l_x2, &l_y2); if (non_empty && l_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 GimpParamDef args[] = { { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"}, { 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 GimpParamDef return_vals[] = { { GIMP_PDB_LAYER, "bent_layer", "the handled layer" } }; static GimpParamDef args_iter[] = { { GIMP_PDB_INT32, "run_mode", "non-interactive" }, { 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 inbetween)" }, { 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_NAME, PLUG_IN_DESCRIPTION, "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 plugin 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_NAME, "/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 *l_env; BenderDialog *cd; GimpDrawable *l_active_drawable = NULL; gint32 l_active_drawable_id = -1; gint32 l_image_id = -1; gint32 l_layer_id = -1; gint32 l_layer_mask_id = -1; gint32 l_bent_layer_id = -1; /* 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 usualy 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 (); cd = NULL; l_env = g_getenv ("BEND_DEBUG"); if (l_env != NULL) { if((*l_env != 'n') && (*l_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_NAME, &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 */ l_image_id = param[1].data.d_int32; l_layer_id = param[2].data.d_drawable; gimp_image_undo_group_start (l_image_id); if (! gimp_drawable_is_layer (l_layer_id)) { g_message (_("Can operate on layers only (but was called on channel or mask).")); status = GIMP_PDB_EXECUTION_ERROR; } /* check for layermask */ l_layer_mask_id = gimp_layer_get_mask (l_layer_id); if (l_layer_mask_id >= 0) { /* apply the layermask * some transitions (especially rotate) cant operate proper on * layers with masks ! */ if (run_mode == GIMP_RUN_NONINTERACTIVE) { gimp_layer_remove_mask (l_layer_id, 0 /* 0==APPLY */ ); } else { g_message (_("Cannot operate on layers with masks.")); status = GIMP_PDB_EXECUTION_ERROR; } } /* if there is a selection, make it the floating selection layer */ l_active_drawable_id = p_if_selection_float_it (l_image_id, l_layer_id); if (l_active_drawable_id < 0) { /* could not float the selection because selection rectangle * is completely empty return GIMP_PDB_EXECUTION_ERROR */ status = GIMP_PDB_EXECUTION_ERROR; if (run_mode != GIMP_RUN_NONINTERACTIVE) { g_message (_("Cannot operate on empty selections.")); } } else { l_active_drawable = gimp_drawable_get (l_active_drawable_id); } /* 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_NAME, &g_bndvals); */ /* Get information from the dialog */ cd = do_dialog (l_active_drawable); 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 = FALSE; cd->drawable = l_active_drawable; cd->rotation = (gdouble) param[3].data.d_float; cd->smoothing = (gint) param[4].data.d_int32; cd->antialias = (gint) param[5].data.d_int32; cd->work_on_copy = (gint) param[6].data.d_int32; cd->curve_type = (gint) param[7].data.d_int32; 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_UPPER, 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 = l_active_drawable; 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) { l_bent_layer_id = p_main_bend (cd, cd->drawable, cd->work_on_copy); /* Store variable states for next run */ if (run_mode == GIMP_RUN_INTERACTIVE) { p_store_values (cd); } } else { status = GIMP_PDB_CANCEL; } gimp_image_undo_group_end (l_image_id); if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush (); } else { gimp_image_undo_group_end (l_image_id); } values[0].data.d_status = status; values[1].data.d_int32 = l_bent_layer_id; /* return the id of handled layer */ } static int p_save_pointfile (BenderDialog *cd, const gchar *filename) { gint j; FILE *l_fp; l_fp = g_fopen(filename, "w+"); if (!l_fp) { g_message (_("Could not open '%s' for writing: %s"), gimp_filename_to_utf8 (filename), g_strerror (errno)); return -1; } fprintf(l_fp, "%s\n", KEY_POINTFILE); fprintf(l_fp, "VERSION 1.0\n\n"); fprintf(l_fp, "# points for upper and lower smooth curve (0.0 <= pt <= 1.0)\n"); fprintf(l_fp, "# there are upto 17 points where unused points are set to -1\n"); fprintf(l_fp, "# UPPERX UPPERY LOWERX LOWERY\n"); fprintf(l_fp, "\n"); for(j = 0; j < 17; j++) { fprintf(l_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(l_fp, "\n"); fprintf(l_fp, "# y values for upper/lower freehand curve (0 <= y <= 255) \n"); fprintf(l_fp, "# there must be exactly 256 y values \n"); fprintf(l_fp, "# UPPER_Y LOWER_Y\n"); fprintf(l_fp, "\n"); for (j = 0; j < 256; j++) { fprintf(l_fp, "%s %3d %3d\n", KEY_VAL_Y, (int)cd->curve[OUTLINE_UPPER][j], (int)cd->curve[OUTLINE_LOWER][j]); } fclose(l_fp); return 0; } static int p_load_pointfile (BenderDialog *cd, const gchar *filename) { gint l_pi, l_ci, l_n, l_len; FILE *l_fp; char l_buff[2000]; float l_fux, l_fuy, l_flx, l_fly; gint l_iuy, l_ily ; l_fp = g_fopen(filename, "r"); if (!l_fp) { g_message (_("Could not open '%s' for reading: %s"), gimp_filename_to_utf8 (filename), g_strerror (errno)); return -1; } l_pi = 0; l_ci = 0; fgets (l_buff, 2000 - 1, l_fp); if (strncmp(l_buff, KEY_POINTFILE, strlen(KEY_POINTFILE)) == 0) { while (NULL != fgets (l_buff, 2000-1, l_fp)) { l_len = strlen(KEY_POINTS); if(strncmp(l_buff, KEY_POINTS, l_len) == 0) { l_n = sscanf(&l_buff[l_len], "%f %f %f %f", &l_fux, &l_fuy, &l_flx, &l_fly); if((l_n == 4) && (l_pi < 17)) { cd->points[OUTLINE_UPPER][l_pi][0] = l_fux; cd->points[OUTLINE_UPPER][l_pi][1] = l_fuy; cd->points[OUTLINE_LOWER][l_pi][0] = l_flx; cd->points[OUTLINE_LOWER][l_pi][1] = l_fly; l_pi++; } else { printf("warnig: BAD points[%d] in file %s are ignored\n", l_pi, filename); } } l_len = strlen(KEY_VAL_Y); if (strncmp(l_buff, KEY_VAL_Y, l_len) == 0) { l_n = sscanf(&l_buff[l_len], "%d %d", &l_iuy, &l_ily); if ((l_n == 2) && (l_ci < 256)) { cd->curve[OUTLINE_UPPER][l_ci] = l_iuy; cd->curve[OUTLINE_LOWER][l_ci] = l_ily; l_ci++; } else { printf("warnig: BAD y_vals[%d] in file %s are ignored\n", l_ci, filename); } } } } fclose(l_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 l_bval; p_cd_to_bval(cd, &l_bval); gimp_set_data(PLUG_IN_NAME, &l_bval, sizeof(l_bval)); } static void p_retrieve_values (BenderDialog *cd) { BenderValues l_bval; l_bval.total_steps = 0; l_bval.current_step = -444.4; /* init with an invalid dummy value */ gimp_get_data (PLUG_IN_NAME, &l_bval); if (l_bval.total_steps == 0) { cd->bval_from = NULL; cd->bval_to = NULL; if(l_bval.current_step != -444.4) { /* last_value data was retrieved (and dummy value was overwritten) */ p_cd_from_bval(cd, &l_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 = l_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 = l_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; } 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]; } } void p_copy_yval (BenderDialog *cd, int outline, int argc, gint8 *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 (GimpDrawable *drawable) { BenderDialog *cd; /* Init GTK */ gimp_ui_init ("curve_bend", TRUE); /* The curve_bend dialog */ cd = bender_new_dialog (drawable); /* create temporary image (with a small copy of drawable) for the preview */ cd->preview_image_id = p_create_pv_image(drawable, &cd->preview_layer_id1); cd->preview_layer_id2 = -1; if (!GTK_WIDGET_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 (GimpDrawable *drawable) { 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; GtkObject *data; 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 = drawable; cd->color = gimp_drawable_is_rgb (cd->drawable->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"), "curve_bend", NULL, 0, gimp_standard_help_func, HELP_ID, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_dialog_set_alternative_button_order (GTK_DIALOG (cd->shell), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); 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_hbox_new (FALSE, 12); gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (cd->shell)->vbox), main_hbox); gtk_widget_show (main_hbox); /* Left side column */ vbox = gtk_vbox_new (FALSE, 12); gtk_container_add (GTK_CONTAINER (main_hbox), vbox); 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_vbox_new (FALSE, 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_hbox_new (FALSE, 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_vbox_new (FALSE, 6); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); /* Render Options */ hbox = gtk_hbox_new (FALSE, 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); spinbutton = gimp_spin_button_new (&data, 0, 0.0, 360.0, 1, 45, 90, 0.5, 1); cd->rotate_data = GTK_ADJUSTMENT (data); 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_container_add (GTK_CONTAINER (main_hbox), frame); gtk_widget_show (frame); vbox = gtk_vbox_new (FALSE, 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_hbox_new (FALSE, 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, _("_Upper"), OUTLINE_UPPER, &upper, _("_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_hbox_new (FALSE, 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_from_stock (GIMP_STOCK_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_hbox_new (FALSE, 4); gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); /* The Load button */ button = gtk_button_new_from_stock (GTK_STOCK_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_from_stock (GTK_STOCK_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) { gint i; gint other; if (update & UP_PREVIEW) { gdk_window_set_cursor (GTK_WIDGET (cd->shell)->window, 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, gimp_drawable_get (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 (cd->shell)->window, 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, gimp_drawable_get (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) { GdkPoint points[256]; /* Clear the pixmap */ gdk_draw_rectangle (cd->pixmap, cd->graph->style->bg_gc[GTK_STATE_NORMAL], TRUE, 0, 0, GRAPH_WIDTH + RADIUS * 2, GRAPH_HEIGHT + RADIUS * 2); /* Draw the grid lines */ for (i = 0; i < 5; i++) { gdk_draw_line (cd->pixmap, cd->graph->style->dark_gc[GTK_STATE_NORMAL], RADIUS, i * (GRAPH_HEIGHT / 4) + RADIUS, GRAPH_WIDTH + RADIUS, i * (GRAPH_HEIGHT / 4) + RADIUS); gdk_draw_line (cd->pixmap, cd->graph->style->dark_gc[GTK_STATE_NORMAL], i * (GRAPH_WIDTH / 4) + RADIUS, RADIUS, i * (GRAPH_WIDTH / 4) + RADIUS, GRAPH_HEIGHT + RADIUS); } /* Draw the other curve */ other = (cd->outline == 0) ? 1 : 0; for (i = 0; i < 256; i++) { points[i].x = i + RADIUS; points[i].y = 255 - cd->curve[other][i] + RADIUS; } gdk_draw_points (cd->pixmap, cd->graph->style->dark_gc[GTK_STATE_NORMAL], points, 256); /* Draw the active curve */ for (i = 0; i < 256; i++) { points[i].x = i + RADIUS; points[i].y = 255 - cd->curve[cd->outline][i] + RADIUS; } gdk_draw_points (cd->pixmap, cd->graph->style->black_gc, points, 256); /* Draw the points */ if (cd->curve_type == SMOOTH) { for (i = 0; i < 17; i++) { if (cd->points[cd->outline][i][0] != -1) gdk_draw_arc (cd->pixmap, cd->graph->style->black_gc, TRUE, (cd->points[cd->outline][i][0] * 255.0), 255 - (cd->points[cd->outline][i][1] * 255.0), RADIUS * 2, RADIUS * 2, 0, 23040); } } gdk_draw_drawable (cd->graph->window, cd->graph->style->black_gc, cd->pixmap, 0, 0, 0, 0, GRAPH_WIDTH + RADIUS * 2, GRAPH_HEIGHT + RADIUS * 2); } } 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) 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) 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 = (BenderDialog*) client_data; if (adjustment->value != cd->rotation) { cd->rotation = adjustment->value; 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->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; int i; cd = (BenderDialog *) client_data; /* 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, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_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, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_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; GdkEventButton *bevent; 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 (cd->graph->window, &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 (x - (cd->points[cd->outline][i][0] * 255.0)) < distance) { distance = abs (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 (cd->graph->window, GRAPH_WIDTH + RADIUS * 2, GRAPH_HEIGHT + RADIUS * 2, -1); bender_update (cd, UP_GRAPH | UP_DRAW); break; case GDK_BUTTON_PRESS: bevent = (GdkEventButton *) event; 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 (cd->graph->window, 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 l_pixel[4]; guchar *l_buf, *l_ptr; GimpDrawable *l_pv_drawable; gint l_x, l_y; gint l_ofx, l_ofy; t_GDRW l_gdrw; t_GDRW *gdrw; l_pv_drawable = gimp_drawable_get (layer_id); l_ptr = l_buf = g_new (guchar, PREVIEW_BPP * PREVIEW_SIZE_X * PREVIEW_SIZE_Y); gdrw = &l_gdrw; p_init_gdrw(gdrw, l_pv_drawable, FALSE, FALSE); /* offsets to set bend layer to preview center */ l_ofx = (l_pv_drawable->width / 2) - (PREVIEW_SIZE_X / 2); l_ofy = (l_pv_drawable->height / 2) - (PREVIEW_SIZE_Y / 2); /* render preview */ for (l_y = 0; l_y < PREVIEW_SIZE_Y; l_y++) { for (l_x = 0; l_x < PREVIEW_SIZE_X; l_x++) { p_get_pixel(gdrw, l_x + l_ofx, l_y + l_ofy, &l_pixel[0]); if (cd->color) { l_ptr[0] = l_pixel[0]; l_ptr[1] = l_pixel[1]; l_ptr[2] = l_pixel[2]; } else { l_ptr[0] = l_pixel[0]; l_ptr[1] = l_pixel[0]; l_ptr[2] = l_pixel[0]; } l_ptr[3] = l_pixel[gdrw->index_alpha]; l_ptr += PREVIEW_BPP; } } gimp_preview_area_draw (GIMP_PREVIEW_AREA (cd->pv_widget), 0, 0, PREVIEW_SIZE_X, PREVIEW_SIZE_Y, GIMP_RGBA_IMAGE, l_buf, PREVIEW_BPP * PREVIEW_SIZE_X); g_free (l_buf); p_end_gdrw(gdrw); } /* end p_render_preview */ /* ===================================================== */ /* curve_bend worker procedures */ /* ===================================================== */ static void p_stretch_curves (BenderDialog *cd, gint32 xmax, gint32 ymax) { gint32 l_x1, l_x2; gdouble l_ya, l_yb; gdouble l_rest; int l_outline; for(l_outline = 0; l_outline < 2; l_outline++) { for(l_x1 = 0; l_x1 <= xmax; l_x1++) { l_x2 = (l_x1 * 255) / xmax; if((xmax <= 255) && (l_x2 < 255)) { cd->curve_ptr[l_outline][l_x1] = ROUND((cd->curve[l_outline][l_x2] * ymax) / 255); } else { /* interpolate */ l_rest = (((gdouble)l_x1 * 255.0) / (gdouble)xmax) - l_x2; l_ya = cd->curve[l_outline][l_x2]; /* y of this point */ l_yb = cd->curve[l_outline][l_x2 +1]; /* y of next point */ cd->curve_ptr[l_outline][l_x1] = ROUND (((l_ya + ((l_yb -l_ya) * l_rest)) * ymax) / 255); } { int l_debugY; l_debugY = ROUND((cd->curve[l_outline][l_x2] * 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 inbetween */ gdouble l_y1, l_y2; gdouble delta; l_y1 = cd->zero2[OUTLINE_UPPER] - cd->curve_ptr[OUTLINE_UPPER][x]; l_y2 = cd->zero2[OUTLINE_LOWER] - cd->curve_ptr[OUTLINE_LOWER][x]; delta = ((double)(l_y2 - l_y1) / (double)(total_steps -1)) * current_step; return SIGNED_ROUND(l_y1 + delta); } static gint32 p_upper_curve_extend (BenderDialog *cd, gint32 drawable_width, gint32 drawable_height) { gint32 l_y1, l_y2; l_y1 = cd->max2[OUTLINE_UPPER] - cd->zero2[OUTLINE_UPPER]; l_y2 = (cd->max2[OUTLINE_LOWER] - cd->zero2[OUTLINE_LOWER]) - drawable_height; return MAX(l_y1, l_y2); } static gint32 p_lower_curve_extend (BenderDialog *cd, gint32 drawable_width, gint32 drawable_height) { gint32 l_y1, l_y2; l_y1 = cd->zero2[OUTLINE_LOWER] - cd->min2[OUTLINE_LOWER]; l_y2 = (cd->zero2[OUTLINE_UPPER] - cd->min2[OUTLINE_UPPER]) - drawable_height; return MAX(l_y1, l_y2); } static void p_end_gdrw (t_GDRW *gdrw) { gimp_pixel_fetcher_destroy (gdrw->pft); } static void p_init_gdrw (t_GDRW *gdrw, GimpDrawable *drawable, int dirty, int shadow) { gdrw->drawable = drawable; gdrw->pft = gimp_pixel_fetcher_new (drawable, FALSE); gimp_pixel_fetcher_set_edge_mode (gdrw->pft, GIMP_PIXEL_FETCHER_EDGE_BLACK); gdrw->tile_width = gimp_tile_width (); gdrw->tile_height = gimp_tile_height (); gimp_drawable_mask_bounds (drawable->drawable_id, &gdrw->x1, &gdrw->y1, &gdrw->x2, &gdrw->y2); gdrw->bpp = drawable->bpp; if (gimp_drawable_has_alpha(drawable->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 */ gimp_pixel_fetcher_get_pixel (gdrw->pft, x, y, pixel); } static void p_put_pixel (t_GDRW *gdrw, gint32 x, gint32 y, guchar *pixel) { gimp_pixel_fetcher_put_pixel (gdrw->pft, x, y, pixel); } void p_put_mix_pixel (t_GDRW *gdrw, gint32 x, gint32 y, guchar *color, gint32 nb_curvy, gint32 nb2_curvy, gint32 curvy) { guchar l_pixel[4]; guchar l_mixmask; gint l_idx; gint l_diff; l_mixmask = 255 - 96; l_diff = abs(nb_curvy - curvy); if (l_diff == 0) { l_mixmask = 255 - 48; l_diff = abs(nb2_curvy - curvy); if (l_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, &l_pixel[0]); if (l_pixel[gdrw->index_alpha] < 10) { /* neighbour is (nearly or full) transparent, do not mix */ p_put_pixel(gdrw, x, y, color); return; } for (l_idx = 0; l_idx < gdrw->index_alpha ; l_idx++) { /* mix in left neighbour color */ l_pixel[l_idx] = MIX_CHANNEL(color[l_idx], l_pixel[l_idx], l_mixmask); } l_pixel[gdrw->index_alpha] = color[gdrw->index_alpha]; p_put_pixel(gdrw, x, y, &l_pixel[0]); } /* ============================================================================ * p_clear_drawable * ============================================================================ */ static void p_clear_drawable (GimpDrawable *drawable) { GimpPixelRgn pixel_rgn; gpointer pr; guint l_row; guchar *l_ptr; gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, TRUE, /* dirty */ FALSE /* shadow */ ); /* clear the drawable with 0 Bytes (black full-transparent pixels) */ for (pr = gimp_pixel_rgns_register (1, &pixel_rgn); pr != NULL; pr = gimp_pixel_rgns_process (pr)) { l_ptr = pixel_rgn.data; for ( l_row = 0; l_row < pixel_rgn.h; l_row++ ) { memset(l_ptr, 0, pixel_rgn.w * drawable->bpp); l_ptr += pixel_rgn.rowstride; } } } /* end p_clear_drawable */ /* ============================================================================ * p_create_pv_image * ============================================================================ */ gint32 p_create_pv_image (GimpDrawable *src_drawable, gint32 *layer_id) { gint32 l_new_image_id; guint l_new_width; guint l_new_height; GimpImageType l_type; guint l_x, l_y; double l_scale; guchar l_pixel[4]; GimpDrawable *dst_drawable; t_GDRW l_src_gdrw; t_GDRW l_dst_gdrw; l_new_image_id = gimp_image_new (PREVIEW_SIZE_X, PREVIEW_SIZE_Y, gimp_image_base_type (gimp_drawable_get_image (src_drawable->drawable_id))); gimp_image_undo_disable (l_new_image_id); l_type = gimp_drawable_type(src_drawable->drawable_id); if (src_drawable->height > src_drawable->width) { l_new_height = PV_IMG_HEIGHT; l_new_width = (src_drawable->width * l_new_height) / src_drawable->height; l_scale = (float)src_drawable->height / PV_IMG_HEIGHT; } else { l_new_width = PV_IMG_WIDTH; l_new_height = (src_drawable->height * l_new_width) / src_drawable->width; l_scale = (float)src_drawable->width / PV_IMG_WIDTH; } *layer_id = gimp_layer_new(l_new_image_id, "preview_original", l_new_width, l_new_height, l_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_add_layer(l_new_image_id, *layer_id, 0); dst_drawable = gimp_drawable_get (*layer_id); p_init_gdrw(&l_src_gdrw, src_drawable, FALSE, FALSE); p_init_gdrw(&l_dst_gdrw, dst_drawable, TRUE, FALSE); for (l_y = 0; l_y < l_new_height; l_y++) { for (l_x = 0; l_x < l_new_width; l_x++) { p_get_pixel(&l_src_gdrw, l_x * l_scale, l_y * l_scale, &l_pixel[0]); p_put_pixel(&l_dst_gdrw, l_x, l_y, &l_pixel[0]); } } p_end_gdrw(&l_src_gdrw); p_end_gdrw(&l_dst_gdrw); /* gimp_display_new(l_new_image_id); */ return l_new_image_id; } /* ============================================================================ * p_add_layer * ============================================================================ */ static GimpDrawable* p_add_layer (gint width, gint height, GimpDrawable *src_drawable) { GimpImageType l_type; static GimpDrawable *l_new_drawable; gint32 l_new_layer_id; char *l_name; char *l_name2; gdouble l_opacity; GimpLayerModeEffects l_mode; gint l_visible; gint32 image_id; gint stack_position; image_id = gimp_drawable_get_image (src_drawable->drawable_id); stack_position = 0; /* TODO: should be same as src_layer */ /* copy type, name, opacity and mode from src_drawable */ l_type = gimp_drawable_type (src_drawable->drawable_id); l_visible = gimp_drawable_get_visible (src_drawable->drawable_id); l_name2 = gimp_drawable_get_name (src_drawable->drawable_id); l_name = g_strdup_printf ("%s_b", l_name2); g_free (l_name2); l_mode = gimp_layer_get_mode (src_drawable->drawable_id); l_opacity = gimp_layer_get_opacity (src_drawable->drawable_id); /* full opacity */ l_new_layer_id = gimp_layer_new (image_id, l_name, width, height, l_type, l_opacity, l_mode); g_free (l_name); if (!gimp_drawable_has_alpha (l_new_layer_id)) { /* always add alpha channel */ gimp_layer_add_alpha (l_new_layer_id); } l_new_drawable = gimp_drawable_get (l_new_layer_id); if (!l_new_drawable) { fprintf (stderr, "p_ad_layer: cant get new_drawable\n"); return NULL; } /* add the copied layer to the temp. working image */ gimp_image_add_layer (image_id, l_new_layer_id, stack_position); /* copy visiblity state */ gimp_drawable_set_visible (l_new_layer_id, l_visible); return l_new_drawable; } /* ============================================================================ * p_bender_calculate_iter_curve * ============================================================================ */ void p_bender_calculate_iter_curve (BenderDialog *cd, gint32 xmax, gint32 ymax) { int l_x; gint l_outline; BenderDialog *cd_from; BenderDialog *cd_to; l_outline = cd->outline; if ((cd->bval_from == NULL) || (cd->bval_to == NULL) || (cd->bval_curr == NULL)) { if(gb_debug) 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) 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 (l_x = 0; l_x <= xmax; l_x++) { p_delta_gint32 (&cd->curve_ptr[OUTLINE_UPPER][l_x], cd_from->curve_ptr[OUTLINE_UPPER][l_x], cd_to->curve_ptr[OUTLINE_UPPER][l_x], cd->bval_curr->total_steps, cd->bval_curr->current_step); p_delta_gint32 (&cd->curve_ptr[OUTLINE_LOWER][l_x], cd_from->curve_ptr[OUTLINE_LOWER][l_x], cd_to->curve_ptr[OUTLINE_LOWER][l_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 = l_outline; } /* ============================================================================ * p_vertical_bend * ============================================================================ */ static void p_vertical_bend (BenderDialog *cd, t_GDRW *src_gdrw, t_GDRW *dst_gdrw) { gint32 l_row, l_col; gint32 l_first_row, l_first_col, l_last_row, l_last_col; gint32 l_x, l_y; gint32 l_x2, l_y2; gint32 l_curvy, l_nb_curvy, l_nb2_curvy; gint32 l_desty, l_othery; gint32 l_miny, l_maxy; gint32 l_sign, l_dy, l_diff; gint32 l_topshift; float l_progress_step; float l_progress_max; float l_progress; t_Last *last_arr; t_Last *first_arr; guchar color[4]; guchar mixcolor[4]; guchar l_alpha_lo; gint l_alias_dir; guchar l_mixmask; l_topshift = p_upper_curve_extend (cd, src_gdrw->drawable->width, src_gdrw->drawable->height); l_diff = l_curvy = l_nb_curvy = l_nb2_curvy= l_miny = l_maxy = 0; l_alpha_lo = 20; /* allocate array of last values (one element foreach x koordinate) */ 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 (l_x/l_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) */ l_first_row = src_gdrw->y1 / src_gdrw->tile_height; l_last_row = (src_gdrw->y2 / src_gdrw->tile_height); l_first_col = src_gdrw->x1 / src_gdrw->tile_width; l_last_col = (src_gdrw->x2 / src_gdrw->tile_width); /* init progress */ l_progress_max = (1 + l_last_row - l_first_row) * (1 + l_last_col - l_first_col); l_progress_step = 1.0 / l_progress_max; l_progress = 0.0; if (cd->show_progress) gimp_progress_init ( _("Curve Bend...")); for (l_row = l_first_row; l_row <= l_last_row; l_row++) { for (l_col = l_first_col; l_col <= l_last_col; l_col++) { if (l_col == l_first_col) l_x = src_gdrw->x1; else l_x = l_col * src_gdrw->tile_width; if (l_col == l_last_col) l_x2 = src_gdrw->x2; else l_x2 = (l_col +1) * src_gdrw->tile_width; if (cd->show_progress) gimp_progress_update (l_progress += l_progress_step); for( ; l_x < l_x2; l_x++) { if (l_row == l_first_row) l_y = src_gdrw->y1; else l_y = l_row * src_gdrw->tile_height; if(l_row == l_last_row) l_y2 = src_gdrw->y2; else l_y2 = (l_row +1) * src_gdrw->tile_height ; for( ; l_y < l_y2; l_y++) { /* ---------- copy SRC_PIXEL to curve position ------ */ p_get_pixel(src_gdrw, l_x, l_y, color); l_curvy = p_curve_get_dy(cd, l_x, (gint32)src_gdrw->drawable->width, (gint32)src_gdrw->drawable->height, (gdouble)l_y); l_desty = l_y + l_topshift + l_curvy; /* ----------- SMOOTING ------------------ */ if (cd->smoothing && (l_x > 0)) { l_nb_curvy = p_curve_get_dy(cd, l_x -1, (gint32)src_gdrw->drawable->width, (gint32)src_gdrw->drawable->height, (gdouble)l_y); if ((l_nb_curvy == l_curvy) && (l_x > 1)) { l_nb2_curvy = p_curve_get_dy(cd, l_x -2, (gint32)src_gdrw->drawable->width, (gint32)src_gdrw->drawable->height, (gdouble)l_y); } else { l_nb2_curvy = l_nb_curvy; } p_put_mix_pixel(dst_gdrw, l_x, l_desty, color, l_nb_curvy, l_nb2_curvy, l_curvy ); } else { p_put_pixel(dst_gdrw, l_x, l_desty, color); } /* ----------- render ANTIALIAS ------------------ */ if(cd->antialias) { l_othery = l_desty; if(l_y == src_gdrw->y1) /* Upper outline */ { first_arr[l_x].y = l_curvy; memcpy(first_arr[l_x].color, color, dst_gdrw->drawable->bpp); if (l_x > 0) { memcpy(mixcolor, first_arr[l_x-1].color, dst_gdrw->drawable->bpp); l_diff = abs(first_arr[l_x - 1].y - l_curvy) +1; l_miny = MIN(first_arr[l_x - 1].y, l_curvy) -1; l_maxy = MAX(first_arr[l_x - 1].y, l_curvy) +1; l_othery = (src_gdrw->y2 -1) + l_topshift + p_curve_get_dy(cd, l_x, (gint32)src_gdrw->drawable->width, (gint32)src_gdrw->drawable->height, (gdouble)(src_gdrw->y2 -1)); } } if (l_y == src_gdrw->y2 - 1) /* Lower outline */ { if (l_x > 0) { memcpy(mixcolor, last_arr[l_x-1].color, dst_gdrw->drawable->bpp); l_diff = abs(last_arr[l_x - 1].y - l_curvy) +1; l_maxy = MAX(last_arr[l_x - 1].y, l_curvy) +1; l_miny = MIN(last_arr[l_x - 1].y, l_curvy) -1; } l_othery = (src_gdrw->y1) + l_topshift + p_curve_get_dy(cd, l_x, (gint32)src_gdrw->drawable->width, (gint32)src_gdrw->drawable->height, (gdouble)(src_gdrw->y1)); } if(l_desty < l_othery) { l_alias_dir = 1; } /* fade to transp. with descending dy */ else if(l_desty > l_othery) { l_alias_dir = -1; } /* fade to transp. with ascending dy */ else { l_alias_dir = 0; } /* no antialias at curve crossing point(s) */ if (l_alias_dir != 0) { l_alpha_lo = 20; if (gimp_drawable_has_alpha(src_gdrw->drawable->drawable_id)) { l_alpha_lo = MIN(20, mixcolor[src_gdrw->index_alpha]); } for(l_dy = 0; l_dy < l_diff; l_dy++) { /* iterate for fading alpha channel */ l_mixmask = 255 * ((gdouble)(l_dy+1) / (gdouble)(l_diff+1)); mixcolor[dst_gdrw->index_alpha] = MIX_CHANNEL(color[dst_gdrw->index_alpha], l_alpha_lo, l_mixmask); if(l_alias_dir > 0) { p_put_pixel(dst_gdrw, l_x -1, l_y + l_topshift + l_miny + l_dy, mixcolor); } else { p_put_pixel(dst_gdrw, l_x -1, l_y + l_topshift + (l_maxy - l_dy), mixcolor); } } } } /* ------------------ FILL HOLES ------------------ */ if (l_y == src_gdrw->y1) { l_diff = 0; l_sign = 1; } else { l_diff = last_arr[l_x].y - l_curvy; if (l_diff < 0) { l_diff = 0 - l_diff; l_sign = -1; } else { l_sign = 1; } memcpy(mixcolor, color, dst_gdrw->drawable->bpp); } for (l_dy = 1; l_dy <= l_diff; l_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) { /* smooting is on, so we are using a mixed color */ gulong alpha1 = last_arr[l_x].color[3]; gulong alpha2 = color[3]; gulong alpha; l_mixmask = 255 * ((gdouble)(l_dy) / (gdouble)(l_diff+1)); alpha = alpha1 * l_mixmask + alpha2 * (255 - l_mixmask); mixcolor[3] = alpha/255; if (mixcolor[3]) { mixcolor[0] = (alpha1 * l_mixmask * last_arr[l_x].color[0] + alpha2 * (255 - l_mixmask) * color[0])/alpha; mixcolor[1] = (alpha1 * l_mixmask * last_arr[l_x].color[1] + alpha2 * (255 - l_mixmask) * color[1])/alpha; mixcolor[2] = (alpha1 * l_mixmask * last_arr[l_x].color[2] + alpha2 * (255 - l_mixmask) * color[2])/alpha; /*mixcolor[2] = MIX_CHANNEL(last_arr[l_x].color[2], color[2], l_mixmask);*/ } } else { /* smooting is off, so we are using this color or the last color */ if (l_dy < l_diff / 2) { memcpy(mixcolor, color, dst_gdrw->drawable->bpp); } else { memcpy(mixcolor, last_arr[l_x].color, dst_gdrw->drawable->bpp); } } if (cd->smoothing) { p_put_mix_pixel(dst_gdrw, l_x, l_desty + (l_dy * l_sign), mixcolor, l_nb_curvy, l_nb2_curvy, l_curvy ); } else { p_put_pixel(dst_gdrw, l_x, l_desty + (l_dy * l_sign), mixcolor); } } /* store y and color */ last_arr[l_x].y = l_curvy; memcpy(last_arr[l_x].color, color, dst_gdrw->drawable->bpp); } } } } } /* ============================================================================ * p_main_bend * ============================================================================ */ gint32 p_main_bend (BenderDialog *cd, GimpDrawable *original_drawable, gint work_on_copy) { t_GDRW l_src_gdrw; t_GDRW l_dst_gdrw; GimpDrawable *dst_drawable; GimpDrawable *src_drawable; gint32 l_dst_height; gint32 l_image_id; gint32 l_tmp_layer_id; gint32 l_interpolation; gint l_offset_x, l_offset_y; gint l_center_x, l_center_y; gint32 xmax, ymax; l_interpolation = cd->smoothing; l_image_id = gimp_drawable_get_image (original_drawable->drawable_id); gimp_drawable_offsets(original_drawable->drawable_id, &l_offset_x, &l_offset_y); l_center_x = l_offset_x + (gimp_drawable_width (original_drawable->drawable_id) / 2 ); l_center_y = l_offset_y + (gimp_drawable_height (original_drawable->drawable_id) / 2 ); /* always copy original_drawable to a tmp src_layer */ l_tmp_layer_id = gimp_layer_copy(original_drawable->drawable_id); /* set layer invisible and dummyname and * add at top of the image while working * (for the case of undo the gimp must know, * that the layer was part of the image) */ gimp_image_add_layer (l_image_id, l_tmp_layer_id, 0); gimp_drawable_set_visible (l_tmp_layer_id, FALSE); gimp_drawable_set_name (l_tmp_layer_id, "curve_bend_dummylayer"); if(gb_debug) printf("p_main_bend l_tmp_layer_id %d\n", (int)l_tmp_layer_id); if (cd->rotation != 0.0) { if(gb_debug) printf("p_main_bend rotate: %f\n", (float)cd->rotation); p_gimp_rotate(l_image_id, l_tmp_layer_id, l_interpolation, cd->rotation); } src_drawable = gimp_drawable_get (l_tmp_layer_id); xmax = ymax = src_drawable->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); l_dst_height = src_drawable->height + p_upper_curve_extend(cd, src_drawable->width, src_drawable->height) + p_lower_curve_extend(cd, src_drawable->width, src_drawable->height); if(gb_debug) printf("p_main_bend: l_dst_height:%d\n", (int)l_dst_height); if(work_on_copy) { dst_drawable = p_add_layer(src_drawable->width, l_dst_height, src_drawable); if(gb_debug) printf("p_main_bend: DONE add layer\n"); } else { /* work on the original */ gimp_layer_resize(original_drawable->drawable_id, src_drawable->width, l_dst_height, l_offset_x, l_offset_y); if(gb_debug) printf("p_main_bend: DONE layer resize\n"); if(!gimp_drawable_has_alpha(original_drawable->drawable_id)) { /* always add alpha channel */ gimp_layer_add_alpha(original_drawable->drawable_id); } dst_drawable = gimp_drawable_get (original_drawable->drawable_id); } p_clear_drawable(dst_drawable); p_init_gdrw(&l_src_gdrw, src_drawable, FALSE, FALSE); p_init_gdrw(&l_dst_gdrw, dst_drawable, TRUE, FALSE); p_vertical_bend(cd, &l_src_gdrw, &l_dst_gdrw); if(gb_debug) printf("p_main_bend: DONE vertical bend\n"); p_end_gdrw(&l_src_gdrw); p_end_gdrw(&l_dst_gdrw); if(cd->rotation != 0.0) { p_gimp_rotate (l_image_id, dst_drawable->drawable_id, l_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) */ l_offset_x = l_center_x - (gimp_drawable_width (dst_drawable->drawable_id) / 2 ); l_offset_y = l_center_y - (gimp_drawable_height (dst_drawable->drawable_id) / 2 ); gimp_layer_set_offsets (dst_drawable->drawable_id, l_offset_x, l_offset_y); /* delete the temp layer */ gimp_image_remove_layer (l_image_id, l_tmp_layer_id); g_free (cd->curve_ptr[OUTLINE_UPPER]); g_free (cd->curve_ptr[OUTLINE_LOWER]); if (gb_debug) printf("p_main_bend: DONE bend main\n"); return dst_drawable->drawable_id; }