/* * This is a plug-in for the GIMP. * * Split up images according to a given formula. * * Copyright (C) 1999, 2000 Maurits Rijk lpeek.mrijk@consunet.nl * * 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. * */ #include #include #include #include #include "gtk/gtk.h" #include "libgimp/gimp.h" #include "libgimp/gimpui.h" #include "libgimp/gimpintl.h" #include "expression.h" #include "options.h" extern Expression_t *parse_formula(const char*, guint, guint); /* Declare local functions. */ static void query (void); static void run (char *name, int nparams, GimpParam *param, int *nreturn_vals, GimpParam **return_vals); static gint dialog (gint32 image_ID, GimpDrawable *drawable); static gint32 doit(GimpDrawable *drawable); GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; typedef enum { KEEP_LAYER1 = 1, KEEP_LAYER2 = 2, KEEP_BOTH_LAYERS = 3 } SplitMode_t; static guint _width, _height; /* picture width & height */ static gint _layer1_x; static gint _layer1_y; static gint _layer1_rot; static gint _layer2_x; static gint _layer2_y; static gint _layer2_rot; static SplitMode_t _split_mode = KEEP_BOTH_LAYERS; static gboolean _merge; static Expression_t *_split_func; typedef struct { GtkWidget *dlg; GtkWidget *func; GtkWidget *frame1; GtkWidget *layer1_x; GtkWidget *layer1_y; GtkWidget *layer1_rot; GtkWidget *frame2; GtkWidget *layer2_x; GtkWidget *layer2_y; GtkWidget *layer2_rot; GtkWidget *merge; } DialogData_t; static gint run_flag = FALSE; static gchar *_data_id = N_("splitter_formula"); 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"}, {GIMP_PDB_STRING, "function", "f(x, y)"}, {GIMP_PDB_INT32, "layer1_x", "Translate x layer 1"}, {GIMP_PDB_INT32, "layer1_y", "Translate y layer 1"}, {GIMP_PDB_INT32, "layer1_rot", "Rotate layer 1"}, {GIMP_PDB_INT32, "layer2_x", "Translate x layer 2"}, {GIMP_PDB_INT32, "layer2_y", "Translate y layer 2"}, {GIMP_PDB_INT32, "layer2_rot", "Rotate layer 2"}, {GIMP_PDB_INT32, "keep_layer", "Keep layer (1 = Layer 1, 2 = Layer 2, 3 = Both layers"}, {GIMP_PDB_INT32, "merge_visible_layers", "Merge visible layers (FALSE/TRUE)"} }; static GimpParamDef return_vals[] = { {GIMP_PDB_IMAGE, "image", "Output image"}, }; static int nargs = sizeof (args) / sizeof (args[0]); static int nreturn_vals = sizeof(return_vals) / sizeof(return_vals[0]); gimp_install_procedure ("plug_in_splitter", _("Splits an image."), _("Splits an image in separate parts using a " "formula of the form f(x, y) = 0"), "Maurits Rijk", "Maurits Rijk", "1999", N_("/Filters/Generic/Splitter..."), "RGB*", GIMP_PLUGIN, nargs, nreturn_vals, args, return_vals); } static void run (char *name, int n_params, GimpParam *param, int *nreturn_vals, GimpParam **return_vals) { static GimpParam values[2]; GimpDrawable *drawable; gint32 image_ID; gint32 new_image_ID = -1; GimpRunModeType run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; *return_vals = values; /* INIT_I18N_UI(); */ run_mode = param[0].data.d_int32; image_ID = param[1].data.d_int32; drawable = gimp_drawable_get (param[2].data.d_drawable); _width = gimp_image_width(image_ID); _height = gimp_image_height(image_ID); if (run_mode == GIMP_RUN_INTERACTIVE) { if (!dialog (image_ID, drawable)) { /* The dialog was closed, or something similarly evil happened. */ status = GIMP_PDB_EXECUTION_ERROR; } } else if (run_mode == GIMP_RUN_NONINTERACTIVE) { if (n_params != 12) { status = GIMP_PDB_CALLING_ERROR; } else { _split_func = parse_formula(param[3].data.d_string, _width, _height); _layer1_x = param[4].data.d_int32; _layer1_y = param[5].data.d_int32; _layer1_rot = param[6].data.d_int32; _layer2_x = param[7].data.d_int32; _layer2_y = param[8].data.d_int32; _layer2_rot = param[9].data.d_int32; _split_mode = param[10].data.d_int32; _merge = param[11].data.d_int32; } } if (status == GIMP_PDB_SUCCESS) { /* Make sure that the drawable is gray or RGB color */ if (gimp_drawable_is_rgb (drawable->id)) { gimp_progress_init (_("Splitting image...")); new_image_ID = doit (drawable); (void) gimp_display_new(new_image_ID); if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush (); } else { status = GIMP_PDB_EXECUTION_ERROR; } gimp_drawable_detach (drawable); } if (image_ID != -1) { *nreturn_vals = 2; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; values[1].type = GIMP_PDB_IMAGE; values[1].data.d_image = new_image_ID; } } static double split_func(int x, int y) { return expression_eval(_split_func, (double) x, (double) y); } static gint32 doit (GimpDrawable * drawable) { gint32 image_id = gimp_drawable_image_id(drawable->id); gint32 new_image_id; gint32 layer1 = -1, layer2 = -1; guint width, height; int i, j; GimpPixelRgn srcPR; GimpPixelRgn destPR1; GimpPixelRgn destPR2; gint bytes, bpp; gboolean has_alpha; guchar *src; guchar *dest1 = NULL; guchar *dest2 = NULL; GimpDrawable *ldrawable1; GimpDrawable *ldrawable2; GimpParam *return_vals; int nreturn_vals; width = gimp_image_width(image_id); height = gimp_image_height(image_id); has_alpha = gimp_drawable_has_alpha(drawable->id); bpp = gimp_drawable_bpp(drawable->id); bytes = (has_alpha) ? bpp : bpp + 1; new_image_id = gimp_image_new(width, height, gimp_image_base_type(image_id)); if (_split_mode & KEEP_LAYER1) { layer1 = gimp_layer_new(new_image_id, "layer_one", width, height, GIMP_RGB_IMAGE, 100, GIMP_NORMAL_MODE); gimp_layer_add_alpha(layer1); gimp_image_add_layer(new_image_id, layer1, 0); gimp_layer_translate(layer1, _layer1_x, _layer1_y); ldrawable1 = gimp_drawable_get(layer1); dest1 = malloc(width * bytes); gimp_pixel_rgn_init (&destPR1, ldrawable1, 0, 0, width, height, FALSE, FALSE); } if (_split_mode & KEEP_LAYER2) { layer2 = gimp_layer_new(new_image_id, "layer_two", width, height, GIMP_RGB_IMAGE, 100, GIMP_NORMAL_MODE); gimp_layer_add_alpha(layer2); gimp_image_add_layer(new_image_id, layer2, 0); gimp_layer_translate(layer2, _layer2_x, _layer2_y); ldrawable2 = gimp_drawable_get(layer2); dest2 = malloc(width * bytes); gimp_pixel_rgn_init (&destPR2, ldrawable2, 0, 0, width, height, FALSE, FALSE); } src = malloc(width * drawable->bpp); gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE); if (_split_mode == KEEP_LAYER1) { for (i = 0; i < height; i++) { guchar *p = dest1; guchar *s = src; gimp_pixel_rgn_get_row (&srcPR, src, 0, i, width); for (j = 0; j < width; j++) { if (split_func(j, i) < 0) { *p++ = *s++; *p++ = *s++; *p++ = *s++; *p++ = (has_alpha) ? *s++ : 255; } else { *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 0; s += bpp; } } gimp_pixel_rgn_set_row(&destPR1, dest1, 0, i, width); gimp_progress_update((double) i / (double) height); } } else if (_split_mode == KEEP_LAYER2) { for (i = 0; i < height; i++) { guchar *p = dest2; guchar *s = src; gimp_pixel_rgn_get_row(&srcPR, src, 0, i, width); for (j = 0; j < width; j++) { if (split_func(j, i) < 0) { *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 0; s += bpp; } else { *p++ = *s++; *p++ = *s++; *p++ = *s++; *p++ = (has_alpha) ? *s++ : 255; } } gimp_pixel_rgn_set_row (&destPR2, dest2, 0, i, width); gimp_progress_update ((double) i / (double) height); } } else { /* KEEP_BOTH_LAYERS */ for (i = 0; i < height; i++) { guchar *p = dest1; guchar *q = dest2; guchar *s = src; gimp_pixel_rgn_get_row (&srcPR, src, 0, i, width); for (j = 0; j < width; j++) { if (split_func(j, i) < 0) { *p++ = *s++; *p++ = *s++; *p++ = *s++; *p++ = (has_alpha) ? *s++ : 255; *q++ = 0; *q++ = 0; *q++ = 0; *q++ = 0; } else { *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 0; *q++ = *s++; *q++ = *s++; *q++ = *s++; *q++ = (has_alpha) ? *s++ : 255; } } gimp_pixel_rgn_set_row (&destPR1, dest1, 0, i, width); gimp_pixel_rgn_set_row (&destPR2, dest2, 0, i, width); gimp_progress_update ((double) i / (double) height); } } if (_split_mode & KEEP_LAYER2) free(dest2); if (_split_mode & KEEP_LAYER1) free(dest1); free(src); if (_split_mode & KEEP_LAYER1 && _layer1_rot) { return_vals = gimp_run_procedure("gimp_rotate", &nreturn_vals, GIMP_PDB_DRAWABLE, layer1, GIMP_PDB_INT32, FALSE, GIMP_PDB_FLOAT, _layer1_rot / 180.0 * M_PI, GIMP_PDB_END); gimp_destroy_params(return_vals, nreturn_vals); } if (_split_mode & KEEP_LAYER2 && _layer2_rot) { return_vals = gimp_run_procedure("gimp_rotate", &nreturn_vals, GIMP_PDB_DRAWABLE, layer2, GIMP_PDB_INT32, FALSE, GIMP_PDB_FLOAT, _layer2_rot / 180.0 * M_PI, GIMP_PDB_END); gimp_destroy_params(return_vals, nreturn_vals); } if (_merge) { gint32 merged_id; guint width, height; merged_id = gimp_image_merge_visible_layers(new_image_id, GIMP_EXPAND_AS_NECESSARY); gimp_layer_set_offsets(merged_id, 0, 0); width = gimp_drawable_width(merged_id); height = gimp_drawable_height(merged_id); gimp_image_resize(new_image_id, width, height, 0, 0); } expression_destruct(_split_func); return new_image_id; } static void close_callback (GtkWidget *widget, gpointer data) { gtk_main_quit (); } static void ok_callback(GtkWidget *widget, DialogData_t *data) { gchar *formula = gtk_entry_get_text(GTK_ENTRY(data->func)); _split_func = parse_formula(formula, _width, _height); if (_split_func) { run_flag = TRUE; _layer1_x = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(data->layer1_x)); _layer1_y = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(data->layer1_x)); _layer1_rot = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(data->layer1_rot)); _layer2_x = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(data->layer2_x)); _layer2_y = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(data->layer2_y)); _layer2_rot = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(data->layer2_rot)); _merge = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->merge)); /* Remember formula for next call to plug-in */ gimp_set_data(_data_id, formula, strlen(formula) + 1); gtk_widget_destroy(data->dlg); } else { gtk_widget_grab_focus(data->func); gimp_message(_("The formula f(x, y) contains an error.")); } } static void options_callback(GtkWidget *widget, DialogData_t *data) { do_option_dialog(); } static GtkWidget* create_spin_button_in_table(GtkWidget *table, int row, int col, int value, int min, int max) { GtkObject *adj = gtk_adjustment_new(value, min, max, 1, 1, 1); GtkWidget *button = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1, 0); gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(button), TRUE); gtk_table_attach_defaults(GTK_TABLE(table), button, col, col + 1, row, row + 1); gtk_widget_show(button); return button; } static GtkWidget* create_label_in_table(GtkWidget *table, int row, int col, const char *text) { GtkWidget *label = gtk_label_new(text); gtk_table_attach_defaults(GTK_TABLE(table), label, col, col + 1, row, row + 1); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_widget_show(label); return label; } static void keep_both_layers_cb(GtkWidget *widget, DialogData_t *data) { if (GTK_WIDGET_STATE(widget) & GTK_STATE_SELECTED) { gtk_widget_set_sensitive(data->frame1, TRUE); gtk_widget_set_sensitive(data->frame2, TRUE); _split_mode = KEEP_BOTH_LAYERS; } } static void keep_layer1_cb(GtkWidget *widget, DialogData_t *data) { if (GTK_WIDGET_STATE(widget) & GTK_STATE_SELECTED) { gtk_widget_set_sensitive(data->frame1, TRUE); gtk_widget_set_sensitive(data->frame2, FALSE); _split_mode = KEEP_LAYER1; } } static void keep_layer2_cb(GtkWidget *widget, DialogData_t *data) { if (GTK_WIDGET_STATE(widget) & GTK_STATE_SELECTED) { gtk_widget_set_sensitive(data->frame1, FALSE); gtk_widget_set_sensitive(data->frame2, TRUE); _split_mode = KEEP_LAYER2; } } static gint dialog (gint32 image_ID, GimpDrawable *drawable) { DialogData_t *data = g_new(DialogData_t, 1); GtkWidget *dlg, *button; GtkWidget *table, *frame; GtkWidget *subtable; GtkWidget *hbbox, *hbox, *label; GSList *group; gchar **argv; gint argc; guint32 data_size; argc = 1; argv = g_new (gchar *, 1); argv[0] = g_strdup ("split"); gtk_init (&argc, &argv); gtk_rc_parse(gimp_gtkrc()); data->dlg = dlg = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (dlg), _("Splitter 0.2")); gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", (GtkSignalFunc) close_callback, NULL); /* Main interface */ table = gtk_table_new(4, 2, FALSE); gtk_container_set_border_width(GTK_CONTAINER(table), 10); gtk_table_set_row_spacings(GTK_TABLE(table), 10); gtk_table_set_col_spacings(GTK_TABLE(table), 10); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->vbox), table, TRUE, TRUE, 10); gtk_widget_show(table); hbox = gtk_hbox_new(FALSE, 1); gtk_table_attach_defaults(GTK_TABLE(table), hbox, 0, 2, 0, 1); gtk_widget_show(hbox); label = gtk_label_new(N_("f(x, y):")); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10); gtk_widget_show(label); data->func = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(hbox), data->func, TRUE, TRUE, 10); gtk_widget_show(data->func); gtk_widget_grab_focus(data->func); data_size = gimp_get_data_size(_data_id); if (data_size) { gchar *formula = g_new(gchar, data_size); gimp_get_data(_data_id, formula); gtk_entry_set_text(GTK_ENTRY(data->func), formula); g_free(formula); } label = gtk_label_new(N_("= 0")); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10); gtk_widget_show(label); /* Frame for layer 1 */ data->frame1 = frame = gtk_frame_new(_("Layer 1")); gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 1, 1, 2); gtk_widget_show(frame); subtable = gtk_table_new(3, 3, FALSE); gtk_container_set_border_width(GTK_CONTAINER(subtable), 10); gtk_table_set_row_spacings(GTK_TABLE(subtable), 10); gtk_table_set_col_spacings(GTK_TABLE(subtable), 10); gtk_container_add(GTK_CONTAINER(frame), subtable); gtk_widget_show(subtable); (void) create_label_in_table(subtable, 0, 0, _("Translate x:")); data->layer1_x = create_spin_button_in_table(subtable, 0, 1, 0, G_MININT, G_MAXINT); (void) create_label_in_table(subtable, 0, 2, _("Pixels")); (void) create_label_in_table(subtable, 1, 0, _("Translate y:")); data->layer1_y = create_spin_button_in_table(subtable, 1, 1, 0, G_MININT, G_MAXINT); (void) create_label_in_table(subtable, 1, 2, _("Pixels")); (void) create_label_in_table(subtable, 2, 0, _("Rotate:")); data->layer1_rot = create_spin_button_in_table(subtable, 2, 1, 0, -360, 360); (void) create_label_in_table(subtable, 2, 2, _("Degrees")); /* Frame for layer 2 */ data->frame2 = frame = gtk_frame_new(_("Layer 2")); gtk_table_attach_defaults(GTK_TABLE(table), frame, 1, 2, 1, 2); gtk_widget_show(frame); subtable = gtk_table_new(3, 3, FALSE); gtk_container_set_border_width(GTK_CONTAINER(subtable), 10); gtk_table_set_row_spacings(GTK_TABLE(subtable), 10); gtk_table_set_col_spacings(GTK_TABLE(subtable), 10); gtk_container_add(GTK_CONTAINER(frame), subtable); gtk_widget_show(subtable); (void) create_label_in_table(subtable, 0, 0, _("Translate x:")); data->layer2_x = create_spin_button_in_table(subtable, 0, 1, 0, G_MININT, G_MAXINT); (void) create_label_in_table(subtable, 0, 2, _("Pixels")); (void) create_label_in_table(subtable, 1, 0, _("Translate y:")); data->layer2_y = create_spin_button_in_table(subtable, 1, 1, 0, G_MININT, G_MAXINT); (void) create_label_in_table(subtable, 1, 2, _("Pixels")); (void) create_label_in_table(subtable, 2, 0, _("Rotate:")); data->layer2_rot = create_spin_button_in_table(subtable, 2, 1, 0, -360, 360); (void) create_label_in_table(subtable, 2, 2, _("Degrees")); /* Options */ hbox = gtk_hbox_new(FALSE, 1); gtk_table_attach_defaults(GTK_TABLE(table), hbox, 0, 2, 2, 3); gtk_widget_show(hbox); button = gtk_radio_button_new_with_label(NULL, _("Keep Both Layers")); gtk_container_add(GTK_CONTAINER(hbox), button); gtk_widget_show(button); gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) keep_both_layers_cb, data); group = gtk_radio_button_group(GTK_RADIO_BUTTON(button)); button = gtk_radio_button_new_with_label(group, _("Keep Layer 1")); gtk_container_add(GTK_CONTAINER(hbox), button); gtk_widget_show(button); gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) keep_layer1_cb, data); group = gtk_radio_button_group(GTK_RADIO_BUTTON(button)); button = gtk_radio_button_new_with_label(group, _("Keep Layer 2")); gtk_container_add(GTK_CONTAINER(hbox), button); gtk_widget_show(button); gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) keep_layer2_cb, data); data->merge = gtk_check_button_new_with_label(_("Merge Visible Layers")); gtk_table_attach_defaults(GTK_TABLE(table), data->merge, 0, 1, 3, 4); gtk_widget_show(data->merge); button = gtk_button_new_with_label(_("Advanced Options...")); gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) options_callback, data); gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, 3, 4); gtk_widget_show(button); /* Action area */ gtk_container_set_border_width (GTK_CONTAINER ( GTK_DIALOG (dlg)->action_area), 2); gtk_box_set_homogeneous (GTK_BOX (GTK_DIALOG (dlg)->action_area), FALSE); hbbox = gtk_hbutton_box_new (); gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbbox), 4); gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dlg)->action_area), hbbox, FALSE, FALSE, 0); gtk_widget_show (hbbox); button = gtk_button_new_with_label (_("OK")); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_signal_connect (GTK_OBJECT (button), "clicked", (GtkSignalFunc) ok_callback, data); gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0); gtk_widget_grab_default (button); gtk_widget_show (button); button = gtk_button_new_with_label (_("Cancel")); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (dlg)); gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0); gtk_widget_show (button); gtk_widget_show (dlg); gtk_main (); gdk_flush (); return run_flag; }