/* carpet.c * by Heinz Sollich * 06/1998 * * Modified for 1.2 and the Graphics Muse Tools CD * by Michael J. Hammel * 01/2002 * * makes a carpet-like structure * * some code adapted from canvas.c, noisify.c and others */ /* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include "gtk/gtk.h" #include "libgimp/gimp.h" #define ENTRY_WIDTH 30 #define SCALE_WIDTH 125 typedef struct { gint depth; gint noise; gint max_shift; gint repeats; } CarpetVals; typedef struct { gint run; } CarpetInterface; /* Declare local functions. */ static void query(void); static void run(char *name, int nparams, GimpParam * param, int *nreturn_vals, GimpParam ** return_vals); static void carpet(GimpDrawable * drawable); static gint carpet_dialog (); static void carpet_close_callback (GtkWidget *widget, gpointer data); static void carpet_ok_callback (GtkWidget *widget, gpointer data); static void carpet_scale_update (GtkAdjustment *adjustment, gint *scale_val); static void carpet_entry_update (GtkWidget *widget, gint *value); static void dialog_create_value (char *title, GtkTable *table, int row, gint *value, double left, double right); GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; static CarpetVals bvals = { 20, /* depth */ 10, /* noise */ 1, /* max_shift */ 2, /* repeats */ }; static CarpetInterface bint = { FALSE /* run */ }; MAIN() static void query() { static GimpParamDef args[] = { {GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"}, {GIMP_PDB_IMAGE, "image", "Input image (unused)"}, {GIMP_PDB_DRAWABLE, "drawable", "Input drawable"}, {GIMP_PDB_INT32, "carpet_depth", "Carpet depth"}, {GIMP_PDB_INT32, "carpet_noise", "Carpet noise"}, {GIMP_PDB_INT32, "carpet_max_shift", "Carpet max Shift"}, {GIMP_PDB_INT32, "carpet_repeats", "Carpet Shift repeats"}, }; static GimpParamDef *return_vals = NULL; static int nargs = sizeof(args) / sizeof(args[0]); static int nreturn_vals = 0; gimp_install_procedure("plug_in_carpet", "makes a carpet-like structure", "More Help", "Heinz Sollich ", "Heinz Sollich", "04/1998", "/Filters/Artistic/Apply Carpet", "RGB*, GRAY*", GIMP_PLUGIN, nargs, nreturn_vals, args, return_vals); } static void run(char *name, int nparams, GimpParam * param, int *nreturn_vals, GimpParam ** return_vals) { static GimpParam values[1]; GimpDrawable *drawable; GimpRunModeType run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; run_mode = param[0].data.d_int32; *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_carpet", &bvals); /* First acquire information with a dialog */ if (!carpet_dialog()) return; break; case GIMP_RUN_NONINTERACTIVE: /* Make sure all the arguments are there! */ if (nparams != 7) status = GIMP_PDB_CALLING_ERROR; if (status == GIMP_PDB_SUCCESS) { bvals.depth = param[3].data.d_int32; bvals.noise = param[4].data.d_int32; bvals.max_shift = param[5].data.d_int32; bvals.repeats = param[6].data.d_int32; } if (status == GIMP_PDB_SUCCESS && ((bvals.depth < 0) || (bvals.depth > 50) || (bvals.noise < 0) || (bvals.noise > 50) || (bvals.max_shift < 0) || (bvals.max_shift > 5) || (bvals.repeats < 0) || (bvals.repeats > 5))) status = GIMP_PDB_CALLING_ERROR; break; case GIMP_RUN_WITH_LAST_VALS: /* Possibly retrieve data */ gimp_get_data("plug_in_carpet", &bvals); break; default: break; } /* Get the specified drawable */ drawable = gimp_drawable_get(param[2].data.d_drawable); /* Make sure that the drawable is gray or RGB color */ if ((status == GIMP_PDB_SUCCESS) && (gimp_drawable_is_rgb(drawable->id) || gimp_drawable_is_gray(drawable->id))) { gimp_progress_init("Carpet"); gimp_tile_cache_ntiles(2 * (drawable->width / gimp_tile_width() + 1)); carpet(drawable); if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush(); /* Store data */ if (run_mode == GIMP_RUN_INTERACTIVE) gimp_set_data ("plug_in_carpet", &bvals, sizeof (CarpetVals)); } else { /* gimp_message ("carpet: cannot operate on indexed color images"); */ status = GIMP_PDB_EXECUTION_ERROR; } values[0].data.d_status = status; gimp_drawable_detach(drawable); } /* * Return a Gaussian (aka normal) random variable. (from noisify.c) */ static inline gdouble gauss () { gint i; gdouble sum = 0.0; for (i = 0; i < 4; i++) sum += rand () & 0x7FFF; return sum * 5.28596089837e-5 - 3.46410161514; } static void carpet(GimpDrawable *drawable) { GimpPixelRgn srcPR, destPR; gint width, height, sw, sh; gint bpp, bytes, has_alpha; guchar *dest, *d; guchar *cr; guchar *p, *p0, *p1, *p2, *p3; gint max_shift, shift; gint pvalue, noise, reps; gint row, col, i, n, m; gint x1, y1, x2, y2; gint v0[4] = { 2, 1, 0, 1 }, v1[4] = { 1, 0, -1, 0 }, v2[4] = { 0, -1, -2, -1 }, v3[4] = { 1, 0, -1, 0 }; float fmax; gimp_drawable_mask_bounds(drawable->id, &x1, &y1, &x2, &y2); sw = x2 - x1; sh = y2 - y1; width = drawable->width; height = drawable->height; bpp = drawable->bpp; has_alpha = gimp_drawable_has_alpha(drawable->id); bytes = bpp - has_alpha; n = bvals.depth; /* depth of the structure */ m = bvals.noise; /* noise */ max_shift = bvals.max_shift; /* max. shift */ fmax = 2.0 * max_shift + 1.0; reps = bvals.repeats; /* number of shifts */ srand (time (NULL)); /* initialize the pixel regions */ gimp_pixel_rgn_init(&srcPR, drawable, 0, 0, width, height, FALSE, FALSE); gimp_pixel_rgn_init(&destPR, drawable, 0, 0, width, height, TRUE, TRUE); /* make structure */ cr = (guchar *) malloc((width+4) * bpp); dest = (guchar *) malloc(width * bpp); for (row=y1; row 255) *d = 255; else *d = pvalue; d++; } if ( has_alpha ) { *d = cr[col * bpp + bytes]; d++; } } gimp_pixel_rgn_set_row(&destPR, dest+x1*bpp, x1, row, (x2-x1)); } for (row=y1+1; row 255) *d = 255; else *d = pvalue; d++; } if ( has_alpha ) { *d = cr[col * bpp + bytes]; d++; } } gimp_pixel_rgn_set_row(&destPR, dest+x1*bpp, x1, row, (x2-x1)); } for (row=y1+2; row 255) *d = 255; else *d = pvalue; d++; } if ( has_alpha ) { *d = cr[col * bpp + bytes]; d++; } } gimp_pixel_rgn_set_row(&destPR, dest+x1*bpp, x1, row, (x2-x1)); } for (row=y1+3; row 255) *d = 255; else *d = pvalue; d++; } if ( has_alpha ) { *d = cr[col * bpp + bytes]; d++; } } gimp_pixel_rgn_set_row(&destPR, dest+x1*bpp, x1, row, (x2-x1)); if ((row % 5) == 0) gimp_progress_update((double) (row) / (double) sw); } free(cr); free(dest); while (reps-- > 0) { /* horizontal shift */ p = (guchar *) malloc((width + 2 * max_shift) * bpp); p0 = p + max_shift * bpp; p2 = p0 + width * bpp; p1 = p2 - max_shift * bpp; dest = (guchar *) malloc((x2-x1) * bpp); for (row=y1; row 0 */ gimp_drawable_flush(drawable); gimp_drawable_merge_shadow(drawable->id, TRUE); gimp_drawable_update(drawable->id, x1, y1, sw, sh); } static gint carpet_dialog() { GtkWidget *dlg; GtkWidget *button; GtkWidget *frame; GtkWidget *table; gchar **argv; gint argc; argc = 1; argv = g_new (gchar *, 1); argv[0] = g_strdup ("carpet"); gtk_init (&argc, &argv); gtk_rc_parse (gimp_gtkrc ()); dlg = gtk_dialog_new(); gtk_window_set_title (GTK_WINDOW (dlg), "Carpeting.."); gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", (GtkSignalFunc) carpet_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) carpet_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_show (button); /* parameter settings */ frame = gtk_frame_new ("Parameter Settings"); 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, 6, FALSE); gtk_container_border_width (GTK_CONTAINER (table), 10); gtk_container_add (GTK_CONTAINER (frame), table); dialog_create_value("Depth", GTK_TABLE(table), 1, &bvals.depth, 1.0, 55.0); dialog_create_value("Noise", GTK_TABLE(table), 2, &bvals.noise, 0.0, 55.0); dialog_create_value("Shift", GTK_TABLE(table), 3, &bvals.max_shift, 0.0, 10.0); dialog_create_value("Repeats", GTK_TABLE(table), 4, &bvals.repeats, 0.0, 10.0); gtk_widget_show (frame); gtk_widget_show (table); gtk_widget_show (dlg); gtk_main(); gdk_flush(); return bint.run; } static void carpet_close_callback(GtkWidget *widget, gpointer data) { gtk_main_quit(); } static void carpet_ok_callback(GtkWidget *widget, gpointer data) { bint.run = TRUE; gtk_widget_destroy(GTK_WIDGET (data)); } static void carpet_entry_update(GtkWidget *widget, gint *value) { GtkAdjustment *adjustment; gint new_value; new_value = atoi(gtk_entry_get_text(GTK_ENTRY(widget))); if (*value != new_value) { adjustment = gtk_object_get_user_data(GTK_OBJECT(widget)); if ((new_value >= adjustment->lower) && (new_value <= adjustment->upper)) { *value = new_value; adjustment->value = new_value; gtk_signal_emit_by_name(GTK_OBJECT(adjustment), "value_changed"); } /* if */ } /* if */ } static void carpet_scale_update (GtkAdjustment *adjustment, gint *value) { GtkWidget *entry; char buf[256]; if (*value != adjustment->value) { *value = adjustment->value; entry = gtk_object_get_user_data(GTK_OBJECT(adjustment)); sprintf(buf, "%i", *value); gtk_signal_handler_block_by_data(GTK_OBJECT(entry), value); gtk_entry_set_text(GTK_ENTRY(entry), buf); gtk_signal_handler_unblock_by_data(GTK_OBJECT(entry), value); } /* if */ } /* * Thanks to Quartic for these. */ static void dialog_create_value(char *title, GtkTable *table, int row, gint *value, double left, double right) { GtkWidget *label; GtkWidget *scale; GtkWidget *entry; GtkObject *scale_data; char buf[256]; label = gtk_label_new(title); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_table_attach(table, label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 4, 0); gtk_widget_show(label); scale_data = gtk_adjustment_new(*value, left, right, 1.0, 5.0, 5.0); gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", (GtkSignalFunc) carpet_scale_update, value); scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); gtk_widget_set_usize(scale, SCALE_WIDTH, 0); gtk_table_attach(table, scale, 1, 2, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE); gtk_scale_set_digits(GTK_SCALE(scale), 3); gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS); gtk_widget_show(scale); entry = gtk_entry_new(); gtk_object_set_user_data(GTK_OBJECT(entry), scale_data); gtk_object_set_user_data(scale_data, entry); gtk_widget_set_usize(entry, ENTRY_WIDTH, 0); sprintf(buf, "%i", *value); gtk_entry_set_text(GTK_ENTRY(entry), buf); gtk_signal_connect(GTK_OBJECT(entry), "changed", (GtkSignalFunc) carpet_entry_update, value); gtk_table_attach(GTK_TABLE(table), entry, 2, 3, row, row + 1, GTK_FILL, GTK_FILL, 4, 0); gtk_widget_show(entry); }