/*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*\ | IFS 1.0 | | Plug-in for GIMP | | Copyright François Gingras | | July 9th, 2010 | | | | Use & distribute as you please, | | but please give credit! Thanks! | \*__________________________________*/ #define IFS_SETTINGS_FILE "ifs.txt" #define IFS_TEXT_LENGTH 128 #include #include #include #include #include // ----------------- GLOBAL DECLARATIONS ---------------------- GtkWidget *checkButtonSupersampling, *checkButtonSpecifyTargetSize, *spinbuttonTargetSizeH, *spinbuttonTargetSizeV; GtkObject *spinbuttonTargetSizeH_adj, *spinbuttonTargetSizeV_adj; gint32 id; gboolean faux = FALSE, vrai = TRUE; typedef struct { gint nbFunctions; gint nbIterations; gint targetWidth, targetHeight; gdouble *a, *b, *c, *d, *e, *f; char ifsName[IFS_TEXT_LENGTH]; gboolean supersampling; gboolean specifyTargetSize; gboolean useCartesianCoordinates; gboolean resizeAfterEachIteration; } Parameters; static Parameters bvals = { 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, FALSE, "", FALSE, FALSE, FALSE, TRUE }; // ----------------- FUNCTION DECLARATIONS ----------------------- static void query (void); static gboolean ifs_dialog (GimpDrawable *drawable); static void run ( const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); static void ifs (GimpDrawable *drawable); static void setDefaultParameters(); static void freeAllocatedMemory(); static void allocateMemoryFunctions(); static void saveOptionsLastValues(); static gboolean loadOptionsLastValues(); static void callback_comboBoxifsSelection(GtkComboBox *combo, gboolean *widthOrHeight); static void callback_supersampling(GtkWidget *widget, gboolean *supersampling); static void callback_specifyTargetSize(GtkWidget *widget, gboolean *specifyTargetSize); static void callback_updateTargetSize(GtkWidget *widget, gboolean *widthOrHeight); double minimum (double a, double b); double maximum (double a, double b); double arrondir (double a); void getContractionRatio (gint hOrig, gint vOrig, double *hRatio, double *vRatio, gint *final_width, gint *final_height); // ----------------- MACROS AND STUFF ---------------------------- GimpPlugInInfo PLUG_IN_INFO = { NULL, NULL, query, run }; MAIN() static void query (void) { static GimpParamDef args[] = { { GIMP_PDB_INT32, "run-mode", "Run mode" }, { GIMP_PDB_IMAGE, "image", "Input image" }, { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" } }; gimp_install_procedure ( "plug-in-ifs", "IFS", "Generates the result of a finite number of iterations of an IFS over an image", "François Gingras", "Copyright François Gingras", "2010", "_IFS...", "RGB*, GRAY*", GIMP_PLUGIN, G_N_ELEMENTS (args), 0, args, NULL); gimp_plugin_menu_register ( "plug-in-ifs", "/Filters/Misc"); } // ----------------- MAIN RUN FUNCTION --------------------------- static void run ( const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[1]; GimpPDBStatusType status = GIMP_PDB_SUCCESS; GimpRunMode run_mode; GimpDrawable *drawable; *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; run_mode = param[0].data.d_int32; drawable = gimp_drawable_get (param[2].data.d_drawable); if(!loadOptionsLastValues()) setDefaultParameters(); switch (run_mode) { case GIMP_RUN_INTERACTIVE: if (! ifs_dialog (drawable)) return; break; default: break; } ifs(drawable); gimp_displays_flush (); gimp_drawable_detach (drawable); freeAllocatedMemory(); } // ----------------- MAIN PLUGIN --------------------------- static void ifs (GimpDrawable *drawable) { gint x1, y1, x2, y2; gint width, height, final_width, final_height; gint channels; gint i, j; gint32 image; gint32 layers[bvals.nbFunctions]; gint32 retvals; const GimpParam *params; char *drawableName, progressString[40]; gdouble hRatio, vRatio; // Get the useful constants id = drawable->drawable_id; image = gimp_drawable_get_image(id); gimp_image_undo_group_start(image); drawableName = gimp_drawable_get_name(id); if(TRUE) id = gimp_image_merge_visible_layers(image, GIMP_EXPAND_AS_NECESSARY); if(bvals.specifyTargetSize) { gimp_drawable_mask_bounds (id, &x1, &y1, &x2, &y2); width = x2 - x1; height = y2 - y1; getContractionRatio (width, height, &hRatio, &vRatio, &final_width, &final_height); final_width = bvals.targetWidth; final_height = bvals.targetHeight; width = final_width / pow(hRatio, (double)bvals.nbIterations); height = final_height / pow(vRatio, (double)bvals.nbIterations); gimp_layer_scale(id, width, height, FALSE); } channels = gimp_drawable_bpp (id); gimp_drawable_get_name(id); if(bvals.useCartesianCoordinates) { gimp_image_flip(image, GIMP_ORIENTATION_VERTICAL); } if(!bvals.resizeAfterEachIteration) { gimp_drawable_mask_bounds (id, &x1, &y1, &x2, &y2); width = x2 - x1; height = y2 - y1; } for(i=0; i 1) id = gimp_image_merge_down(image, layers[bvals.nbFunctions-1], GIMP_EXPAND_AS_NECESSARY); else id = layers[0]; if(bvals.resizeAfterEachIteration) { params = gimp_run_procedure ("plug_in_autocrop_layer", &retvals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, image, GIMP_PDB_DRAWABLE, id, GIMP_PDB_END); gimp_image_resize_to_layers(image); } } if(bvals.useCartesianCoordinates) { gimp_image_flip(image, GIMP_ORIENTATION_VERTICAL); } if(!bvals.resizeAfterEachIteration) { params = gimp_run_procedure ("plug_in_autocrop_layer", &retvals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, image, GIMP_PDB_DRAWABLE, id, GIMP_PDB_END); gimp_image_resize_to_layers(image); } gimp_drawable_set_name(id, drawableName); gimp_image_undo_group_end(image); saveOptionsLastValues(); } // ----------------- MAIN DIALOG --------------------------- static gboolean ifs_dialog (GimpDrawable *drawable) { GtkWidget *dialog; GtkWidget *vbox_main; GtkWidget *hbox_iterations, *hbox_ifs_selection, *hbox_target_size, *hbox_supersampling, *vbox_options; GtkWidget *frame_iterations, *frame_ifs_selection, *frame_options; GtkWidget *alignment_iterations, *alignment_ifs_selection, *alignment_options; GtkWidget *spinbuttonIterations, *comboBoxifsSelection; GtkObject *spinbuttonIterations_adj; GtkWidget *frame_label_iterations, *frame_label_ifs_selection, *frame_label_options, *label_times, *label_pixels; gboolean run, widthOrHeight = FALSE; char *predefinedIFS[] = { "Sierpinski triangle", "Sierpinski carpet", "Pythagoras tree", "Hex flake", "T-square", "Self affine set", "Cantor dust", "Vicsek snowflake", "Pentaflake" }; gint nb_predefined_ifs = 9, i; // General id = drawable->drawable_id; gimp_ui_init ("ui_ifs", FALSE); dialog = gimp_dialog_new ( "IFS", "ui_ifs", NULL, 0, gimp_standard_help_func, "plug-in-ifs", GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); vbox_main = gtk_vbox_new (FALSE, 6); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox_main); // Iterations frame_iterations = gtk_frame_new (NULL); gtk_box_pack_start (GTK_BOX (vbox_main), frame_iterations, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (frame_iterations), 5); frame_label_iterations = gtk_label_new ("Number of iterations"); gtk_frame_set_label_widget (GTK_FRAME (frame_iterations), frame_label_iterations); gtk_label_set_use_markup (GTK_LABEL (frame_label_iterations), TRUE); alignment_iterations = gtk_alignment_new (0, 0, 0, 0); gtk_container_add (GTK_CONTAINER (frame_iterations), alignment_iterations); gtk_alignment_set_padding (GTK_ALIGNMENT (alignment_iterations), 5, 15, 15, 5); hbox_iterations = gtk_hbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (alignment_iterations), hbox_iterations); spinbuttonIterations_adj = gtk_adjustment_new (bvals.nbIterations, 1, 99, 1, 5, 0); g_signal_connect_after (spinbuttonIterations_adj, "value_changed", G_CALLBACK (callback_updateTargetSize), &widthOrHeight); spinbuttonIterations = gtk_spin_button_new (GTK_ADJUSTMENT (spinbuttonIterations_adj), 1, 0); // Climb rate, number of decimals gtk_box_pack_start (GTK_BOX (hbox_iterations), spinbuttonIterations, TRUE, FALSE, 5); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbuttonIterations), TRUE); // Only numeric values allowed g_signal_connect (spinbuttonIterations_adj, "value_changed", G_CALLBACK (gimp_int_adjustment_update), &bvals.nbIterations); // Fractal selection frame_ifs_selection = gtk_frame_new (NULL); gtk_box_pack_start (GTK_BOX (vbox_main), frame_ifs_selection, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (frame_ifs_selection), 5); frame_label_ifs_selection = gtk_label_new ("Choose your IFS"); gtk_frame_set_label_widget (GTK_FRAME (frame_ifs_selection), frame_label_ifs_selection); gtk_label_set_use_markup (GTK_LABEL (frame_label_ifs_selection), TRUE); alignment_ifs_selection = gtk_alignment_new (0, 0, 0, 0); gtk_container_add (GTK_CONTAINER (frame_ifs_selection), alignment_ifs_selection); gtk_alignment_set_padding (GTK_ALIGNMENT (alignment_ifs_selection), 5, 15, 15, 5); hbox_ifs_selection = gtk_hbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (alignment_ifs_selection), hbox_ifs_selection); comboBoxifsSelection = gtk_combo_box_new_text (); for(i=0; iactive) *supersampling = TRUE; else *supersampling = FALSE; } void callback_specifyTargetSize (GtkWidget *widget, gboolean *specifyTargetSize) { if (GTK_TOGGLE_BUTTON (widget)->active) { *specifyTargetSize = TRUE; gtk_widget_set_sensitive(spinbuttonTargetSizeH, TRUE); gtk_widget_set_sensitive(spinbuttonTargetSizeV, TRUE); } else { *specifyTargetSize = FALSE; gtk_widget_set_sensitive(spinbuttonTargetSizeH, FALSE); gtk_widget_set_sensitive(spinbuttonTargetSizeV, FALSE); } } void callback_updateTargetSize (GtkWidget *widget, gboolean *widthOrHeight) { gint x1, x2, y1, y2, width, height, final_width, final_height; double hRatio, vRatio; gint newWidth, newHeight; gimp_drawable_mask_bounds (id, &x1, &y1, &x2, &y2); width = x2 - x1; height = y2 - y1; getContractionRatio (width, height, &hRatio, &vRatio, &final_width, &final_height); if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (checkButtonSpecifyTargetSize))) { if(*widthOrHeight) // C'est le height qui a été modifié par l'usager { newHeight = gtk_adjustment_get_value(GTK_ADJUSTMENT(spinbuttonTargetSizeV_adj)); newWidth = (((double)newHeight / pow(vRatio, (double)bvals.nbIterations)) / height) * width * pow(hRatio, (double)bvals.nbIterations); g_signal_handlers_block_by_func(spinbuttonTargetSizeH_adj, G_CALLBACK(callback_updateTargetSize), &faux); gtk_adjustment_set_value(GTK_ADJUSTMENT(spinbuttonTargetSizeH_adj), newWidth); gimp_int_adjustment_update (GTK_ADJUSTMENT(spinbuttonTargetSizeH_adj), &newWidth); g_signal_handlers_unblock_by_func(spinbuttonTargetSizeH_adj, G_CALLBACK(callback_updateTargetSize), &faux); } else { newWidth = gtk_adjustment_get_value(GTK_ADJUSTMENT(spinbuttonTargetSizeH_adj)); newHeight = (((double)newWidth / pow(hRatio, (double)bvals.nbIterations)) / width) * height * pow(vRatio, (double)bvals.nbIterations); g_signal_handlers_block_by_func(spinbuttonTargetSizeV_adj, G_CALLBACK(callback_updateTargetSize), &vrai); gtk_adjustment_set_value(GTK_ADJUSTMENT(spinbuttonTargetSizeV_adj), newHeight); gimp_int_adjustment_update (GTK_ADJUSTMENT(spinbuttonTargetSizeV_adj), &newHeight); g_signal_handlers_unblock_by_func(spinbuttonTargetSizeV_adj, G_CALLBACK(callback_updateTargetSize), &vrai); } bvals.targetWidth = newWidth; bvals.targetHeight = newHeight; } else { gtk_adjustment_set_value(GTK_ADJUSTMENT(spinbuttonTargetSizeH_adj), final_width); gtk_adjustment_set_value(GTK_ADJUSTMENT(spinbuttonTargetSizeV_adj), final_height); gimp_int_adjustment_update (GTK_ADJUSTMENT(spinbuttonTargetSizeH_adj), &final_width); gimp_int_adjustment_update (GTK_ADJUSTMENT(spinbuttonTargetSizeV_adj), &final_height); bvals.targetWidth = final_width; bvals.targetHeight = final_height; } } double minimum (double a, double b) { return a>b?b:a; } double maximum (double a, double b) { return a>b?a:b; } double arrondir (double a) { double b = floor(a); return a-b>=0.5?b+1:b; } void getContractionRatio (gint hOrig, gint vOrig, double *hRatio, double *vRatio, gint *final_width, gint *final_height) { gint i; gdouble x1, x2, y1, y2; gdouble x_min = 9999999, x_max = 0, y_min = 9999999, y_max = 0; for(i=0; i