/* curve_bend plugin for the GIMP (tested with GIMP 1.1.17, requires gtk+ 1.2) */ /* 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 * (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.1.17 (2000/02/16)" #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 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 3 #define PREVIEW_BG_GRAY1 108 #define PREVIEW_BG_GRAY2 156 #define SMOOTH 0 #define GFREE 1 #define RANGE_MASK GDK_EXPOSURE_MASK | \ GDK_ENTER_NOTIFY_MASK #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 *filesel; GDrawable *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; gint 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]; /* p_buildmenu Structures */ typedef struct _MenuItem MenuItem; typedef void (*MenuItemCallback) (GtkWidget *widget, gpointer user_data); struct _MenuItem { gchar *label; gchar unused_accelerator_key; gint unused_accelerator_mods; MenuItemCallback callback; gpointer user_data; MenuItem *unused_subitems; GtkWidget *widget; }; typedef struct { GDrawable *drawable; GPixelRgn pr; gint x1; gint y1; gint x2; gint y2; gint index_alpha; /* 0 == no alpha, 1 == GREYA, 3 == RGBA */ gint bpp; GTile *tile; gint tile_row; gint tile_col; gint tile_width; gint tile_height; gint tile_dirty; gint shadow; gint32 seldeltax; gint32 seldeltay; gint32 tile_swapcount; } t_GDRW; typedef struct { gint32 y; guchar color[4]; } t_Last; /* curves action functions */ static void query (void); static void run (gchar *name, gint nparams, GParam *param, gint *nreturn_vals, GParam **return_vals); static BenderDialog * bender_new_dialog (GDrawable *); 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_upper_callback (GtkWidget *, gpointer); static void bender_lower_callback (GtkWidget *, gpointer); static void bender_smooth_callback (GtkWidget *, gpointer); static void bender_free_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_ok_callback (GtkWidget *, gpointer); static void bender_cancel_callback (GtkWidget *, gpointer); 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 *, gpointer); static void bender_save_callback (GtkWidget *, gpointer); static gint bender_pv_widget_events (GtkWidget *, GdkEvent *, 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 (GDrawable *); GtkWidget * p_buildmenu (MenuItem *); static void p_init_gdrw (t_GDRW *gdrw, GDrawable *drawable, int dirty, int shadow); static void p_end_gdrw (t_GDRW *gdrw); static gint32 p_main_bend (BenderDialog *, GDrawable *, gint); static gint32 p_create_pv_image (GDrawable *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, char *filename); /* Global Variables */ GPlugInInfo 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 MenuItem outline_items[] = { { N_("Upper"), 0, 0, bender_upper_callback, NULL, NULL, NULL }, { N_("Lower"), 0, 0, bender_lower_callback, NULL, NULL, NULL }, { NULL, 0, 0, NULL, NULL, NULL, NULL } }; static MenuItem curve_type_items[] = { { N_("Smooth"), 0, 0, bender_smooth_callback, NULL, NULL, NULL }, { N_("Free"), 0, 0, bender_free_callback, NULL, NULL, NULL }, { NULL, 0, 0, NULL, NULL, NULL, NULL } }; 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 * ============================================================================ */ gint p_pdb_procedure_available (char *proc_name) { int l_nparams; int l_nreturn_vals; int l_proc_type; char *l_proc_blurb; char *l_proc_help; char *l_proc_author; char *l_proc_copyright; char *l_proc_date; GParamDef *l_params; GParamDef *l_return_vals; gint l_rc; l_rc = 0; /* Query the gimp application's procedural database * regarding a particular procedure. */ if(gimp_query_procedure (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); } printf("Warning: Procedure %s not found.\n", proc_name); return -1; } /* end p_pdb_procedure_available */ /* ============================================================================ * p_gimp_rotate * PDB call of 'gimp_rotate' * ============================================================================ */ gint p_gimp_rotate (gint32 image_id, gint32 drawable_id, gint32 interpolation, gdouble angle_deg) { static char *l_rotate_proc = "gimp_rotate"; GParam *return_vals; int nreturn_vals; gdouble l_angle_rad; int l_nparams; int l_rc; #ifdef ROTATE_OPTIMIZE static char *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, PARAM_INT32, RUN_NONINTERACTIVE, PARAM_IMAGE, image_id, PARAM_DRAWABLE, drawable_id, PARAM_INT32, l_angle_step, PARAM_INT32, FALSE, /* dont rotate the whole image */ PARAM_END); if (return_vals[0].data.d_status == STATUS_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, PARAM_DRAWABLE, drawable_id, PARAM_INT32, interpolation, PARAM_FLOAT, l_angle_rad, PARAM_END); if (return_vals[0].data.d_status == STATUS_SUCCESS) { l_rc = 0; } else { printf("Error: %s call failed %d\n", l_rotate_proc, (int)return_vals[0].data.d_status); } gimp_destroy_params (return_vals, nreturn_vals); } else { printf("Error: Procedure %s not found.\n",l_rotate_proc); } return (l_rc); } /* end p_gimp_rotate */ /* ============================================================================ * p_gimp_edit_copy * * ============================================================================ */ gint32 p_gimp_edit_copy (gint32 image_id, gint32 drawable_id) { static char *l_procname = "gimp_edit_copy"; GParam *return_vals; int nreturn_vals; if (p_pdb_procedure_available(l_procname) >= 0) { return_vals = gimp_run_procedure (l_procname, &nreturn_vals, PARAM_DRAWABLE, drawable_id, PARAM_END); if (return_vals[0].data.d_status == STATUS_SUCCESS) { gimp_destroy_params (return_vals, nreturn_vals); return 0; } printf("Error: PDB call of %s failed status:%d\n", l_procname, (int)return_vals[0].data.d_status); } return(-1); } /* end p_gimp_edit_copy */ /* ============================================================================ * p_gimp_edit_paste * * ============================================================================ */ gint32 p_gimp_edit_paste (gint32 image_id, gint32 drawable_id, gint32 paste_into) { static char *l_procname = "gimp_edit_paste"; GParam *return_vals; int nreturn_vals; gint32 fsel_layer_id; if (p_pdb_procedure_available(l_procname) >= 0) { return_vals = gimp_run_procedure (l_procname, &nreturn_vals, PARAM_DRAWABLE, drawable_id, PARAM_INT32, paste_into, PARAM_END); if (return_vals[0].data.d_status == STATUS_SUCCESS) { fsel_layer_id = return_vals[1].data.d_layer; gimp_destroy_params (return_vals, nreturn_vals); return (fsel_layer_id); } printf("Error: PDB call of %s failed status:%d\n", l_procname, (int)return_vals[0].data.d_status); } return(-1); } /* end p_gimp_edit_paste */ /* ============================================================================ * p_if_selection_float_it * ============================================================================ */ gint32 p_if_selection_float_it (gint32 image_id, gint32 layer_id) { gint32 l_sel_channel_id; gint32 l_layer_id; gint32 l_x1, l_x2, l_y1, l_y2; gint32 non_empty; l_layer_id = layer_id; if(!gimp_layer_is_floating_selection (layer_id) ) { /* 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 */ p_gimp_edit_copy(image_id, layer_id); l_layer_id = p_gimp_edit_paste(image_id, layer_id, FALSE); } } return(l_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 GParamDef args[] = { { PARAM_INT32, "run_mode", "Interactive, non-interactive"}, { PARAM_IMAGE, "image", "Input image" }, { PARAM_DRAWABLE, "drawable", "Input drawable (must be a layer without layermask)"}, { PARAM_FLOAT, "rotation", "Direction {angle 0 to 360 degree } of the bend effect"}, { PARAM_INT32, "smoothing", "Smoothing { TRUE, FALSE }"}, { PARAM_INT32, "antialias", "Antialias { TRUE, FALSE }"}, { PARAM_INT32, "work_on_copy", "{ TRUE, FALSE } TRUE: copy the drawable and bend the copy"}, { PARAM_INT32, "curve_type", " { 0, 1 } 0 == smooth (use 17 points), 1 == freehand (use 256 val_y) "}, { PARAM_INT32, "argc_upper_point_x", "{2 <= argc <= 17} "}, { PARAM_FLOATARRAY, "upper_point_x", "array of 17 x point_koords { 0.0 <= x <= 1.0 or -1 for unused point }"}, { PARAM_INT32, "argc_upper_point_y", "{2 <= argc <= 17} "}, { PARAM_FLOATARRAY, "upper_point_y", "array of 17 y point_koords { 0.0 <= y <= 1.0 or -1 for unused point }"}, { PARAM_INT32, "argc_lower_point_x", "{2 <= argc <= 17} "}, { PARAM_FLOATARRAY, "lower_point_x", "array of 17 x point_koords { 0.0 <= x <= 1.0 or -1 for unused point }"}, { PARAM_INT32, "argc_lower_point_y", "{2 <= argc <= 17} "}, { PARAM_FLOATARRAY, "lower_point_y", "array of 17 y point_koords { 0.0 <= y <= 1.0 or -1 for unused point }"}, { PARAM_INT32, "argc_upper_val_y", "{ 256 } "}, { PARAM_INT8ARRAY, "upper_val_y", "array of 256 y freehand koord { 0 <= y <= 255 }"}, { PARAM_INT32, "argc_lower_val_y", "{ 256 } "}, { PARAM_INT8ARRAY, "lower_val_y", "array of 256 y freehand koord { 0 <= y <= 255 }"} }; static gint nargs = sizeof (args) / sizeof (args[0]); static GParamDef return_vals[] = { { PARAM_LAYER, "bent_layer", "the handled layer" } }; static gint nreturn_vals = sizeof(return_vals) / sizeof(return_vals[0]); static GParamDef args_iter[] = { { PARAM_INT32, "run_mode", "non-interactive" }, { PARAM_INT32, "total_steps", "total number of steps (# of layers-1 to apply the related plug-in)" }, { PARAM_FLOAT, "current_step", "current (for linear iterations this is the layerstack position, otherwise some value inbetween)" }, { PARAM_INT32, "len_struct", "length of stored data structure with id is equal to the plug_in proc_name" }, }; static gint nargs_iter = sizeof (args_iter) / sizeof (args_iter[0]); /* 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_("/Filters/Distorts/CurveBend..."), PLUG_IN_IMAGE_TYPES, PROC_PLUG_IN, nargs, nreturn_vals, args, return_vals); /* the installation of the Iterator extension for the bend plugin */ gimp_install_procedure (PLUG_IN_ITER_NAME, "This extension 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, PROC_EXTENSION, nargs_iter, 0, args_iter, NULL); } static void run (char *name, /* name of plugin */ int nparams, /* number of in-paramters */ GParam *param, /* in-parameters */ int *nreturn_vals, /* number of out-parameters */ GParam **return_vals) /* out-parameters */ { char *l_env; BenderDialog *cd; GDrawable *l_active_drawable = NULL; 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 */ GRunModeType run_mode = param[0].data.d_int32; /* status variable, use it to check for errors in invocation usualy only during non-interactive calling */ GStatusType status = STATUS_SUCCESS; /*always return at least the status to the caller. */ static GParam values[2]; if (run_mode == RUN_INTERACTIVE) { INIT_I18N_UI(); } else { 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) fprintf(stderr, "\n\nDEBUG: run %s\n", name); /* initialize the return of the status */ values[0].type = PARAM_STATUS; values[0].data.d_status = status; values[1].type = PARAM_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 == 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); memcpy(&bval, &bval_from, sizeof(bval)); 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 = STATUS_CALLING_ERROR; } else status = STATUS_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_undo_push_group_start (l_image_id); if(!gimp_drawable_is_layer(l_layer_id)) { gimp_message(_("CurveBend operates on layers only (but was called on channel or mask)")); printf("Passed drawable is no Layer\n"); status = STATUS_EXECUTION_ERROR; } /* check for layermask */ l_layer_mask_id = gimp_layer_get_mask_id(l_layer_id); if(l_layer_mask_id >= 0) { /* apply the layermask * some transitions (especially rotate) cant operate proper on * layers with masks ! */ gimp_image_remove_layer_mask(l_image_id, l_layer_id, 0 /* 0==APPLY */ ); } /* if there is a selection, make it the floating selection layer */ l_active_drawable = gimp_drawable_get (p_if_selection_float_it(l_image_id, l_layer_id)); /* how are we running today? */ if(status == STATUS_SUCCESS) { /* how are we running today? */ switch (run_mode) { case 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 RUN_NONINTERACTIVE: /* check to see if invoked with the correct number of parameters */ if (nparams >= 20) { cd = g_malloc (sizeof (BenderDialog)); 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 = (int) param[7].data.d_int32; p_copy_points(cd, OUTLINE_UPPER, 0, (int)param[8].data.d_int32, param[9].data.d_floatarray); p_copy_points(cd, OUTLINE_UPPER, 1, (int)param[10].data.d_int32, param[11].data.d_floatarray); p_copy_points(cd, OUTLINE_LOWER, 0, (int)param[12].data.d_int32, param[13].data.d_floatarray); p_copy_points(cd, OUTLINE_LOWER, 1, (int)param[14].data.d_int32, param[15].data.d_floatarray); p_copy_yval(cd, OUTLINE_UPPER, (int)param[16].data.d_int32, param[17].data.d_int8array); p_copy_yval(cd, OUTLINE_UPPER, (int)param[18].data.d_int32, param[19].data.d_int8array); } else { status = STATUS_CALLING_ERROR; } break; case RUN_WITH_LAST_VALS: cd = g_malloc (sizeof (BenderDialog)); 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 == NULL) { status = STATUS_EXECUTION_ERROR; } if (status == STATUS_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 == RUN_INTERACTIVE) { p_store_values(cd); } } else { status = STATUS_EXECUTION_ERROR; /* dialog ended with cancel button */ } gimp_undo_push_group_end (l_image_id); /* If run mode is interactive, flush displays, else (script) don't do it, as the screen updates would make the scripts slow */ if (run_mode != RUN_NONINTERACTIVE) gimp_displays_flush (); } else { gimp_undo_push_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 */ if (gb_debug) printf ("end run curve_bend plugin\n"); } /* end run */ /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ /* Last_VALUEs and ITERATOR stuff */ /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ int p_save_pointfile (BenderDialog *cd, char *filename) { int j; FILE *l_fp; if(gb_debug) printf("Saving curve to file:%s\n", filename); l_fp = fopen(filename, "w+"); if(l_fp == NULL) { 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; /* OK */ } /* end p_save_pointfile */ int p_load_pointfile (BenderDialog *cd, char *filename) { int l_pi, l_ci, l_n, l_len; FILE *l_fp; char l_buff[2000]; float l_fux, l_fuy, l_flx, l_fly; int l_iuy, l_ily ; if(gb_debug) printf("Loading curve from file:%s\n", filename); l_fp = fopen(filename, "r"); if(l_fp == NULL) { 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)) { if(gb_debug) printf("FGETS: %s\n", l_buff); 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; if(gb_debug) printf("OK points[%d]\n", l_pi); 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++; if(gb_debug) printf("OK y_val[%d]\n", l_pi); } else { printf("warnig: BAD y_vals[%d] in file %s are ignored\n", l_ci, filename); } } } } fclose(l_fp); return 0; /* OK */ } /* end p_load_pointfile */ void p_cd_to_bval (BenderDialog *cd, BenderValues *bval) { int 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; } void p_cd_from_bval(BenderDialog *cd, BenderValues *bval) { int 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; } 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)); } 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_malloc(sizeof(BenderValues)); cd->bval_to = g_malloc(sizeof(BenderValues)); cd->bval_curr = g_malloc(sizeof(BenderValues)); memcpy(cd->bval_curr, &l_bval, sizeof(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); memcpy(cd->bval_curr, &l_bval, sizeof(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 */ BenderDialog * do_dialog (GDrawable *drawable) { BenderDialog *cd; gint i; /* 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; /* show the outline menu */ for (i = 0; i < 2; i++) { gtk_widget_set_sensitive( outline_items[i].widget, TRUE); } /* set the current selection */ gtk_option_menu_set_history ( GTK_OPTION_MENU (cd->outline_menu), 0); if (!GTK_WIDGET_VISIBLE (cd->shell)) gtk_widget_show (cd->shell); bender_update (cd, UP_GRAPH | UP_DRAW); 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; if(gb_debug) printf("do_dialog END\n"); return cd; } /**************************/ /* Select Curves dialog */ /**************************/ static BenderDialog * bender_new_dialog (GDrawable *drawable) { BenderDialog *cd; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *label; GtkWidget *frame; GtkWidget *toggle; GtkWidget *option_menu; GtkWidget *outline_hbox; GtkWidget *menu; GtkWidget *table; GtkWidget *button; GtkWidget *spinbutton; GtkObject *data; int i, j; cd = g_malloc (sizeof (BenderDialog)); cd->preview = FALSE; cd->curve_type = SMOOTH; cd->pixmap = NULL; cd->filesel = 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->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 */ for (i = 0; i < 2; i++) outline_items [i].user_data = (gpointer) cd; for (i = 0; i < 2; i++) curve_type_items [i].user_data = (gpointer) cd; /* The shell and main vbox */ cd->shell = gimp_dialog_new (_("Curve Bend"), "curve_bend", gimp_plugin_help_func, "filters/curve_bend.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, _("Reset"), bender_reset_callback, cd, NULL, NULL, FALSE, FALSE, _("Copy"), bender_copy_callback, cd, NULL, NULL, FALSE, FALSE, _("CopyInv"), bender_copy_inv_callback, cd, NULL, NULL, FALSE, FALSE, _("Swap"), bender_swap_callback, cd, NULL, NULL, FALSE, FALSE, _("OK"), bender_ok_callback, cd, NULL, NULL, TRUE, FALSE, _("Cancel"), bender_cancel_callback, cd, NULL, NULL, FALSE, TRUE, NULL); vbox = gtk_vbox_new (FALSE, 2); gtk_container_set_border_width (GTK_CONTAINER (vbox), 2); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (cd->shell)->vbox), vbox, TRUE, TRUE, 0); /* The option menu for selecting outlines */ outline_hbox = gtk_hbox_new (FALSE, 2+4); gtk_box_pack_start (GTK_BOX (vbox), outline_hbox, FALSE, FALSE, 0); /* The Load button */ button = gtk_button_new_with_label (_("LoadCurve")); gtk_box_pack_start (GTK_BOX (outline_hbox), button, TRUE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (bender_load_callback), cd); gtk_widget_show (button); /* The Save button */ button = gtk_button_new_with_label (_("SaveCurve")); gtk_box_pack_start (GTK_BOX (outline_hbox), button, TRUE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (bender_save_callback), cd); gtk_widget_show (button); /* Rotate label & spinbutton */ label = gtk_label_new (_("Rotate: ")); gtk_box_pack_start (GTK_BOX (outline_hbox), label, FALSE, FALSE, 0); data = gtk_adjustment_new (0, 0.0, 360.0, 1, 45, 90); cd->rotate_data = GTK_ADJUSTMENT (data); spinbutton = gtk_spin_button_new (cd->rotate_data, 0.5, 1); gtk_widget_set_usize (spinbutton, ENTRY_WIDTH, -1); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); gtk_box_pack_start (GTK_BOX (outline_hbox), spinbutton, FALSE, FALSE, 2); gtk_signal_connect (GTK_OBJECT (cd->rotate_data), "value_changed", GTK_SIGNAL_FUNC (bender_rotate_adj_callback), cd); gtk_widget_show (spinbutton); label = gtk_label_new (_("Curve for Border: ")); gtk_box_pack_start (GTK_BOX (outline_hbox), label, FALSE, FALSE, 0); menu = p_buildmenu (outline_items); cd->outline_menu = gtk_option_menu_new (); gtk_box_pack_start (GTK_BOX (outline_hbox), cd->outline_menu, FALSE, FALSE, 2); gtk_widget_show (label); gtk_widget_show (cd->outline_menu); gtk_widget_show (outline_hbox); gtk_option_menu_set_menu (GTK_OPTION_MENU (cd->outline_menu), menu); /* The option menu for selecting the drawing method */ label = gtk_label_new (_("Curve Type: ")); gtk_box_pack_start (GTK_BOX (outline_hbox), label, FALSE, FALSE, 0); menu = p_buildmenu (curve_type_items); option_menu = gtk_option_menu_new (); gtk_box_pack_start (GTK_BOX (outline_hbox), option_menu, FALSE, FALSE, 2); gtk_widget_show (label); gtk_widget_show (option_menu); gtk_widget_show (outline_hbox); gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu); /* The table for the pv_widget preview */ table = gtk_table_new (2, 2, FALSE); gtk_container_set_border_width (GTK_CONTAINER (table), 2); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); /* The range drawing area */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 0, 1, GTK_EXPAND, GTK_EXPAND, 0, 0); cd->pv_widget = gtk_preview_new (GTK_PREVIEW_COLOR); gtk_preview_size (GTK_PREVIEW (cd->pv_widget), PREVIEW_SIZE_X, PREVIEW_SIZE_Y); gtk_widget_set_events (cd->pv_widget, RANGE_MASK); gtk_signal_connect (GTK_OBJECT (cd->pv_widget), "event", (GtkSignalFunc) bender_pv_widget_events, cd); gtk_container_add (GTK_CONTAINER (frame), cd->pv_widget); gtk_widget_show (cd->pv_widget); gtk_widget_show (frame); /* The curves graph */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 0, 1, GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_FILL, 0, 0); cd->graph = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (cd->graph), GRAPH_WIDTH + RADIUS * 2, GRAPH_HEIGHT + RADIUS * 2); gtk_widget_set_events (cd->graph, GRAPH_MASK); gtk_signal_connect (GTK_OBJECT (cd->graph), "event", (GtkSignalFunc) bender_graph_events, cd); gtk_container_add (GTK_CONTAINER (frame), cd->graph); gtk_widget_show (cd->graph); gtk_widget_show (frame); gtk_widget_show (table); /* Horizontal box for preview */ hbox = gtk_hbox_new (FALSE, 2+3); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); /* The preview button */ button = gtk_button_new_with_label (_("PreviewOnce")); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (button), "clicked", (GtkSignalFunc) bender_preview_update_once, cd); gtk_widget_show (button); /* The preview toggle */ toggle = gtk_check_button_new_with_label (_("Preview")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->preview); gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) bender_preview_update, cd); gtk_widget_show (toggle); /* The smoothing toggle */ toggle = gtk_check_button_new_with_label (_("Smoothing")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->smoothing); gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) bender_smoothing_callback, cd); gtk_widget_show (toggle); /* The antialiasing toggle */ toggle = gtk_check_button_new_with_label (_("Antialiasing")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->antialias); gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) bender_antialias_callback, cd); gtk_widget_show (toggle); /* The wor_on_copy toggle */ toggle = gtk_check_button_new_with_label (_("Work on Copy")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->work_on_copy); gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, FALSE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) bender_work_on_copy_callback, cd); gtk_widget_show (toggle); gtk_widget_show (hbox); gtk_widget_show (vbox); return cd; } static void bender_update (BenderDialog *cd, int update) { int i; int other; if (update & UP_PREVIEW) { 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_draw (cd->pv_widget, 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_draw (cd->pv_widget, NULL); } 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 */ if(cd->outline == 0) { other = 1; } else { other = 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_pixmap (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; 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_upper_callback (GtkWidget *w, gpointer client_data) { BenderDialog *cd; cd = (BenderDialog *) client_data; if (cd->outline != OUTLINE_UPPER) { cd->outline = OUTLINE_UPPER; bender_update (cd, UP_GRAPH | UP_DRAW); } } static void bender_lower_callback (GtkWidget *w, gpointer client_data) { BenderDialog *cd; cd = (BenderDialog *) client_data; if (cd->outline != OUTLINE_LOWER) { cd->outline = OUTLINE_LOWER; bender_update (cd, UP_GRAPH | UP_DRAW); } } static void bender_smooth_callback (GtkWidget *w, gpointer client_data) { BenderDialog *cd; int i; gint32 index; cd = (BenderDialog *) client_data; if (cd->curve_type != SMOOTH) { cd->curve_type = SMOOTH; /* pick representative points from the curve and make them control points */ for (i = 0; i <= 8; i++) { 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); } } static void bender_free_callback (GtkWidget *w, gpointer client_data) { BenderDialog *cd; cd = (BenderDialog *) client_data; if (cd->curve_type != GFREE) { cd->curve_type = GFREE; 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; int i; int other; cd = (BenderDialog *) client_data; if(cd->outline == 0) other = 1; else other = 0; 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; int i; int other; cd = (BenderDialog *) client_data; if(cd->outline == 0) other = 1; else other = 0; 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; int i; int other; gdouble hd; guchar hu; cd = (BenderDialog *) client_data; if(cd->outline == 0) other = 1; else other = 0; 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_ok_callback (GtkWidget *widget, gpointer data) { BenderDialog *cd; cd = (BenderDialog *) data; if (GTK_WIDGET_VISIBLE (cd->shell)) gtk_widget_hide (cd->shell); cd->run = TRUE; gtk_widget_destroy (GTK_WIDGET (cd->shell)); gtk_main_quit (); } static void bender_cancel_callback (GtkWidget *widget, gpointer data) { BenderDialog *cd; cd = (BenderDialog *) data; gtk_main_quit (); } static void bender_preview_update (GtkWidget *widget, gpointer data) { BenderDialog *cd; cd = (BenderDialog *) data; if (GTK_TOGGLE_BUTTON (widget)->active) { cd->preview = TRUE; bender_update (cd, UP_PREVIEW | UP_DRAW); } else { cd->preview = FALSE; } } static void bender_preview_update_once (GtkWidget *widget, gpointer data) { BenderDialog *cd; cd = (BenderDialog *) data; bender_update (cd, UP_PREVIEW | UP_DRAW); } static void p_filesel_close_cb (GtkWidget *widget, gpointer data) { BenderDialog *cd; cd = (BenderDialog *) data; if(gb_debug) printf("p_filesel_close_cb\n"); if(cd->filesel == NULL) return; gtk_widget_destroy(GTK_WIDGET(cd->filesel)); cd->filesel = NULL; /* now filesel is closed */ } static void p_points_save_to_file (GtkWidget *widget, gpointer data) { BenderDialog *cd; char *filename; if(gb_debug) printf("p_points_save_to_file\n"); cd = (BenderDialog *) data; if(cd->filesel == NULL) return; filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (cd->filesel)); p_save_pointfile(cd, filename); gtk_widget_destroy(GTK_WIDGET(cd->filesel)); cd->filesel = NULL; } static void p_points_load_from_file (GtkWidget *widget, gpointer data) { BenderDialog *cd; char *filename; if(gb_debug) printf("p_points_load_from_file\n"); cd = (BenderDialog *) data; if(cd->filesel == NULL) return; filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (cd->filesel)); p_load_pointfile(cd, filename); gtk_widget_destroy(GTK_WIDGET(cd->filesel)); cd->filesel = NULL; bender_update (cd, UP_ALL); } static void bender_load_callback (GtkWidget *w, gpointer data) { BenderDialog *cd; GtkWidget *filesel; cd = (BenderDialog *) data; if(cd->filesel != NULL) return; /* filesel is already open */ filesel = gtk_file_selection_new ( _("Load Curve Points from file")); cd->filesel = filesel; gtk_window_position (GTK_WINDOW (filesel), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button), "clicked", (GtkSignalFunc) p_points_load_from_file, cd); gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button), "clicked", (GtkSignalFunc) p_filesel_close_cb, cd); gtk_signal_connect (GTK_OBJECT (filesel), "destroy", (GtkSignalFunc) p_filesel_close_cb, cd); gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), "curve_bend.points"); gtk_widget_show (filesel); } static void bender_save_callback (GtkWidget *w, gpointer data) { BenderDialog *cd; GtkWidget *filesel; cd = (BenderDialog *) data; if(cd->filesel != NULL) return; /* filesel is already open */ filesel = gtk_file_selection_new ( _("Save Curve Points to file")); cd->filesel = filesel; gtk_window_position (GTK_WINDOW (filesel), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button), "clicked", (GtkSignalFunc) p_points_save_to_file, cd); gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button), "clicked", (GtkSignalFunc) p_filesel_close_cb, cd); gtk_signal_connect (GTK_OBJECT (filesel), "destroy", (GtkSignalFunc) p_filesel_close_cb, cd); gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), "curve_bend.points"); gtk_widget_show (filesel); } static void bender_smoothing_callback (GtkWidget *w, gpointer data) { BenderDialog *cd; cd = (BenderDialog *) data; if (GTK_TOGGLE_BUTTON (w)->active) { cd->smoothing = TRUE; } else { cd->smoothing = FALSE; } if(cd->preview) bender_update (cd, UP_PREVIEW | UP_DRAW); } static void bender_antialias_callback (GtkWidget *w, gpointer data) { BenderDialog *cd; cd = (BenderDialog *) data; if (GTK_TOGGLE_BUTTON (w)->active) { cd->antialias = TRUE; } else { cd->antialias = FALSE; } if(cd->preview) bender_update (cd, UP_PREVIEW | UP_DRAW); } static void bender_work_on_copy_callback (GtkWidget *w, gpointer data) { BenderDialog *cd; cd = (BenderDialog *) data; if (GTK_TOGGLE_BUTTON (w)->active) { cd->work_on_copy = TRUE; } else { cd->work_on_copy = FALSE; } } static gint 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 gint bender_pv_widget_events (GtkWidget *widget, GdkEvent *event, BenderDialog *cd) { switch (event->type) { case GDK_EXPOSE: bender_update (cd, UP_PREVIEW_EXPOSE); break; default: break; } return FALSE; } static void bender_CR_compose (CRMatrix a, CRMatrix b, CRMatrix ab) { int 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]); } } } /* ============================================================================ * p_buildmenu * build menu widget for all Items passed in the MenuItems Parameter * MenuItems is an array of Pointers to Structure MenuItem. * The End is marked by a Structure Member where the label is a NULL pointer * (simplifyed version of GIMP 1.0.2 bulid_menu procedur) * ============================================================================ */ GtkWidget * p_buildmenu (MenuItem *items) { GtkWidget *menu; GtkWidget *menu_item; menu = gtk_menu_new (); while (items->label) { menu_item = gtk_menu_item_new_with_label ( gettext(items->label)); gtk_container_add (GTK_CONTAINER (menu), menu_item); if (items->callback) gtk_signal_connect (GTK_OBJECT (menu_item), "activate", (GtkSignalFunc) items->callback, items->user_data); gtk_widget_show (menu_item); items->widget = menu_item; items++; } return menu; } /* end p_buildmenu */ void p_render_preview (BenderDialog *cd, gint32 layer_id) { guchar l_rowbuf[PREVIEW_BPP * PREVIEW_SIZE_X]; guchar l_pixel[4]; guchar *l_ptr; GDrawable *l_pv_drawable; gint l_x, l_y; gint l_ofx, l_ofy; gint l_idx; guchar l_bg_gray; t_GDRW l_gdrw; t_GDRW *gdrw; l_pv_drawable = gimp_drawable_get (layer_id); 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++) { l_ptr = &l_rowbuf[0]; 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(l_pixel[gdrw->index_alpha] < 255) { /* for transparent pixels: mix with preview background color */ if((l_x % 32) < 16) { l_bg_gray = PREVIEW_BG_GRAY1; if ((l_y % 32) < 16) l_bg_gray = PREVIEW_BG_GRAY2; } else { l_bg_gray = PREVIEW_BG_GRAY2; if ((l_y % 32) < 16) l_bg_gray = PREVIEW_BG_GRAY1; } for(l_idx = 0; l_idx < gdrw->index_alpha ; l_idx++) { l_pixel[l_idx] = MIX_CHANNEL(l_pixel[l_idx], l_bg_gray, l_pixel[gdrw->index_alpha]); } } if(cd->color) { *l_ptr = l_pixel[0]; l_ptr[1] = l_pixel[1]; l_ptr[2] = l_pixel[2]; } else { *l_ptr = l_pixel[0]; l_ptr[1] = l_pixel[0]; l_ptr[2] = l_pixel[0]; } l_ptr += PREVIEW_BPP; } gtk_preview_draw_row(GTK_PREVIEW(cd->pv_widget), &l_rowbuf[0], 0, l_y, PREVIEW_SIZE_X); } p_end_gdrw(gdrw); } /* end p_render_preview */ /* ===================================================== */ /* curve_bend worker procedures */ /* ===================================================== */ 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); /* * printf("_X: %d _X2: %d Y2: %d _Y: %d debugY:%d\n", * (int)l_x1, (int)l_x2, * (int)cd->curve[l_outline][l_x2], * (int)cd->curve_ptr[l_outline][l_x1], * l_debugY); */ } } } } 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]); if(gb_debug) { printf("bender_init_min_max: zero2[0]: %d min2[0]:%d max2[0]:%d\n", (int)cd->zero2[0], (int)cd->min2[0], (int)cd->max2[0] ); printf("bender_init_min_max: zero2[1]: %d min2[1]:%d max2[1]:%d\n", (int)cd->zero2[1], (int)cd->min2[1], (int)cd->max2[1] ); } } 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 */ gint32 l_y; double l_y1, l_y2; double 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; l_y = SIGNED_ROUND(l_y1 + delta); return l_y; } 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)); } 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)); } void p_end_gdrw (t_GDRW *gdrw) { if(gb_debug) printf ("\np_end_gdrw: drawable %x ID: %d\n", (int)gdrw->drawable, (int)gdrw->drawable->id); if(gdrw->tile) { if(gb_debug) printf("p_end_gdrw: tile unref\n"); gimp_tile_unref( gdrw->tile, gdrw->tile_dirty); gdrw->tile = NULL; } if(gb_debug) printf("p_end_gdrw:TILE_SWAPCOUNT: %d\n", (int)gdrw->tile_swapcount); } /* end p_end_gdrw */ void p_init_gdrw (t_GDRW *gdrw, GDrawable *drawable, int dirty, int shadow) { gint32 l_image_id; gint32 l_sel_channel_id; gint l_offsetx, l_offsety; if(gb_debug) printf("\np_init_gdrw: drawable %x ID: %d\n", (int)drawable, (int)drawable->id); gdrw->drawable = drawable; gdrw->tile = NULL; gdrw->tile_dirty = FALSE; gdrw->tile_width = gimp_tile_width (); gdrw->tile_height = gimp_tile_height (); gdrw->shadow = shadow; gdrw->tile_swapcount = 0; gdrw->seldeltax = 0; gdrw->seldeltay = 0; gimp_drawable_offsets (drawable->id, &l_offsetx, &l_offsety); /* get offsets within the image */ gimp_drawable_mask_bounds (drawable->id, &gdrw->x1, &gdrw->y1, &gdrw->x2, &gdrw->y2); /* * gimp_pixel_rgn_init (&gdrw->pr, drawable, * gdrw->x1, gdrw->y1, gdrw->x2 - gdrw->x1, gdrw->y2 - gdrw->y1, * dirty, shadow); */ gdrw->bpp = drawable->bpp; 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 */ } if(gb_debug) printf("\np_init_gdrw: bpp %d index_alpha: %d\n", (int)gdrw->bpp, (int)gdrw->index_alpha); l_image_id = gimp_layer_get_image_id(drawable->id); /* check and see if we have a selection mask */ l_sel_channel_id = gimp_image_get_selection(l_image_id); if(gb_debug) { printf("p_init_gdrw: image_id %d sel_channel_id: %d\n", (int)l_image_id, (int)l_sel_channel_id); printf("p_init_gdrw: BOUNDS x1: %d y1: %d x2:%d y2: %d\n", (int)gdrw->x1, (int)gdrw->y1, (int)gdrw->x2,(int)gdrw->y2); printf("p_init_gdrw: OFFS x: %d y: %d\n", (int)l_offsetx, (int)l_offsety ); } } /* end p_init_gdrw */ static void p_provide_tile (t_GDRW *gdrw, gint col, gint row, gint shadow ) { if ( col != gdrw->tile_col || row != gdrw->tile_row || !gdrw->tile ) { if( gdrw->tile ) { gimp_tile_unref( gdrw->tile, gdrw->tile_dirty ); } gdrw->tile_col = col; gdrw->tile_row = row; gdrw->tile = gimp_drawable_get_tile( gdrw->drawable, shadow, gdrw->tile_row, gdrw->tile_col ); gdrw->tile_dirty = FALSE; gimp_tile_ref( gdrw->tile ); gdrw->tile_swapcount++; return; } } /* end p_provide_tile */ /* 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 ) { gint row, col; gint offx, offy; guchar *ptr; if((x < 0) || (x > gdrw->drawable->width -1) || (y < 0) || (y > gdrw->drawable->height - 1)) { pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0; return; } col = x / gdrw->tile_width; row = y / gdrw->tile_height; offx = x % gdrw->tile_width; offy = y % gdrw->tile_height; p_provide_tile( gdrw, col, row, gdrw->shadow); pixel[1] = 255; pixel[3] = 255; /* simulate full visible alpha channel */ ptr = gdrw->tile->data + ((( offy * gdrw->tile->ewidth) + offx ) * gdrw->bpp); memcpy(pixel, ptr, gdrw->bpp); /* printf("p_get_pixel: x: %d y: %d bpp:%d RGBA:%d %d %d %d\n", (int)x, (int)y, (int)gdrw->bpp, (int)pixel[0], (int)pixel[1], (int)pixel[2], (int)pixel[3]); */ } /* end p_get_pixel */ void p_put_pixel (t_GDRW *gdrw, gint32 x, gint32 y, guchar *pixel) { gint row, col; gint offx, offy; guchar *ptr; if (x < gdrw->x1) return; if (x > gdrw->x2 - 1) return; if (y < gdrw->y1) return; if (y > gdrw->y2 - 1) return; col = x / gdrw->tile_width; row = y / gdrw->tile_height; offx = x % gdrw->tile_width; offy = y % gdrw->tile_height; p_provide_tile( gdrw, col, row, gdrw->shadow ); ptr = gdrw->tile->data + ((( offy * gdrw->tile->ewidth ) + offx ) * gdrw->bpp); memcpy(ptr, pixel, gdrw->bpp); gdrw->tile_dirty = TRUE; /* printf("p_put_pixel: x: %d y: %d shadow: %d bpp:%d RGBA:%d %d %d %d\n", (int)x, (int)y, (int)gdrw->shadow, (int)gdrw->bpp, (int)ptr[0], (int)ptr[1], (int)ptr[2], (int)ptr[3]); */ } /* end p_put_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]); } void p_put_mix_pixel_FIX (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; l_mixmask = 256 - 64; /* get left neighbour pixel */ p_get_pixel(gdrw, x-1, y, &l_pixel[0]); if(l_pixel[gdrw->index_alpha] < 20) { /* 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 * ============================================================================ */ void p_clear_drawable (GDrawable *drawable) { GPixelRgn pixel_rgn; gpointer pr; gint 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 (GDrawable *src_drawable, gint32 *layer_id) { gint32 l_new_image_id; guint l_new_width; guint l_new_height; GDrawableType l_type; gint l_x, l_y; double l_scale; guchar l_pixel[4]; GDrawable *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_layer_get_image_id(src_drawable->id))); gimp_image_undo_disable (l_new_image_id); l_type = gimp_drawable_type(src_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 * ============================================================================ */ GDrawable* p_add_layer (gint width, gint height, GDrawable *src_drawable) { GDrawableType l_type; static GDrawable *l_new_drawable; gint32 l_new_layer_id; char *l_name; char *l_name2; gdouble l_opacity; GLayerMode l_mode; gint l_visible; gint32 image_id; gint stack_position; image_id = gimp_layer_get_image_id(src_drawable->id); stack_position = 0; /* TODO: should be same as src_layer */ /* copy type, name, opacity and mode from src_drawable */ l_type = gimp_layer_type(src_drawable->id); l_visible = gimp_layer_get_visible(src_drawable->id); if (TRUE != TRUE) { l_name = gimp_layer_get_name(src_drawable->id); } else { l_name2 = gimp_layer_get_name(src_drawable->id); l_name = g_malloc(strlen(l_name2) + 10); if(l_name == NULL) return (NULL); sprintf(l_name, "%s_b", l_name2); g_free(l_name2); } l_mode = gimp_layer_get_mode(src_drawable->id); l_opacity = gimp_layer_get_opacity(src_drawable->id); /* full opacity */ l_new_layer_id = gimp_layer_new(image_id, l_name, width, height, l_type, l_opacity, l_mode ); if(l_name != NULL) 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 == NULL) { 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_layer_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_malloc (sizeof (BenderDialog)); p_cd_from_bval(cd_from, cd->bval_from); cd_from->curve_ptr[OUTLINE_UPPER] = g_malloc(sizeof(gint32) * (1+xmax)); cd_from->curve_ptr[OUTLINE_LOWER] = g_malloc(sizeof(gint32) * (1+xmax)); /* init TO curves */ cd_to = g_malloc (sizeof (BenderDialog)); p_cd_from_bval(cd_to, cd->bval_to); cd_to->curve_ptr[OUTLINE_UPPER] = g_malloc(sizeof(gint32) * (1+xmax)); cd_to->curve_ptr[OUTLINE_LOWER] = g_malloc(sizeof(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; } /* end p_bender_calculate_iter_curve */ /* ============================================================================ * p_vertical_bend * ============================================================================ */ int 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_malloc(sizeof(t_Last) * src_gdrw->x2); first_arr = g_malloc(sizeof(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 ; /* printf("X: %4d Y:%4d Y2:%4d\n", (int)l_x, (int)l_y, (int)l_y2); */ for( ; l_y < l_y2; l_y++) { /* ---------- copy SRC_PIXEL to curve position ------ */ p_get_pixel(src_gdrw, l_x, l_y, &color[0]); 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 == TRUE) && (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[0], l_nb_curvy, l_nb2_curvy, l_curvy ); } else { p_put_pixel(dst_gdrw, l_x, l_desty, &color[0]); } /* ----------- 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[0], &color[0], dst_gdrw->drawable->bpp); if(l_x > 0) { memcpy(&mixcolor[0], &first_arr[l_x-1].color[0], 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[0], &last_arr[l_x-1].color[0], 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->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[0]); } else { p_put_pixel(dst_gdrw, l_x -1, l_y + l_topshift + (l_maxy - l_dy), &mixcolor[0]); } } } } /* ------------------ 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[0], &color[0], 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 == TRUE) { /* smooting is on, so we are using a mixed color */ l_mixmask = 255 * ((gdouble)(l_dy) / (gdouble)(l_diff+1)); mixcolor[0] = MIX_CHANNEL(last_arr[l_x].color[0], color[0], l_mixmask); mixcolor[1] = MIX_CHANNEL(last_arr[l_x].color[1], color[1], l_mixmask); mixcolor[2] = MIX_CHANNEL(last_arr[l_x].color[2], color[2], l_mixmask); mixcolor[3] = MIX_CHANNEL(last_arr[l_x].color[3], color[3], l_mixmask); } else { /* smooting is off, so we are using this color or the last color */ if(l_dy < l_diff / 2) { memcpy(&mixcolor[0], &color[0], dst_gdrw->drawable->bpp); } else { memcpy(&mixcolor[0], &last_arr[l_x].color[0], dst_gdrw->drawable->bpp); } } if(cd->smoothing == TRUE) { p_put_mix_pixel(dst_gdrw, l_x, l_desty + (l_dy * l_sign), &mixcolor[0], l_nb_curvy, l_nb2_curvy, l_curvy ); } else { p_put_pixel(dst_gdrw, l_x, l_desty + (l_dy * l_sign), &mixcolor[0]); } } /* store y and color */ last_arr[l_x].y = l_curvy; memcpy(&last_arr[l_x].color[0], &color[0], dst_gdrw->drawable->bpp); } } } } if (gb_debug) printf("ROWS: %d - %d COLS: %d - %d\n", (int)l_first_row, (int)l_last_row, (int)l_first_col, (int)l_last_col); return 0; } /* ============================================================================ * p_main_bend * ============================================================================ */ gint32 p_main_bend (BenderDialog *cd, GDrawable *original_drawable, gint work_on_copy) { t_GDRW l_src_gdrw; t_GDRW l_dst_gdrw; GDrawable *dst_drawable; GDrawable *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_layer_get_image_id(original_drawable->id); gimp_drawable_offsets(original_drawable->id, &l_offset_x, &l_offset_y); l_center_x = l_offset_x + (gimp_drawable_width (original_drawable->id) / 2 ); l_center_y = l_offset_y + (gimp_drawable_height (original_drawable->id) / 2 ); /* always copy original_drawable to a tmp src_layer */ l_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 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_layer_set_visible(l_tmp_layer_id, FALSE); gimp_layer_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_malloc(sizeof(gint32) * (1+xmax)); cd->curve_ptr[OUTLINE_LOWER] = g_malloc(sizeof(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->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->id)) { /* always add alpha channel */ gimp_layer_add_alpha(original_drawable->id); } dst_drawable = gimp_drawable_get (original_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->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->id) / 2 ); l_offset_y = l_center_y - (gimp_drawable_height (dst_drawable->id) / 2 ); gimp_layer_set_offsets (dst_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->id; } /* end p_main_bend */