/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * IfsCompose is a interface for creating IFS fractals by * direct manipulation. * Copyright (C) 1997 Owen Taylor * * 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. */ /* TODO * ---- * * 1. Run in non-interactive mode (need to figure out useful * way for a script to give the 19N paramters for an image). * Perhaps just support saving parameters to a file, script * passes file name. * 2. Save settings on a per-layer basis (long term, needs GIMP * support to do properly). Load/save from affine parameters? * 3. Figure out if we need multiple phases for supersampled * brushes. * 4. (minor) Make undo work correctly when focus is in entry widget. */ #include "config.h" #include #include #include #include #ifdef __GNUC__ #warning GTK_DISABLE_DEPRECATED #endif #undef GTK_DISABLE_DEPRECATED #include #include #include #include "ifscompose.h" #include "libgimp/stdplugins-intl.h" #define SCALE_WIDTH 150 #define ENTRY_WIDTH 60 #define DESIGN_AREA_MAX_SIZE 256 #define PREVIEW_RENDER_CHUNK 10000 #define UNDO_LEVELS 10 #define IFSCOMPOSE_PARASITE "ifscompose-parasite" #define IFSCOMPOSE_DATA "plug_in_ifscompose" typedef enum { OP_TRANSLATE, OP_ROTATE, /* or scale */ OP_STRETCH } DesignOp; typedef enum { VALUE_PAIR_INT, VALUE_PAIR_DOUBLE } ValuePairType; typedef struct { GtkObject *adjustment; GtkWidget *scale; GtkWidget *entry; ValuePairType type; union { gdouble *d; gint *i; } data; gint entry_handler_id; } ValuePair; typedef struct { IfsComposeVals ifsvals; AffElement **elements; gint *element_selected; gint current_element; } UndoItem; typedef struct { GimpRGB *color; GtkWidget *hbox; GtkWidget *orig_preview; GtkWidget *button; gboolean fixed_point; } ColorMap; typedef struct { GtkWidget *dialog; ValuePair *iterations_pair; ValuePair *subdivide_pair; ValuePair *radius_pair; ValuePair *memory_pair; } IfsOptionsDialog; typedef struct { GtkWidget *area; GtkWidget *op_menu; GdkPixmap *pixmap; DesignOp op; gdouble op_x; gdouble op_y; gdouble op_xcenter; gdouble op_ycenter; gdouble op_center_x; gdouble op_center_y; guint button_state; gint num_selected; GdkGC *selected_gc; } IfsDesignArea; typedef struct { ValuePair *prob_pair; ValuePair *x_pair; ValuePair *y_pair; ValuePair *scale_pair; ValuePair *angle_pair; ValuePair *asym_pair; ValuePair *shear_pair; GtkWidget *flip_check_button; ColorMap *red_cmap; ColorMap *green_cmap; ColorMap *blue_cmap; ColorMap *black_cmap; ColorMap *target_cmap; ValuePair *hue_scale_pair; ValuePair *value_scale_pair; GtkWidget *simple_button; GtkWidget *full_button; GtkWidget *current_frame; GtkWidget *move_button; gint move_handler; GtkWidget *rotate_button; gint rotate_handler; GtkWidget *stretch_button; gint stretch_handler; GtkWidget *preview; guchar *preview_data; gint preview_iterations; gint drawable_width; gint drawable_height; AffElement *selected_orig; gint current_element; AffElementVals current_vals; gint auto_preview; gboolean in_update; /* true if we're currently in update_values() - don't do anything on updates */ } IfsDialog; typedef struct { gint run; } IfsComposeInterface; /* Declare local functions. */ static void query (void); static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); /* user interface functions */ static gint ifs_compose_dialog (GimpDrawable *drawable); static void ifs_options_dialog (void); static GtkWidget *ifs_compose_trans_page (void); static GtkWidget *ifs_compose_color_page (void); static void design_op_menu_popup (gint button, guint32 activate_time); static void design_op_menu_create (GtkWidget *window); static void design_area_create (GtkWidget *window, gint design_width, gint design_height); /* functions for drawing design window */ static void update_values (void); static void set_current_element (gint index); static gint design_area_expose (GtkWidget *widget, GdkEventExpose *event); static gint design_area_button_press (GtkWidget *widget, GdkEventButton *event); static gint design_area_button_release (GtkWidget *widget, GdkEventButton *event); static void design_area_select_all_callback (GtkWidget *w, gpointer data); static gint design_area_configure (GtkWidget *widget, GdkEventConfigure *event); static gint design_area_motion (GtkWidget *widget, GdkEventMotion *event); static void design_area_redraw (void); /* Undo ring functions */ static void undo_begin (void); static void undo_update (gint element); static void undo_exchange (gint el); static void undo (void); static void redo (void); static void recompute_center (gboolean save_undo); static void recompute_center_cb (GtkWidget *widget, gpointer data); static void ifs_compose (GimpDrawable *drawable); static ColorMap *color_map_create (gchar *name, GimpRGB *orig_color, GimpRGB *data, gboolean fixed_point); static void color_map_color_changed_cb (GtkWidget *widget, ColorMap *color_map); static void color_map_update (ColorMap *color_map); /* interface functions */ static void simple_color_toggled (GtkWidget *widget,gpointer data); static void simple_color_set_sensitive (void); static void val_changed_update (void); static ValuePair *value_pair_create (gpointer data, gdouble lower, gdouble upper, gboolean create_scale, ValuePairType type); static void value_pair_update (ValuePair *value_pair); static void value_pair_entry_callback (GtkWidget *w, ValuePair *value_pair); static void value_pair_destroy_callback (GtkWidget *widget, ValuePair *value_pair); static void value_pair_button_release (GtkWidget *widget, GdkEventButton *event, gpointer data); static void value_pair_scale_callback (GtkAdjustment *adjustment, ValuePair *value_pair); static void auto_preview_callback (GtkWidget *widget, gpointer data); static void design_op_callback (GtkWidget *widget, gpointer data); static void design_op_update_callback (GtkWidget *widget, gpointer data); static void flip_check_button_callback (GtkWidget *widget, gpointer data); static gint preview_idle_render (void); static void ifs_compose_set_defaults (void); static void ifs_compose_defaults_callback (GtkWidget *widget, gpointer data); static void ifs_compose_new_callback (GtkWidget *widget, gpointer data); static void ifs_compose_delete_callback (GtkWidget *widget, gpointer data); static void ifs_compose_preview_callback (GtkWidget *widget, GtkWidget *preview); static void ifs_compose_close_callback (GtkWidget *widget, GtkWidget **destroyed_widget); static void ifs_compose_ok_callback (GtkWidget *widget, GtkWidget *window); /* * Some static variables */ IfsDialog *ifsD = NULL; IfsOptionsDialog *ifsOptD = NULL; IfsDesignArea *ifsDesign = NULL; static AffElement **elements = NULL; static gint *element_selected = NULL; /* labels are generated by printing this int */ static gint count_for_naming = 0; static UndoItem undo_ring[UNDO_LEVELS]; static gint undo_cur = -1; static gint undo_num = 0; static gint undo_start = 0; /* num_elements = 0, signals not inited */ static IfsComposeVals ifsvals = { 0, /* num_elements */ 50000, /* iterations */ 4096, /* max_memory */ 4, /* subdivide */ 0.75, /* radius */ 1.0, /* aspect ratio */ 0.5, /* center_x */ 0.5, /* center_y */ }; static IfsComposeInterface ifscint = { FALSE, /* run */ }; GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; 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" }, }; static GimpParamDef *return_vals = NULL; static int nreturn_vals = 0; INIT_I18N (); gimp_install_procedure ("plug_in_ifs_compose", "Create an Iterated Function System Fractal", "Interactively create an Iterated Function System fractal. " "Use the window on the upper left to adjust the component " "transformations of the fractal. The operation that is performed " "is selected by the buttons underneath the window, or from a " "menu popped up by the right mouse button. The fractal will be " "rendered with a transparent background if the current image has " "a transparent background.", "Owen Taylor", "Owen Taylor", "1997", N_("/Filters/Render/Nature/IfsCompose..."), "RGB*, GRAY*", GIMP_PLUGIN, G_N_ELEMENTS (args), nreturn_vals, args, return_vals); } static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[1]; GimpDrawable *active_drawable; GimpRunMode run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; GimpParasite *parasite = NULL; gboolean found_parasite; run_mode = param[0].data.d_int32; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; *nreturn_vals = 1; *return_vals = values; INIT_I18N_UI(); /* kill (getpid(), 19); */ /* Get the active drawable */ active_drawable = gimp_drawable_get (param[2].data.d_drawable); switch (run_mode) { case GIMP_RUN_INTERACTIVE: /* Possibly retrieve data; first look for a parasite - * if not found, fall back to global values */ parasite = gimp_drawable_parasite_find (active_drawable->drawable_id, IFSCOMPOSE_PARASITE); found_parasite = FALSE; if (parasite) { found_parasite = ifsvals_parse_string (gimp_parasite_data (parasite), &ifsvals, &elements); gimp_parasite_free (parasite); } if (!found_parasite) { gint length; gchar *data; length = gimp_get_data_size (IFSCOMPOSE_DATA); if (length) { data = g_new (gchar, length); gimp_get_data (IFSCOMPOSE_DATA, data); ifsvals_parse_string (data, &ifsvals, &elements); g_free (data); } } /* after ifsvals_parse_string, need to set up naming */ count_for_naming = ifsvals.num_elements; /* First acquire information with a dialog */ if (! ifs_compose_dialog (active_drawable)) return; break; case GIMP_RUN_NONINTERACTIVE: /* Make sure all the arguments are there! */ status = GIMP_PDB_CALLING_ERROR; break; case GIMP_RUN_WITH_LAST_VALS: /* Possibly retrieve data */ { gint length; gchar *data; length = gimp_get_data_size (IFSCOMPOSE_DATA); if (length) { data = g_new (gchar, length); gimp_get_data (IFSCOMPOSE_DATA, data); ifsvals_parse_string (data, &ifsvals, &elements); g_free (data); } else ifs_compose_set_defaults (); } break; default: break; } /* Render the fractal */ if ((status == GIMP_PDB_SUCCESS) && (gimp_drawable_is_rgb (active_drawable->drawable_id) || gimp_drawable_is_gray (active_drawable->drawable_id))) { /* set the tile cache size so that the operation works well */ gimp_tile_cache_ntiles (2 * (MAX (active_drawable->width, active_drawable->height) / gimp_tile_width () + 1)); /* run the effect */ ifs_compose (active_drawable); /* If the run mode is interactive, flush the displays */ if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush (); /* Store data for next invocation - both globally and * as a parasite on this layer */ if (run_mode == GIMP_RUN_INTERACTIVE) { gchar *str = ifsvals_stringify (&ifsvals, elements); GimpParasite *parasite; gimp_set_data (IFSCOMPOSE_DATA, str, strlen(str)+1); parasite = gimp_parasite_new (IFSCOMPOSE_PARASITE, GIMP_PARASITE_PERSISTENT | GIMP_PARASITE_UNDOABLE, strlen(str)+1, str); gimp_drawable_parasite_attach (active_drawable->drawable_id, parasite); gimp_parasite_free (parasite); g_free (str); } } else if (status == GIMP_PDB_SUCCESS) { status = GIMP_PDB_EXECUTION_ERROR; } values[0].data.d_status = status; gimp_drawable_detach (active_drawable); } static GtkWidget * ifs_compose_trans_page (void) { GtkWidget *vbox; GtkWidget *table; GtkWidget *label; vbox = gtk_vbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); table = gtk_table_new (3, 6, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_col_spacing (GTK_TABLE (table), 1, 6); gtk_table_set_col_spacing (GTK_TABLE (table), 3, 6); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_table_set_row_spacing (GTK_TABLE (table), 1, 4); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); /* X */ label = gtk_label_new (_("X:")); gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); ifsD->x_pair = value_pair_create (&ifsD->current_vals.x, 0.0, 1.0, FALSE, VALUE_PAIR_DOUBLE); gtk_table_attach (GTK_TABLE(table), ifsD->x_pair->entry, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->x_pair->entry); /* Y */ label = gtk_label_new (_("Y:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); ifsD->y_pair = value_pair_create (&ifsD->current_vals.y, 0.0, 1.0, FALSE, VALUE_PAIR_DOUBLE); gtk_table_attach (GTK_TABLE(table), ifsD->y_pair->entry, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->y_pair->entry); /* Scale */ label = gtk_label_new (_("Scale:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); ifsD->scale_pair = value_pair_create (&ifsD->current_vals.scale, 0.0, 1.0, FALSE, VALUE_PAIR_DOUBLE); gtk_table_attach (GTK_TABLE (table), ifsD->scale_pair->entry, 3, 4, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->scale_pair->entry); /* Angle */ label = gtk_label_new (_("Angle:")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0); gtk_table_attach (GTK_TABLE (table), label, 2, 3, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); ifsD->angle_pair = value_pair_create (&ifsD->current_vals.theta, -180, 180, FALSE, VALUE_PAIR_DOUBLE); gtk_table_attach (GTK_TABLE (table), ifsD->angle_pair->entry, 3, 4, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->angle_pair->entry); /* Asym */ label = gtk_label_new (_("Asymmetry:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 4, 5, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); ifsD->asym_pair = value_pair_create (&ifsD->current_vals.asym, 0.10, 10.0, FALSE, VALUE_PAIR_DOUBLE); gtk_table_attach (GTK_TABLE (table), ifsD->asym_pair->entry, 5, 6, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->asym_pair->entry); /* Shear */ label = gtk_label_new (_("Shear:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 4, 5, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); ifsD->shear_pair = value_pair_create (&ifsD->current_vals.shear, -10.0, 10.0, FALSE, VALUE_PAIR_DOUBLE); gtk_table_attach (GTK_TABLE (table), ifsD->shear_pair->entry, 5, 6, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->shear_pair->entry); /* Flip */ ifsD->flip_check_button = gtk_check_button_new_with_label (_("Flip")); gtk_table_attach (GTK_TABLE (table), ifsD->flip_check_button, 0, 6, 2, 3, GTK_FILL, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (ifsD->flip_check_button), "toggled", GTK_SIGNAL_FUNC (flip_check_button_callback), NULL); gtk_widget_show (ifsD->flip_check_button); return vbox; } static GtkWidget * ifs_compose_color_page (void) { GtkWidget *vbox; GtkWidget *table; GtkWidget *label; GSList *group = NULL; GimpRGB color; vbox = gtk_vbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); table = gtk_table_new (3, 5, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 6); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); /* Simple color control section */ ifsD->simple_button = gtk_radio_button_new_with_label (group, _("Simple")); gtk_table_attach (GTK_TABLE (table), ifsD->simple_button, 0, 1, 0, 2, GTK_FILL, GTK_FILL, 0, 0); group = gtk_radio_button_group (GTK_RADIO_BUTTON (ifsD->simple_button)); gtk_signal_connect (GTK_OBJECT (ifsD->simple_button), "toggled", GTK_SIGNAL_FUNC (simple_color_toggled), NULL); gtk_widget_show (ifsD->simple_button); ifsD->target_cmap = color_map_create (_("IfsCompose: Target"), NULL, &ifsD->current_vals.target_color, TRUE); gtk_table_attach (GTK_TABLE (table), ifsD->target_cmap->hbox, 1, 2, 0, 2, GTK_FILL, 0, 0, 0); gtk_widget_show (ifsD->target_cmap->hbox); label = gtk_label_new (_("Scale Hue by:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); ifsD->hue_scale_pair = value_pair_create (&ifsD->current_vals.hue_scale, 0.0, 1.0, TRUE, VALUE_PAIR_DOUBLE); gtk_table_attach (GTK_TABLE (table), ifsD->hue_scale_pair->scale, 3, 4, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->hue_scale_pair->scale); gtk_table_attach (GTK_TABLE (table), ifsD->hue_scale_pair->entry, 4, 5, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->hue_scale_pair->entry); label = gtk_label_new (_("Scale Value by:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 2, 3, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); ifsD->value_scale_pair = value_pair_create (&ifsD->current_vals.value_scale, 0.0, 1.0, TRUE, VALUE_PAIR_DOUBLE); gtk_table_attach (GTK_TABLE (table), ifsD->value_scale_pair->scale, 3, 4, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->value_scale_pair->scale); gtk_table_attach (GTK_TABLE (table), ifsD->value_scale_pair->entry, 4, 5, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->value_scale_pair->entry); /* Full color control section */ ifsD->full_button = gtk_radio_button_new_with_label (group, _("Full")); gtk_table_attach (GTK_TABLE (table), ifsD->full_button, 0, 1, 2, 3, GTK_FILL, GTK_FILL, 0, 0); group = gtk_radio_button_group (GTK_RADIO_BUTTON (ifsD->full_button)); gtk_widget_show (ifsD->full_button); gimp_rgb_set (&color, 1.0, 0.0, 0.0); ifsD->red_cmap = color_map_create (_("IfsCompose: Red"), &color, &ifsD->current_vals.red_color, FALSE); gtk_table_attach (GTK_TABLE (table), ifsD->red_cmap->hbox, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->red_cmap->hbox); gimp_rgb_set (&color, 0.0, 1.0, 0.0); ifsD->green_cmap = color_map_create( _("IfsCompose: Green"), &color, &ifsD->current_vals.green_color, FALSE); gtk_table_attach (GTK_TABLE (table), ifsD->green_cmap->hbox, 2, 3, 2, 3, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->green_cmap->hbox); gimp_rgb_set (&color, 0.0, 0.0, 1.0); ifsD->blue_cmap = color_map_create (_("IfsCompose: Blue"), &color, &ifsD->current_vals.blue_color, FALSE); gtk_table_attach (GTK_TABLE (table), ifsD->blue_cmap->hbox, 3, 4, 2, 3, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->blue_cmap->hbox); gimp_rgb_set (&color, 0.0, 0.0, 0.0); ifsD->black_cmap = color_map_create (_("IfsCompose: Black"), &color, &ifsD->current_vals.black_color, FALSE); gtk_table_attach (GTK_TABLE (table), ifsD->black_cmap->hbox, 4, 5, 2, 3, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsD->black_cmap->hbox); return vbox; } static gint ifs_compose_dialog (GimpDrawable *drawable) { GtkWidget *dlg; GtkWidget *label; GtkWidget *button; GtkWidget *check_button; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *util_hbox; GtkWidget *main_vbox; GtkWidget *alignment; GtkWidget *aspect_frame; GtkWidget *notebook; GtkWidget *page; gint design_width; gint design_height; design_width = drawable->width; design_height = drawable->height; if (design_width > design_height) { if (design_width > DESIGN_AREA_MAX_SIZE) { design_height = design_height * DESIGN_AREA_MAX_SIZE / design_width; design_width = DESIGN_AREA_MAX_SIZE; } } else { if (design_height > DESIGN_AREA_MAX_SIZE) { design_width = design_width * DESIGN_AREA_MAX_SIZE / design_height; design_height = DESIGN_AREA_MAX_SIZE; } } ifsD = g_new (IfsDialog, 1); ifsD->auto_preview = TRUE; ifsD->drawable_width = drawable->width; ifsD->drawable_height = drawable->height; ifsD->selected_orig = NULL; ifsD->preview_data = NULL; ifsD->preview_iterations = 0; ifsD->in_update = 0; gimp_ui_init ("ifscompose", TRUE); dlg = gimp_dialog_new (_("IfsCompose"), "ifscompose", gimp_standard_help_func, "filters/ifscompose.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, GTK_STOCK_CANCEL, gtk_widget_destroy, NULL, 1, NULL, FALSE, TRUE, _("Delete"), ifs_compose_delete_callback, NULL, NULL, NULL, FALSE, FALSE, _("Reset"), ifs_compose_defaults_callback, NULL, NULL, NULL, FALSE, FALSE, _("New"), ifs_compose_new_callback, NULL, NULL, NULL, FALSE, FALSE, GTK_STOCK_OK, ifs_compose_ok_callback, NULL, NULL, NULL, TRUE, FALSE, NULL); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", GTK_SIGNAL_FUNC (ifs_compose_close_callback), &dlg); /* The main vbox */ main_vbox = gtk_vbox_new (FALSE, 6); gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), main_vbox, TRUE, TRUE, 0); /* The design area */ hbox = gtk_hbox_new (FALSE, 6); gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); aspect_frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, (gdouble) design_width / design_height, 0); gtk_frame_set_shadow_type (GTK_FRAME (aspect_frame), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (hbox), aspect_frame, TRUE, TRUE, 0); design_area_create (dlg, design_width, design_height); gtk_container_add (GTK_CONTAINER (aspect_frame), ifsDesign->area); gtk_widget_show (ifsDesign->area); gtk_widget_show (aspect_frame); /* The Preview */ aspect_frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, (gdouble) design_width / design_height, 0); gtk_frame_set_shadow_type (GTK_FRAME (aspect_frame), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (hbox), aspect_frame, TRUE, TRUE, 0); ifsD->preview = gtk_preview_new (GTK_PREVIEW_COLOR); gtk_preview_size (GTK_PREVIEW (ifsD->preview), design_width, design_height); gtk_container_add (GTK_CONTAINER (aspect_frame), ifsD->preview); gtk_widget_show (ifsD->preview); gtk_widget_show (aspect_frame); gtk_widget_show (hbox); /* Iterations and preview options */ hbox = gtk_hbox_new (FALSE, 4); gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); alignment = gtk_alignment_new (1.0, 0.5, 0.5, 0.0); gtk_box_pack_start (GTK_BOX (hbox), alignment, TRUE, TRUE, 0); util_hbox = gtk_hbox_new (FALSE, 4); gtk_container_add (GTK_CONTAINER (alignment), util_hbox); ifsD->move_button = gtk_toggle_button_new_with_label (_("Move")); gtk_box_pack_start (GTK_BOX (util_hbox), ifsD->move_button, TRUE, TRUE, 0); gtk_widget_show (ifsD->move_button); ifsD->move_handler = gtk_signal_connect (GTK_OBJECT (ifsD->move_button),"toggled", GTK_SIGNAL_FUNC (design_op_callback), (gpointer) ((long) OP_TRANSLATE)); ifsD->rotate_button = gtk_toggle_button_new_with_label (_("Rotate/Scale")); gtk_box_pack_start (GTK_BOX (util_hbox), ifsD->rotate_button, TRUE, TRUE, 0); gtk_widget_show (ifsD->rotate_button); ifsD->rotate_handler = gtk_signal_connect (GTK_OBJECT (ifsD->rotate_button), "toggled", GTK_SIGNAL_FUNC (design_op_callback), (gpointer) ((long) OP_ROTATE)); ifsD->stretch_button = gtk_toggle_button_new_with_label (_("Stretch")); gtk_box_pack_start (GTK_BOX (util_hbox), ifsD->stretch_button, TRUE, TRUE, 0); gtk_widget_show (ifsD->stretch_button); ifsD->stretch_handler = gtk_signal_connect (GTK_OBJECT (ifsD->stretch_button), "toggled", GTK_SIGNAL_FUNC (design_op_callback), (gpointer) ((long) OP_STRETCH)); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ifsD->move_button), TRUE); gtk_widget_show (alignment); gtk_widget_show (util_hbox); alignment = gtk_alignment_new (1.0, 0.5, 0.5, 0.0); gtk_box_pack_start (GTK_BOX (hbox), alignment, TRUE, TRUE, 0); util_hbox = gtk_hbox_new (FALSE, 4); gtk_container_add (GTK_CONTAINER (alignment), util_hbox); button = gtk_button_new_with_label (_("Render Options")); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (ifs_options_dialog), NULL); gtk_box_pack_start (GTK_BOX (util_hbox), button, TRUE, TRUE, 0); gtk_widget_show (button); button = gtk_button_new_with_label (_("Preview")); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (ifs_compose_preview_callback), GTK_OBJECT (ifsD->preview)); gtk_box_pack_start (GTK_BOX (util_hbox), button, TRUE, TRUE, 0); gtk_widget_show (button); check_button = gtk_check_button_new_with_label (_("Auto")); gtk_box_pack_start (GTK_BOX (util_hbox), check_button, TRUE, TRUE, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), ifsD->auto_preview); gtk_signal_connect (GTK_OBJECT (check_button), "toggled", GTK_SIGNAL_FUNC (auto_preview_callback), NULL); gtk_widget_show (check_button); gtk_widget_show (util_hbox); gtk_widget_show (alignment); gtk_widget_show (hbox); /* The current transformation frame */ ifsD->current_frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (ifsD->current_frame), GTK_SHADOW_ETCHED_IN); gtk_box_pack_start (GTK_BOX (main_vbox), ifsD->current_frame, FALSE, FALSE, 0); vbox = gtk_vbox_new (FALSE, 4); gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); gtk_container_add (GTK_CONTAINER (ifsD->current_frame), vbox); /* The notebook */ notebook = gtk_notebook_new (); gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP); gtk_box_pack_start (GTK_BOX (vbox), notebook, FALSE, FALSE, 0); gtk_widget_show (notebook); page = ifs_compose_trans_page (); label = gtk_label_new (_("Spatial Transformation")); gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, label); gtk_widget_show (page); page = ifs_compose_color_page (); label = gtk_label_new (_("Color Transformation")); gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, label); gtk_widget_show (page); /* The probability entry */ hbox = gtk_hbox_new (FALSE, 4); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); label = gtk_label_new (_("Relative Probability:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); ifsD->prob_pair = value_pair_create (&ifsD->current_vals.prob, 0.0, 5.0, TRUE, VALUE_PAIR_DOUBLE); gtk_box_pack_start (GTK_BOX (hbox), ifsD->prob_pair->scale, TRUE, TRUE, 0); gtk_widget_show (ifsD->prob_pair->scale); gtk_box_pack_start (GTK_BOX (hbox), ifsD->prob_pair->entry, FALSE, TRUE, 0); gtk_widget_show (ifsD->prob_pair->entry); gtk_widget_show (hbox); gtk_widget_show (vbox); gtk_widget_show (ifsD->current_frame); gtk_widget_show (main_vbox); if (ifsvals.num_elements == 0) { ifs_compose_set_defaults(); if (ifsD->auto_preview) ifs_compose_preview_callback (NULL, ifsD->preview); } else { gint i; gdouble ratio = (gdouble) ifsD->drawable_height / ifsD->drawable_width; element_selected = g_new (gint, ifsvals.num_elements); element_selected[0] = TRUE; for (i = 1; i < ifsvals.num_elements; i++) element_selected[i] = FALSE; if (ratio != ifsvals.aspect_ratio) { /* Adjust things so that what fit onto the old image, fits onto the new image */ Aff2 t1,t2,t3; gdouble x_offset, y_offset; gdouble center_x, center_y; gdouble scale; if (ratio < ifsvals.aspect_ratio) { scale = ratio/ifsvals.aspect_ratio; x_offset = (1-scale)/2; y_offset = 0; } else { scale = 1; x_offset = 0; y_offset = (ratio - ifsvals.aspect_ratio)/2; } aff2_scale (&t1, scale, 0); aff2_translate (&t2, x_offset, y_offset); aff2_compose (&t3, &t2, &t1); aff2_invert (&t1, &t3); aff2_apply (&t3, ifsvals.center_x, ifsvals.center_y, ¢er_x, ¢er_y); for (i = 0; i < ifsvals.num_elements; i++) { aff_element_compute_trans (elements[i],1,ifsvals.aspect_ratio, ifsvals.center_x, ifsvals.center_y); aff2_compose (&t2, &elements[i]->trans, &t1); aff2_compose (&elements[i]->trans, &t3, &t2); aff_element_decompose_trans (elements[i],&elements[i]->trans, 1, ifsvals.aspect_ratio, center_x, center_y); } ifsvals.center_x = center_x; ifsvals.center_y = center_y; ifsvals.aspect_ratio = ratio; } for (i = 0; i < ifsvals.num_elements; i++) aff_element_compute_color_trans (elements[i]); /* boundary and spatial transformations will be computed when the design_area gets a ConfigureNotify event */ set_current_element (0); if (ifsD->auto_preview) ifs_compose_preview_callback (NULL, ifsD->preview); ifsD->selected_orig = g_new (AffElement, ifsvals.num_elements); } gtk_widget_show (dlg); gtk_main (); gtk_object_unref (GTK_OBJECT (ifsDesign->op_menu)); if (dlg) gtk_widget_destroy (dlg); if (ifsOptD) gtk_widget_destroy (ifsOptD->dialog); gdk_flush (); g_object_unref (ifsDesign->selected_gc); g_free(ifsD); return ifscint.run; } static void design_area_create (GtkWidget *window, gint design_width, gint design_height) { ifsDesign = g_new (IfsDesignArea, 1); ifsDesign->op = OP_TRANSLATE; ifsDesign->button_state = 0; ifsDesign->pixmap = NULL; ifsDesign->selected_gc = NULL; ifsDesign->area = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (ifsDesign->area), design_width, design_height); gtk_signal_connect (GTK_OBJECT (ifsDesign->area), "expose_event", (GtkSignalFunc)design_area_expose, NULL); gtk_signal_connect (GTK_OBJECT (ifsDesign->area), "button_press_event", (GtkSignalFunc)design_area_button_press, NULL); gtk_signal_connect (GTK_OBJECT (ifsDesign->area), "button_release_event", (GtkSignalFunc)design_area_button_release, NULL); gtk_signal_connect (GTK_OBJECT (ifsDesign->area), "motion_notify_event", (GtkSignalFunc)design_area_motion, NULL); gtk_signal_connect (GTK_OBJECT (ifsDesign->area), "configure_event", (GtkSignalFunc) design_area_configure, NULL); gtk_widget_set_events (ifsDesign->area, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); design_op_menu_create (window); } static void design_op_menu_create (GtkWidget *window) { GtkWidget *menu_item; GtkAccelGroup *accel_group; ifsDesign->op_menu = gtk_menu_new(); gtk_object_ref (GTK_OBJECT (ifsDesign->op_menu)); gtk_object_sink (GTK_OBJECT (ifsDesign->op_menu)); accel_group = gtk_accel_group_new(); gtk_menu_set_accel_group(GTK_MENU(ifsDesign->op_menu), accel_group); gtk_window_add_accel_group(GTK_WINDOW(window),accel_group); menu_item = gtk_menu_item_new_with_label(_("Move")); gtk_menu_shell_append (GTK_MENU_SHELL (ifsDesign->op_menu), menu_item); gtk_widget_show(menu_item); gtk_signal_connect(GTK_OBJECT(menu_item),"activate", (GtkSignalFunc)design_op_update_callback, (gpointer)((long)OP_TRANSLATE)); gtk_widget_add_accelerator(menu_item, "activate", accel_group, 'M', 0, GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED); menu_item = gtk_menu_item_new_with_label(_("Rotate/Scale")); gtk_menu_shell_append (GTK_MENU_SHELL (ifsDesign->op_menu), menu_item); gtk_widget_show(menu_item); gtk_signal_connect(GTK_OBJECT(menu_item),"activate", (GtkSignalFunc)design_op_update_callback, (gpointer)((long)OP_ROTATE)); gtk_widget_add_accelerator(menu_item, "activate", accel_group, 'R', 0, GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED); menu_item = gtk_menu_item_new_with_label(_("Stretch")); gtk_menu_shell_append (GTK_MENU_SHELL (ifsDesign->op_menu), menu_item); gtk_widget_show(menu_item); gtk_signal_connect(GTK_OBJECT(menu_item),"activate", (GtkSignalFunc)design_op_update_callback, (gpointer)((long)OP_STRETCH)); gtk_widget_add_accelerator(menu_item, "activate", accel_group, 'S', 0, GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED); /* A separator */ menu_item = gtk_menu_item_new(); gtk_menu_shell_append (GTK_MENU_SHELL (ifsDesign->op_menu), menu_item); gtk_widget_show(menu_item); menu_item = gtk_menu_item_new_with_label(_("Select All")); gtk_menu_shell_append (GTK_MENU_SHELL (ifsDesign->op_menu), menu_item); gtk_widget_show(menu_item); gtk_signal_connect(GTK_OBJECT(menu_item),"activate", (GtkSignalFunc)design_area_select_all_callback, NULL); gtk_widget_add_accelerator(menu_item, "activate", accel_group, 'A', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED); menu_item = gtk_menu_item_new_with_label(_("Recompute Center")); gtk_menu_shell_append (GTK_MENU_SHELL (ifsDesign->op_menu), menu_item); gtk_widget_show(menu_item); gtk_signal_connect(GTK_OBJECT(menu_item),"activate", (GtkSignalFunc)recompute_center_cb, NULL); gtk_widget_add_accelerator(menu_item, "activate", accel_group, 'R', GDK_MOD1_MASK, GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED); menu_item = gtk_menu_item_new_with_label(_("Undo")); gtk_menu_shell_append (GTK_MENU_SHELL (ifsDesign->op_menu), menu_item); gtk_widget_show(menu_item); gtk_signal_connect(GTK_OBJECT(menu_item),"activate", (GtkSignalFunc)undo, NULL); gtk_widget_add_accelerator(menu_item, "activate", accel_group, 'Z', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED); menu_item = gtk_menu_item_new_with_label(_("Redo")); gtk_menu_shell_append (GTK_MENU_SHELL (ifsDesign->op_menu), menu_item); gtk_widget_show(menu_item); gtk_signal_connect(GTK_OBJECT(menu_item),"activate", (GtkSignalFunc)redo, NULL); gtk_widget_add_accelerator(menu_item, "activate", accel_group, 'R', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED); } static void design_op_menu_popup (gint button, guint32 activate_time) { gtk_menu_popup (GTK_MENU (ifsDesign->op_menu), NULL, NULL, NULL, NULL, button, activate_time); } static void ifs_options_dialog (void) { GtkWidget *table; GtkWidget *label; if (!ifsOptD) { ifsOptD = g_new (IfsOptionsDialog, 1); ifsOptD->dialog = gimp_dialog_new (_("IfsCompose Options"), "ifscompose", gimp_standard_help_func, "filters/ifscompose.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, GTK_STOCK_CLOSE, gtk_widget_hide, NULL, 1, NULL, TRUE, TRUE, NULL); /* Table of options */ table = gtk_table_new (4, 3, FALSE); gtk_container_set_border_width (GTK_CONTAINER (table), 6); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (ifsOptD->dialog)->vbox), table, FALSE, FALSE, 0); gtk_widget_show (table); label = gtk_label_new (_("Max. Memory:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); ifsOptD->memory_pair = value_pair_create (&ifsvals.max_memory, 1, 1000000, FALSE, VALUE_PAIR_INT); gtk_table_attach (GTK_TABLE (table), ifsOptD->memory_pair->entry, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsOptD->memory_pair->entry); label = gtk_label_new (_("Iterations:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); ifsOptD->iterations_pair = value_pair_create (&ifsvals.iterations, 1, 10000000, FALSE, VALUE_PAIR_INT); gtk_table_attach (GTK_TABLE (table), ifsOptD->iterations_pair->entry, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsOptD->iterations_pair->entry); gtk_widget_show (label); label = gtk_label_new (_("Subdivide:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); ifsOptD->subdivide_pair = value_pair_create (&ifsvals.subdivide, 1, 10, FALSE, VALUE_PAIR_INT); gtk_table_attach (GTK_TABLE (table), ifsOptD->subdivide_pair->entry, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsOptD->subdivide_pair->entry); label = gtk_label_new (_("Spot Radius:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); ifsOptD->radius_pair = value_pair_create (&ifsvals.radius, 0, 5, TRUE, VALUE_PAIR_DOUBLE); gtk_table_attach (GTK_TABLE (table), ifsOptD->radius_pair->scale, 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsOptD->radius_pair->scale); gtk_table_attach (GTK_TABLE (table), ifsOptD->radius_pair->entry, 2, 3, 3, 4, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (ifsOptD->radius_pair->entry); value_pair_update (ifsOptD->iterations_pair); value_pair_update (ifsOptD->subdivide_pair); value_pair_update (ifsOptD->memory_pair); value_pair_update (ifsOptD->radius_pair); gtk_widget_show (ifsOptD->dialog); } else { if (!GTK_WIDGET_VISIBLE (ifsOptD->dialog)) gtk_widget_show (ifsOptD->dialog); } } static void ifs_compose (GimpDrawable *drawable) { GimpImageType type = gimp_drawable_type (drawable->drawable_id); gchar *buffer; gint width = drawable->width; gint height = drawable->height; gint num_bands, band_height, band_y, band_no; gint i, j; guchar *data; guchar *mask = NULL; guchar *nhits; guchar rc, gc, bc; GimpRGB color; num_bands = ceil((gdouble)(width*height*SQR(ifsvals.subdivide)*5) / (1024 * ifsvals.max_memory)); band_height = (height + num_bands - 1) / num_bands; if (band_height > height) band_height = height; mask = g_new (guchar, width * band_height * SQR (ifsvals.subdivide)); data = g_new (guchar, width * band_height * SQR (ifsvals.subdivide) * 3); nhits = g_new (guchar, width * band_height * SQR (ifsvals.subdivide)); gimp_palette_get_background (&color); gimp_rgb_get_uchar (&color, &rc, &gc, &bc); band_y = 0; for (band_no = 0; band_no < num_bands; band_no++) { guchar *ptr; guchar *maskptr; guchar *dest; guchar *destrow; guchar maskval; GimpPixelRgn dest_rgn; gint progress; gint max_progress; gpointer pr; buffer = g_strdup_printf (_("Rendering IFS (%d/%d)..."), band_no+1, num_bands); gimp_progress_init(buffer); g_free (buffer); /* render the band to a buffer */ if (band_y + band_height > height) band_height = height - band_y; /* we don't need to clear data since we store nhits */ memset(mask, 0, width*band_height*SQR(ifsvals.subdivide)); memset(nhits, 0, width*band_height*SQR(ifsvals.subdivide)); ifs_render(elements, ifsvals.num_elements, width, height, ifsvals.iterations, &ifsvals, band_y, band_height, data, mask, nhits, FALSE); /* transfer the image to the drawable */ buffer = g_strdup_printf (_("Copying IFS to image (%d/%d)..."), band_no+1,num_bands); gimp_progress_init(buffer); g_free (buffer); progress = 0; max_progress = band_height * width; gimp_pixel_rgn_init (&dest_rgn, drawable, 0, band_y, width, band_height, TRUE, TRUE); for (pr = gimp_pixel_rgns_register (1, &dest_rgn); pr != NULL; pr = gimp_pixel_rgns_process (pr)) { destrow = dest_rgn.data; for (j = dest_rgn.y; j < (dest_rgn.y + dest_rgn.h); j++) { dest = destrow; for (i = dest_rgn.x; i < (dest_rgn.x + dest_rgn.w); i++) { /* Accumulate a reduced pixel */ gint ii,jj; gint rtot=0; gint btot=0; gint gtot=0; gint mtot=0; for (jj=0;jjdrawable_id, TRUE); gimp_drawable_update (drawable->drawable_id,0,0,width,height); } static void update_values (void) { ifsD->in_update = TRUE; ifsD->current_vals = elements[ifsD->current_element]->v; ifsD->current_vals.theta *= 180/G_PI; value_pair_update(ifsD->prob_pair); value_pair_update(ifsD->x_pair); value_pair_update(ifsD->y_pair); value_pair_update(ifsD->scale_pair); value_pair_update(ifsD->angle_pair); value_pair_update(ifsD->asym_pair); value_pair_update(ifsD->shear_pair); color_map_update(ifsD->red_cmap); color_map_update(ifsD->green_cmap); color_map_update(ifsD->blue_cmap); color_map_update(ifsD->black_cmap); color_map_update(ifsD->target_cmap); value_pair_update(ifsD->hue_scale_pair); value_pair_update(ifsD->value_scale_pair); if (elements[ifsD->current_element]->v.simple_color) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ifsD->simple_button), TRUE); else gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ifsD->full_button), TRUE); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ifsD->flip_check_button), elements[ifsD->current_element]->v.flip); ifsD->in_update = FALSE; simple_color_set_sensitive(); } static void set_current_element (gint index) { ifsD->current_element = index; gtk_frame_set_label(GTK_FRAME(ifsD->current_frame),elements[index]->name); update_values(); } static gint design_area_expose (GtkWidget *widget, GdkEventExpose *event) { gint i; gint cx,cy; if (!ifsDesign->selected_gc) { ifsDesign->selected_gc = gdk_gc_new(ifsDesign->area->window); gdk_gc_set_line_attributes(ifsDesign->selected_gc,2, GDK_LINE_SOLID,GDK_CAP_ROUND, GDK_JOIN_ROUND); } gdk_draw_rectangle(ifsDesign->pixmap, widget->style->bg_gc[widget->state], TRUE, event->area.x, event->area.y, event->area.width,event->area.height); /* draw an indicator for the center */ cx = ifsvals.center_x * widget->allocation.width; cy = ifsvals.center_y * widget->allocation.width; gdk_draw_line(ifsDesign->pixmap, widget->style->fg_gc[widget->state], cx - 10, cy, cx + 10, cy); gdk_draw_line(ifsDesign->pixmap, widget->style->fg_gc[widget->state], cx, cy - 10, cx, cy + 10); for (i=0;iallocation.width, widget->allocation.height, ifsDesign->pixmap, widget->style->fg_gc[widget->state], ifsDesign->selected_gc, gtk_style_get_font (ifsDesign->area->style)); } gdk_draw_drawable(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE (widget)], ifsDesign->pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); return FALSE; } static gint design_area_configure (GtkWidget *widget, GdkEventConfigure *event) { gint i; gdouble width = widget->allocation.width; gdouble height = widget->allocation.height; for (i=0;ipixmap) { g_object_unref(ifsDesign->pixmap); } ifsDesign->pixmap = gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, gtk_preview_get_visual()->depth); return FALSE; } static gint design_area_button_press (GtkWidget *widget, GdkEventButton *event) { gint i; gdouble width = ifsDesign->area->allocation.width; gint old_current; gtk_widget_grab_focus(widget); if (event->button != 1 || (ifsDesign->button_state & GDK_BUTTON1_MASK)) { if (event->button == 3) design_op_menu_popup(event->button, event->time); return FALSE; } old_current = ifsD->current_element; ifsD->current_element = -1; /* Find out where the button press was */ for (i=0;iclick_boundary,event->x,event->y) ) { set_current_element(i); break; } } /* if the user started manipulating an object, set up a new position on the undo ring */ if (ifsD->current_element >= 0) undo_begin(); if (!(event->state & GDK_SHIFT_MASK) && ( (ifsD->current_element<0) || !element_selected[ifsD->current_element] )) { for (i=0;icurrent_element >= 0) { ifsDesign->button_state |= GDK_BUTTON1_MASK; element_selected[ifsD->current_element] = TRUE; ifsDesign->num_selected = 0; ifsDesign->op_xcenter = 0.0; ifsDesign->op_ycenter = 0.0; for (i=0;iselected_orig[i] = *elements[i]; ifsDesign->op_xcenter += elements[i]->v.x; ifsDesign->op_ycenter += elements[i]->v.y; ifsDesign->num_selected++; undo_update(i); } } ifsDesign->op_xcenter /= ifsDesign->num_selected; ifsDesign->op_ycenter /= ifsDesign->num_selected; ifsDesign->op_x = (gdouble)event->x/width; ifsDesign->op_y = (gdouble)event->y/width; ifsDesign->op_center_x = ifsvals.center_x; ifsDesign->op_center_y = ifsvals.center_y; } else { ifsD->current_element = old_current; element_selected[old_current] = TRUE; } design_area_redraw(); return FALSE; } static gint design_area_button_release (GtkWidget *widget, GdkEventButton *event) { if (event->button == 1 && (ifsDesign->button_state & GDK_BUTTON1_MASK)) { ifsDesign->button_state &= ~GDK_BUTTON1_MASK; if (ifsD->auto_preview) ifs_compose_preview_callback(NULL, ifsD->preview); } return FALSE; } static gint design_area_motion (GtkWidget *widget, GdkEventMotion *event) { gint i; gdouble xo; gdouble yo; gdouble xn; gdouble yn; gint px,py; gdouble width = ifsDesign->area->allocation.width; gdouble height = ifsDesign->area->allocation.height; Aff2 trans,t1,t2,t3; if (!(ifsDesign->button_state & GDK_BUTTON1_MASK)) return FALSE; if (event->is_hint) { gtk_widget_get_pointer(ifsDesign->area, &px, &py); event->x = px; event->y = py; } xo = (ifsDesign->op_x - ifsDesign->op_xcenter); yo = (ifsDesign->op_y - ifsDesign->op_ycenter); xn = (gdouble)event->x/width - ifsDesign->op_xcenter; yn = (gdouble)event->y/width - ifsDesign->op_ycenter; /* for (i=0;ibounding_box); } */ switch (ifsDesign->op) { case OP_ROTATE: { aff2_translate(&t1,-ifsDesign->op_xcenter*width, -ifsDesign->op_ycenter*width); aff2_scale(&t2, sqrt((SQR(xn)+SQR(yn))/(SQR(xo)+SQR(yo))), 0); aff2_compose(&t3, &t2, &t1); aff2_rotate(&t1, - atan2(yn,xn) + atan2(yo,xo)); aff2_compose(&t2, &t1, &t3); aff2_translate(&t3,ifsDesign->op_xcenter*width, ifsDesign->op_ycenter*width); aff2_compose(&trans, &t3, &t2); break; } case OP_STRETCH: { aff2_translate(&t1,-ifsDesign->op_xcenter*width, -ifsDesign->op_ycenter*width); aff2_compute_stretch(&t2, xo, yo, xn, yn); aff2_compose(&t3, &t2, &t1); aff2_translate(&t1,ifsDesign->op_xcenter*width, ifsDesign->op_ycenter*width); aff2_compose(&trans, &t1, &t3); break; } case OP_TRANSLATE: { aff2_translate(&trans,(xn-xo)*width,(yn-yo)*width); break; } } for (i=0;inum_selected == ifsvals.num_elements) { gdouble cx,cy; aff2_invert(&t1, &trans); aff2_compose(&t2, &trans, &ifsD->selected_orig[i].trans); aff2_compose(&elements[i]->trans, &t2, &t1); cx = ifsDesign->op_center_x * width; cy = ifsDesign->op_center_y * width; aff2_apply(&trans,cx,cy,&cx,&cy); ifsvals.center_x = cx / width; ifsvals.center_y = cy / width; } else { aff2_compose(&elements[i]->trans, &trans, &ifsD->selected_orig[i].trans); } aff_element_decompose_trans(elements[i],&elements[i]->trans, width,height, ifsvals.center_x, ifsvals.center_y); aff_element_compute_trans(elements[i],width,height, ifsvals.center_x, ifsvals.center_y); } update_values(); design_area_redraw(); return FALSE; } static void design_area_redraw (void) { gint i; gdouble width = ifsDesign->area->allocation.width; gdouble height = ifsDesign->area->allocation.height; for (i=0;iarea,NULL); } /* Undo ring functions */ static void undo_begin (void) { gint i,j; gint to_delete; gint new_index; if (undo_cur == UNDO_LEVELS-1) { to_delete = 1; undo_start = (undo_start + 1)%UNDO_LEVELS; } else { undo_cur++; to_delete = undo_num - undo_cur; } undo_num = undo_num - to_delete + 1; new_index = (undo_start+undo_cur)%UNDO_LEVELS; /* remove any redo elements or the oldest element if necessary */ for (j=new_index;to_delete>0;j=(j+1)%UNDO_LEVELS,to_delete--) { for (i=0;icurrent_element; for (i=0;idraw_boundary = NULL; elem->click_boundary = NULL; } static void undo_exchange (gint el) { gint i; AffElement **telements; gint *tselected; IfsComposeVals tifsvals; gint tcurrent; gdouble width = ifsDesign->area->allocation.width; gdouble height = ifsDesign->area->allocation.height; /* swap the arrays and values*/ telements = elements; elements = undo_ring[el].elements; undo_ring[el].elements = telements; tifsvals = ifsvals; ifsvals = undo_ring[el].ifsvals; undo_ring[el].ifsvals = tifsvals; tselected = element_selected; element_selected = undo_ring[el].element_selected; undo_ring[el].element_selected = tselected; tcurrent = ifsD->current_element; ifsD->current_element = undo_ring[el].current_element; undo_ring[el].current_element = tcurrent; /* now swap back any unchanged elements */ for (i=0;icurrent_element); design_area_redraw(); if (ifsD->auto_preview) ifs_compose_preview_callback(NULL, ifsD->preview); } static void undo (void) { if (undo_cur >= 0) { undo_exchange((undo_start+undo_cur)%UNDO_LEVELS); undo_cur--; } } static void redo (void) { if (undo_cur != undo_num - 1) { undo_cur++; undo_exchange((undo_start+undo_cur)%UNDO_LEVELS); } } static void design_area_select_all_callback (GtkWidget *w, gpointer data) { gint i; for (i=0;iarea->allocation.width; gdouble height = ifsDesign->area->allocation.height; AffElement *cur = elements[ifsD->current_element]; if (ifsD->in_update) return; undo_begin(); undo_update(ifsD->current_element); cur->v = ifsD->current_vals; cur->v.theta *= G_PI/180.0; aff_element_compute_trans(cur,width,height, ifsvals.center_x, ifsvals.center_y); aff_element_compute_color_trans(cur); design_area_redraw(); if (ifsD->auto_preview) ifs_compose_preview_callback(NULL, ifsD->preview); } /* Pseudo-widget representing a color mapping */ #define COLOR_SAMPLE_SIZE 30 static ColorMap * color_map_create (gchar *name, GimpRGB *orig_color, GimpRGB *data, gboolean fixed_point) { GtkWidget *frame; GtkWidget *arrow; ColorMap *color_map = g_new (ColorMap,1); color_map->color = data; color_map->fixed_point = fixed_point; color_map->hbox = gtk_hbox_new (FALSE,2); frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (color_map->hbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); color_map->orig_preview = gimp_color_area_new (fixed_point ? data : orig_color, GIMP_COLOR_AREA_FLAT, 0); gtk_drag_dest_unset (color_map->orig_preview); gtk_widget_set_usize (color_map->orig_preview, COLOR_SAMPLE_SIZE, COLOR_SAMPLE_SIZE); gtk_container_add (GTK_CONTAINER(frame), color_map->orig_preview); gtk_widget_show (color_map->orig_preview); arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (color_map->hbox), arrow, FALSE, FALSE, 0); gtk_widget_show (arrow); color_map->button = gimp_color_button_new (name, COLOR_SAMPLE_SIZE, COLOR_SAMPLE_SIZE, data, GIMP_COLOR_AREA_FLAT); gtk_box_pack_start (GTK_BOX (color_map->hbox), color_map->button, FALSE, FALSE, 0); gtk_widget_show (color_map->button); gtk_signal_connect (GTK_OBJECT (color_map->button), "color_changed", GTK_SIGNAL_FUNC (gimp_color_button_get_color), data); gtk_signal_connect (GTK_OBJECT (color_map->button), "color_changed", GTK_SIGNAL_FUNC (color_map_color_changed_cb), color_map); return color_map; } static void color_map_color_changed_cb (GtkWidget *widget, ColorMap *color_map) { undo_begin (); undo_update (ifsD->current_element); elements[ifsD->current_element]->v = ifsD->current_vals; elements[ifsD->current_element]->v.theta *= G_PI/180.0; aff_element_compute_color_trans (elements[ifsD->current_element]); update_values (); if (ifsD->auto_preview) ifs_compose_preview_callback (NULL, ifsD->preview); } static void color_map_update (ColorMap *color_map) { gimp_color_button_set_color (GIMP_COLOR_BUTTON (color_map->button), color_map->color); if (color_map->fixed_point) gimp_color_area_set_color (GIMP_COLOR_AREA (color_map->orig_preview), color_map->color); } static void simple_color_set_sensitive (void) { gint sc = elements[ifsD->current_element]->v.simple_color; gtk_widget_set_sensitive(ifsD->target_cmap->hbox,sc); gtk_widget_set_sensitive(ifsD->hue_scale_pair->scale,sc); gtk_widget_set_sensitive(ifsD->hue_scale_pair->entry,sc); gtk_widget_set_sensitive(ifsD->value_scale_pair->scale,sc); gtk_widget_set_sensitive(ifsD->value_scale_pair->entry,sc); gtk_widget_set_sensitive(ifsD->red_cmap->hbox,!sc); gtk_widget_set_sensitive(ifsD->green_cmap->hbox,!sc); gtk_widget_set_sensitive(ifsD->blue_cmap->hbox,!sc); gtk_widget_set_sensitive(ifsD->black_cmap->hbox,!sc); } static void simple_color_toggled (GtkWidget *widget, gpointer data) { AffElement *cur = elements[ifsD->current_element]; cur->v.simple_color = GTK_TOGGLE_BUTTON(widget)->active; ifsD->current_vals.simple_color = cur->v.simple_color; if (cur->v.simple_color) { aff_element_compute_color_trans(cur); val_changed_update(); } simple_color_set_sensitive(); } /* Generic mechanism for scale/entry combination (possibly without scale) */ static ValuePair * value_pair_create (gpointer data, gdouble lower, gdouble upper, gboolean create_scale, ValuePairType type) { ValuePair *value_pair = g_new(ValuePair,1); value_pair->data.d = data; value_pair->type = type; value_pair->adjustment = gtk_adjustment_new (1.0, lower, upper, (upper-lower) / 100, (upper-lower) / 10, 0.0); /* We need to sink the adjustment, since we may not create a scale for * it, so nobody will assume the initial refcount */ gtk_object_ref (value_pair->adjustment); gtk_object_sink (value_pair->adjustment); gtk_signal_connect (GTK_OBJECT (value_pair->adjustment), "value_changed", GTK_SIGNAL_FUNC (value_pair_scale_callback), value_pair); if (create_scale) { value_pair->scale = gtk_hscale_new(GTK_ADJUSTMENT (value_pair->adjustment)); gtk_widget_ref (value_pair->scale); if (type == VALUE_PAIR_INT) gtk_scale_set_digits (GTK_SCALE (value_pair->scale), 0); else gtk_scale_set_digits (GTK_SCALE (value_pair->scale), 2); gtk_scale_set_draw_value (GTK_SCALE (value_pair->scale), FALSE); gtk_signal_connect (GTK_OBJECT (value_pair->scale), "button_release_event", (GtkSignalFunc) value_pair_button_release, NULL); } else value_pair->scale = NULL; /* We destroy the value pair when the entry is destroyed, so * we don't need to hold a refcount on the entry */ value_pair->entry = gtk_entry_new (); gtk_widget_set_usize (value_pair->entry, ENTRY_WIDTH, 0); value_pair->entry_handler_id = gtk_signal_connect (GTK_OBJECT (value_pair->entry), "changed", GTK_SIGNAL_FUNC (value_pair_entry_callback), value_pair); gtk_signal_connect (GTK_OBJECT (value_pair->entry), "destroy", GTK_SIGNAL_FUNC (value_pair_destroy_callback), value_pair); return value_pair; } static void value_pair_update (ValuePair *value_pair) { gchar buffer[32]; if (value_pair->type == VALUE_PAIR_INT) { GTK_ADJUSTMENT(value_pair->adjustment)->value = *value_pair->data.i; sprintf (buffer, "%d", *value_pair->data.i); } else { GTK_ADJUSTMENT(value_pair->adjustment)->value = *value_pair->data.d; sprintf (buffer, "%0.2f", *value_pair->data.d); } gtk_signal_emit_by_name (value_pair->adjustment, "value_changed"); gtk_signal_handler_block(GTK_OBJECT(value_pair->entry), value_pair->entry_handler_id); gtk_entry_set_text (GTK_ENTRY (value_pair->entry), buffer); gtk_signal_handler_unblock(GTK_OBJECT(value_pair->entry), value_pair->entry_handler_id); } static void value_pair_button_release (GtkWidget *widget, GdkEventButton *event, gpointer data) { val_changed_update (); } static void value_pair_scale_callback (GtkAdjustment *adjustment, ValuePair *value_pair) { gchar buffer[32]; gint changed = FALSE; if (value_pair->type == VALUE_PAIR_DOUBLE) { if ((gfloat)*value_pair->data.d != adjustment->value) { changed = TRUE; *value_pair->data.d = adjustment->value; sprintf (buffer, "%0.2f", adjustment->value); } } else { if (*value_pair->data.i != (gint)adjustment->value) { changed = TRUE; *value_pair->data.i = adjustment->value; sprintf (buffer, "%d", (gint)adjustment->value); } } if (changed) { gtk_signal_handler_block(GTK_OBJECT(value_pair->entry), value_pair->entry_handler_id); gtk_entry_set_text (GTK_ENTRY (value_pair->entry), buffer); gtk_signal_handler_unblock(GTK_OBJECT(value_pair->entry), value_pair->entry_handler_id); } } static void value_pair_entry_callback (GtkWidget *widget, ValuePair *value_pair) { GtkAdjustment *adjustment = GTK_ADJUSTMENT (value_pair->adjustment); gdouble new_value; gdouble old_value; if (value_pair->type == VALUE_PAIR_INT) { old_value = *value_pair->data.i; new_value = atoi(gtk_entry_get_text(GTK_ENTRY(widget))); } else { old_value = *value_pair->data.d; new_value = atof(gtk_entry_get_text(GTK_ENTRY(widget))); } if (floor(0.5+old_value*10000) != floor(0.5+new_value*10000)) { if ((new_value >= adjustment->lower) && (new_value <= adjustment->upper)) { if (value_pair->type == VALUE_PAIR_INT) *value_pair->data.i = new_value; else *value_pair->data.d = new_value; adjustment->value = new_value; gtk_signal_emit_by_name(GTK_OBJECT(adjustment), "value_changed"); val_changed_update(); } } } static void value_pair_destroy_callback (GtkWidget *widget, ValuePair *value_pair) { if (value_pair->scale) gtk_object_unref (GTK_OBJECT (value_pair->scale)); gtk_object_unref (value_pair->adjustment); } static void design_op_callback (GtkWidget *widget, gpointer data) { DesignOp op = (DesignOp)data; if (op != ifsDesign->op) { switch (ifsDesign->op) { case OP_TRANSLATE: gtk_signal_handler_block(GTK_OBJECT(ifsD->move_button), ifsD->move_handler); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ifsD->move_button), FALSE); gtk_signal_handler_unblock(GTK_OBJECT(ifsD->move_button), ifsD->move_handler); break; case OP_ROTATE: gtk_signal_handler_block(GTK_OBJECT(ifsD->rotate_button), ifsD->rotate_handler); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ifsD->rotate_button), FALSE); gtk_signal_handler_unblock(GTK_OBJECT(ifsD->rotate_button), ifsD->rotate_handler); break; case OP_STRETCH: gtk_signal_handler_block(GTK_OBJECT(ifsD->stretch_button), ifsD->stretch_handler); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ifsD->stretch_button), FALSE); gtk_signal_handler_unblock(GTK_OBJECT(ifsD->stretch_button), ifsD->stretch_handler); break; } ifsDesign->op = op; } else { GTK_TOGGLE_BUTTON(widget)->active = TRUE; } } static void design_op_update_callback (GtkWidget *widget, gpointer data) { DesignOp op = (DesignOp)data; if (op != ifsDesign->op) { switch (op) { case OP_TRANSLATE: gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(ifsD->move_button), TRUE); break; case OP_ROTATE: gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(ifsD->rotate_button), TRUE); break; case OP_STRETCH: gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(ifsD->stretch_button), TRUE); break; } } } static void recompute_center_cb (GtkWidget *widget, gpointer data) { recompute_center (TRUE); } static void recompute_center (gboolean save_undo) { gint i; gdouble x,y; gdouble center_x = 0.0; gdouble center_y = 0.0; gdouble width = ifsDesign->area->allocation.width; gdouble height = ifsDesign->area->allocation.height; if (save_undo) undo_begin(); for (i=0;itrans, &x, &y); center_x += x; center_y += y; } ifsvals.center_x = center_x/ifsvals.num_elements; ifsvals.center_y = center_y/ifsvals.num_elements; for (i=0;itrans, 1,ifsvals.aspect_ratio, ifsvals.center_x, ifsvals.center_y); } if (width > 1 && height > 1) { for (i=0;iauto_preview) { ifsD->auto_preview = 0; } else { ifsD->auto_preview = 1; ifs_compose_preview_callback(NULL, ifsD->preview); } } static void flip_check_button_callback (GtkWidget *widget, gpointer data) { ifsD->current_vals.flip = GTK_TOGGLE_BUTTON(widget)->active; val_changed_update (); } static void ifs_compose_set_defaults (void) { gint i; GimpRGB color; gimp_palette_get_foreground (&color); ifsvals.aspect_ratio = (gdouble)ifsD->drawable_height/ifsD->drawable_width; for (i=0;idrawable_height * ifsD->drawable_width; ifsvals.subdivide = 3; ifsvals.max_memory = 4096; if (ifsOptD) { value_pair_update (ifsOptD->iterations_pair); value_pair_update (ifsOptD->subdivide_pair); value_pair_update (ifsOptD->radius_pair); value_pair_update (ifsOptD->memory_pair); } ifsvals.radius = 0.7; set_current_element(0); element_selected[0] = TRUE; recompute_center(FALSE); if (ifsD->selected_orig) g_free(ifsD->selected_orig); ifsD->selected_orig = g_new (AffElement, ifsvals.num_elements); } static void ifs_compose_defaults_callback (GtkWidget *widget, gpointer data) { gint i; gdouble width = ifsDesign->area->allocation.width; gdouble height = ifsDesign->area->allocation.height; undo_begin (); for (i = 0; i < ifsvals.num_elements; i++) undo_update(i); ifs_compose_set_defaults(); if (ifsD->auto_preview) ifs_compose_preview_callback (NULL, ifsD->preview); for (i = 0; i < ifsvals.num_elements; i++) aff_element_compute_trans (elements[i], width, height, ifsvals.center_x, ifsvals.center_y); design_area_redraw (); } static void ifs_compose_new_callback (GtkWidget *widget, gpointer data) { GimpRGB color; gdouble width = ifsDesign->area->allocation.width; gdouble height = ifsDesign->area->allocation.height; gint i; AffElement *elem; undo_begin(); gimp_palette_get_foreground (&color); elem = aff_element_new (0.5, 0.5 * height / width, &color, ++count_for_naming); ifsvals.num_elements++; elements = g_realloc (elements, ifsvals.num_elements * sizeof (AffElement *)); element_selected = g_realloc (element_selected, ifsvals.num_elements * sizeof (gint)); for (i=0;iselected_orig = g_realloc(ifsD->selected_orig, ifsvals.num_elements*sizeof(AffElement)); aff_element_compute_trans(elem,width,height, ifsvals.center_x, ifsvals.center_y); design_area_redraw(); if (ifsD->auto_preview) ifs_compose_preview_callback(NULL, ifsD->preview); } static void ifs_compose_delete_callback (GtkWidget *widget, gpointer data) { gint i; gint new_current; if (ifsvals.num_elements <= 2) return; undo_begin(); undo_update(ifsD->current_element); aff_element_free(elements[ifsD->current_element]); if (ifsD->current_element < ifsvals.num_elements-1) { undo_update(ifsvals.num_elements-1); elements[ifsD->current_element] = elements[ifsvals.num_elements-1]; new_current = ifsD->current_element; } else new_current = ifsvals.num_elements-2; ifsvals.num_elements--; for (i=0;iauto_preview) ifs_compose_preview_callback(NULL, ifsD->preview); } static void ifs_compose_close_callback (GtkWidget *widget, GtkWidget **destroyed_widget) { *destroyed_widget = NULL; gtk_main_quit (); } static gint preview_idle_render (void) { gint i; gint width = GTK_WIDGET(ifsD->preview)->requisition.width; gint height = GTK_WIDGET(ifsD->preview)->requisition.height; gint iterations = PREVIEW_RENDER_CHUNK; if (iterations > ifsD->preview_iterations) iterations = ifsD->preview_iterations; for (i=0;ipreview_data,NULL,NULL,TRUE); for (i=0;iarea->allocation.width, ifsDesign->area->allocation.height, ifsvals.center_x, ifsvals.center_y); ifsD->preview_iterations -= iterations; for (i = 0; i < height; i++) gtk_preview_draw_row (GTK_PREVIEW (ifsD->preview), ifsD->preview_data + i * width * 3, 0, i, width); gtk_widget_draw (ifsD->preview, NULL); return (ifsD->preview_iterations != 0); } static void ifs_compose_preview_callback (GtkWidget *widget, GtkWidget *preview) { /* Expansion isn't really supported for previews */ gint i; gint width = GTK_WIDGET(ifsD->preview)->requisition.width; gint height = GTK_WIDGET(ifsD->preview)->requisition.height; guchar rc,gc,bc; guchar *ptr; GimpRGB color; if (!ifsD->preview_data) ifsD->preview_data = g_new (guchar, 3 * width * height); gimp_palette_get_background (&color); gimp_rgb_get_uchar (&color, &rc, &gc, &bc); ptr = ifsD->preview_data; for (i=0; i < width * height; i++) { *ptr++ = rc; *ptr++ = gc; *ptr++ = bc; } if (ifsD->preview_iterations == 0) gtk_idle_add ((GtkFunction)preview_idle_render, NULL); ifsD->preview_iterations = ifsvals.iterations*((gdouble)width*height/ (ifsD->drawable_width*ifsD->drawable_height)); } static void ifs_compose_ok_callback (GtkWidget *widget, GtkWidget *window) { ifscint.run = TRUE; gtk_widget_destroy (window); }