#include #include #include #include #include #include "gtk/gtk.h" #include "libgimp/gimp.h" #include "libgimp/gimpui.h" #define PLUG_IN_NAME "shapeit" #define PLUG_IN_PRINT_NAME "ShapeIt" #define PLUG_IN_VERSION "v0.5 990606" #define PLUG_IN_MENU_PATH "/Filters/Map/Shape It" #define PLUG_IN_IMAGE_TYPES "RGBA, GRAYA" #define PLUG_IN_AUTHOR "Claes G Lindblad " #define PLUG_IN_COPYRIGHT "Claes G Lindblad " #define PLUG_IN_DESCRIPTION "Shapes active layer according to information from a black & white map." #define PLUG_IN_HELP "Create a white map layer and fill the shape you need with black. Then use this map layer to displace any other layer. Please note that both must have alpha." #define NUMBER_IN_ARGS 6 #define IN_ARGS { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},\ { GIMP_PDB_IMAGE, "image", "Input image" },\ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable"},\ { GIMP_PDB_DRAWABLE, "shape_map_y", "Displacement map for Y direction" },\ { GIMP_PDB_INT32, "offset", "Offset amount." },\ { GIMP_PDB_INT32, "invert", "Inversion" } #define NUMBER_OUT_ARGS 0 #define OUT_ARGS NULL #define ENTRY_WIDTH 40 #define TILE_CACHE_SIZE 48 typedef struct { gint shape_map_y; gint offset; gint invert; } ShapeVals; typedef struct { GtkWidget *menu_y; GtkWidget *offset; GtkWidget *invert; gint run; } ShapeInterface; /* * Function prototypes. */ static void query (void); static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); static void shape (GimpDrawable *drawable); static gint shape_dialog (GimpDrawable *drawable); static gint shape_map_constrain (gint32 image_id, gint32 drawable_id, gpointer data); static void shape_map_y_callback (gint32 id, gpointer data); static void shape_close_callback (GtkWidget *widget, gpointer data); static void shape_ok_callback (GtkWidget *widget, gpointer data); static void shape_entry_callback (GtkWidget *widget, gpointer data); static void invert_toggle_update (GtkWidget *widget, gpointer data); /***** Local vars *****/ GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; static ShapeVals dvals = { -1, /* shape_map_y */ 0, /* offset */ FALSE /* invert */ }; static ShapeInterface dint = { NULL, /* menu_y */ NULL, /* offset */ NULL, /* invert */ FALSE /* run */ }; /***** Functions *****/ MAIN () static void query () { static GimpParamDef args[] = { IN_ARGS }; static gint nargs = NUMBER_IN_ARGS; static GimpParamDef *return_vals = OUT_ARGS; static gint nreturn_vals = NUMBER_OUT_ARGS; /* the actual installation of the plugin */ gimp_install_procedure (PLUG_IN_NAME, PLUG_IN_DESCRIPTION, PLUG_IN_HELP, PLUG_IN_AUTHOR, PLUG_IN_COPYRIGHT, PLUG_IN_VERSION, PLUG_IN_MENU_PATH, PLUG_IN_IMAGE_TYPES, GIMP_PLUGIN, nargs, 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 *drawable; GimpRunModeType run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; /* Get the run mode from the in-parameters */ run_mode = param[0].data.d_int32; /* Get the specified drawable */ drawable = gimp_drawable_get (param[2].data.d_drawable); *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; switch (run_mode) { case GIMP_RUN_INTERACTIVE: /* Possibly retrieve data */ gimp_get_data (PLUG_IN_NAME, &dvals); /* First acquire information with a dialog */ if (! shape_dialog (drawable)) return; break; case GIMP_RUN_NONINTERACTIVE: /* Make sure all the arguments are there! */ if (nparams != NUMBER_IN_ARGS) status = GIMP_PDB_CALLING_ERROR; if (status == GIMP_PDB_SUCCESS) { dvals.shape_map_y = param[3].data.d_int32; dvals.offset = param[4].data.d_int32; dvals.invert = param[5].data.d_int32; } break; case GIMP_RUN_WITH_LAST_VALS: /* Possibly retrieve data */ gimp_get_data (PLUG_IN_NAME, &dvals); break; default: break; } if (status == GIMP_PDB_SUCCESS) { gimp_progress_init ("Shaping..."); /* set the tile cache size */ gimp_tile_cache_ntiles (TILE_CACHE_SIZE); /* run the shape effect */ shape (drawable); if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush (); /* Store data */ if (run_mode == GIMP_RUN_INTERACTIVE) gimp_set_data (PLUG_IN_NAME, &dvals, sizeof (ShapeVals)); } values[0].data.d_status = status; gimp_drawable_detach (drawable); } static int shape_dialog (GimpDrawable *drawable) { GtkWidget *dlg; GtkWidget *button; GtkWidget *button3; GtkWidget *frame; GtkWidget *label; GtkWidget *table; GtkWidget *entry; GtkWidget *option_menu; GtkWidget *menu; gchar buffer[32]; gchar **argv; gint argc; argc = 1; argv = g_new (gchar *, 1); argv[0] = g_strdup ("shape"); gtk_init (&argc, &argv); gtk_rc_parse(gimp_gtkrc()); dlg = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (dlg), "Shape It"); gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", (GtkSignalFunc) shape_close_callback, NULL); /* Action area */ button = gtk_button_new_with_label ("OK"); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_signal_connect (GTK_OBJECT (button), "clicked", (GtkSignalFunc) shape_ok_callback, dlg); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 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 (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0); gtk_widget_grab_default (button); gtk_widget_show (button); /* The main table */ frame = gtk_frame_new ("Which layer to use as Shape Map on active layer?"); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_border_width (GTK_CONTAINER (frame), 10); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0); table = gtk_table_new (3, 3, FALSE); gtk_container_border_width (GTK_CONTAINER (table), 5); gtk_container_add (GTK_CONTAINER (frame), table); gtk_table_set_row_spacing (GTK_TABLE (table), 0, 10); gtk_table_set_row_spacing (GTK_TABLE (table), 1, 10); gtk_table_set_col_spacing (GTK_TABLE (table), 0, 10); gtk_table_set_col_spacing (GTK_TABLE (table), 1, 10); /* label */ label = gtk_label_new("Offset amount"); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 4, 0); gtk_widget_show(label); /* invert */ button3 = gtk_check_button_new_with_label("Invert shape"); gtk_table_attach(GTK_TABLE(table), button3, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (button3), "toggled", (GtkSignalFunc) invert_toggle_update, &dvals.invert); /********************* for Gimp 1.1.x, use this line: gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button3), dvals.invert); *********************/ /********************* for Gimp 1.0.x, use this line: gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button3), dvals.invert); *********************/ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button3), dvals.invert); gtk_widget_show(button3); /* offset */ entry = gtk_entry_new (); gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_set_usize (entry, ENTRY_WIDTH, 0); sprintf (buffer, "%d", dvals.offset); gtk_entry_set_text (GTK_ENTRY (entry), buffer); gtk_signal_connect (GTK_OBJECT (entry), "changed", (GtkSignalFunc) shape_entry_callback, &dvals.offset); gtk_widget_show (entry); /* menu_y */ dint.menu_y = option_menu = gtk_option_menu_new (); gtk_table_attach (GTK_TABLE (table), option_menu, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); menu = gimp_drawable_menu_new (shape_map_constrain, shape_map_y_callback, drawable, dvals.shape_map_y); gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu); gtk_widget_set_sensitive (dint.menu_y, TRUE); gtk_widget_show (option_menu); gtk_widget_show (table); gtk_widget_show (frame); gtk_widget_show (dlg); gtk_main (); gdk_flush (); return dint.run; } /* The shaping is done here. */ static void shape (GimpDrawable *drawable) { GimpDrawable *map_y; GimpPixelRgn mapPR, destPR, activePR; gint width, height, bytes; gint mapbytes; gint row, col; gint position; gint start; gint pixel, pixelA; guchar *map_col, *active_col, *temp_col; /* initialize */ row = col = 0; map_y = gimp_drawable_get(dvals.shape_map_y); /* Get the size of the drawable. */ width = drawable->width; height = drawable->height; bytes = drawable->bpp; mapbytes = map_y->bpp; if (bytes != mapbytes) { gimp_message("Active layer and Map layer must be of same type.\nDo you need to add Alpha channel to one of them?\n"); return; } gimp_pixel_rgn_init (&mapPR, map_y, 0, 0, width, height, FALSE, FALSE); gimp_pixel_rgn_init (&activePR, drawable, 0, 0, width, height, FALSE, FALSE); gimp_pixel_rgn_init (&destPR, drawable, 0, 0, width, height, TRUE, TRUE); map_col = (guchar *) g_malloc (height * bytes); active_col = (guchar *) g_malloc (height * bytes); temp_col = (guchar *) g_malloc (2 * height * bytes); for (col = 0; col < width; col++) { gimp_pixel_rgn_get_col (&mapPR, map_col, col, 0, height); row = 0; position = FALSE; while ((row < height) && (position == FALSE)) { pixel = map_col[row * bytes]; pixelA = map_col[row * bytes + bytes - 1]; if ((pixel == 0) && (pixelA == 255)) position = height - row; row++; } if (position == FALSE) position = 0; if (dvals.invert == TRUE) position = height - position; while (dvals.offset > height) { dvals.offset -= height; } while (dvals.offset < (height * -1)) { dvals.offset += height; } gimp_pixel_rgn_get_col(&activePR, active_col, col, 0, height); memcpy(temp_col, active_col, height * bytes); memcpy(temp_col + height * bytes, active_col, height * bytes); start = height + dvals.offset + position; start = start % height; gimp_pixel_rgn_set_col(&destPR, temp_col + start * bytes, col, 0, height); if ((col % 10) == 0) gimp_progress_update ((double) col / (double) width); } g_free (map_col); g_free (active_col); g_free (temp_col); gimp_progress_update ( 1.0 ); gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->id, TRUE); gimp_drawable_update(drawable->id, 0, 0, width, height); return; } /* Shape interface functions */ static gint shape_map_constrain (gint32 image_id, gint32 drawable_id, gpointer data) { GimpDrawable *drawable; drawable = (GimpDrawable *) data; if (drawable_id == -1) return TRUE; if (gimp_drawable_width (drawable_id) == drawable->width && gimp_drawable_height (drawable_id) == drawable->height) return TRUE; else return FALSE; } static void shape_map_y_callback (gint32 id, gpointer data) { dvals.shape_map_y = id; } static void shape_close_callback (GtkWidget *widget, gpointer data) { gtk_main_quit (); } static void shape_ok_callback (GtkWidget *widget, gpointer data) { dint.run = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); } static void invert_toggle_update (GtkWidget *widget, gpointer data) { int *toggle_val; toggle_val = (int *) data; if (GTK_TOGGLE_BUTTON (widget)->active) *toggle_val = TRUE; else *toggle_val = FALSE; } static void shape_entry_callback (GtkWidget *widget, gpointer data) { int *text_val; text_val = (int *) data; *text_val = atoi (gtk_entry_get_text (GTK_ENTRY (widget))); }