/////////////////////////////////////////////////////////////// /* 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 "gtk/gtk.h" #include "lib/plugin_main.h" #include "libgimp/stdplugins-intl.h" #define ENTRY_WIDTH 100 /* extern int mainttt(); */ guint16 *bu; int glutwin=-1; gint width1, height1; GPixelRgn dest_rgn2; TileDrawable *drawable1; typedef struct { gdouble radius; gint horizontal; gint vertical; } BlurValues; typedef struct { gint run; } BlurInterface; /* Declare local functions. */ static void query (void); static void run (gchar *name, gint nparams, GParam *param, gint *nreturn_vals, GParam **return_vals); static void gauss_test_u8 (TileDrawable *drawable, gdouble horz, gdouble vert); static void gauss_test_u16 (TileDrawable *drawable, gdouble horz, gdouble vert); static void gauss_test (TileDrawable *drawable, gint horizontal, gint vertical, gdouble std_dev); /* * Gaussian blur interface */ static gint gauss_test_dialog (void); /* * Gaussian blur helper functions */ static gfloat * make_curve (gdouble sigma, gint * length, gfloat cutoff); static gint * make_curve_gint (gdouble sigma, gint * length); static void run_length_encode_u8 (guchar * src, gint * dest, gint bytes, gint width); static void run_length_encode_u16 (guint16 * src, gint * dest, gint bytes, gint width); static void run_length_encode_float (gfloat * src, gint * buf_number, gfloat * buf_values, gint num_channels, gint width); static void run_length_encode_float16 (guint16 * src, gint * buf_number, guint16 * buf_values, gint num_channels, gint width); static void gauss_close_callback (GtkWidget *widget, gpointer data); static void gauss_ok_callback (GtkWidget *widget, gpointer data); static void gauss_toggle_update (GtkWidget *widget, gpointer data); static void gauss_entry_callback (GtkWidget *widget, gpointer data); GPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; static BlurValues bvals = { 5.0, /* radius */ TRUE, /* horizontal blur */ TRUE /* vertical blur */ }; static BlurInterface bint = { FALSE /* run */ }; MAIN () static void query () { static GParamDef args[] = { { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, { PARAM_IMAGE, "image", "Input image (unused)" }, { PARAM_DRAWABLE, "drawable", "Input drawable" }, { PARAM_FLOAT, "radius", "Radius of gaussian blur (in pixels > 1.0)" }, { PARAM_INT32, "horizontal", "Blur in horizontal direction" }, { PARAM_INT32, "vertical", "Blur in vertical direction" }, }; static GParamDef *return_vals = NULL; static gint nargs = sizeof (args) / sizeof (args[0]); static gint nreturn_vals = 0; gimp_install_procedure ("plug_in_gauss_test", "an adapted gaussian plugin by TV", "Applies a gaussian blur to the drawable, with specified radius of affect. The standard deviation of the normal distribution used to modify pixel values is calculated based on the supplied radius. Horizontal and vertical blurring can be independently invoked by specifying only one to run. The RLE gaussian blurring performs most efficiently on computer-generated images or images with large areas of constant intensity. Values for radii less than 1.0 are invalid as they will generate spurious results.", "Spencer Kimball & Peter Mattis", "Spencer Kimball & Peter Mattis", "1995-1996", "/Filters/Blur/Gaussian Test, TV", /* "RGB*, GRAY*, U16_RGB*, U16_GRAY*, FLOAT_RGB*, FLOAT_GRAY*, FLOAT16_RGB*, FLOAT16_GRAY*", */ "U16_RGB*", PROC_PLUG_IN, nargs, nreturn_vals, args, return_vals); _("Blurtest_tv"); _("Gaussian Test (TV)"); } int mainttt(guint16 *bu, gint width, gint height); static GParam values[1]; static void run (gchar *name, gint nparams, GParam *param, gint *nreturn_vals, GParam **return_vals) { TileDrawable *drawable; GRunModeType run_mode; GStatusType status = STATUS_SUCCESS; gdouble radius, std_dev, cutoff; INIT_I18N_UI(); run_mode = (GRunModeType) param[0].data.d_int32; *nreturn_vals = 1; *return_vals = values; values[0].type = PARAM_STATUS; values[0].data.d_status = status; /* Get the specified drawable */ drawable = gimp_drawable_get (param[2].data.d_drawable); switch (gimp_drawable_type (drawable->id)) { case GRAY_IMAGE: case GRAYA_IMAGE: case RGB_IMAGE: case RGBA_IMAGE: case U16_GRAY_IMAGE: case U16_GRAYA_IMAGE: case U16_RGB_IMAGE: case U16_RGBA_IMAGE: break; default: gimp_message (_("gauss_test: cannot operate on other than 16-bit RGB images.")); /*gauss_rle (drawable, bvals.horizontal, bvals.vertical, std_dev); */ return; } switch (run_mode) { case RUN_INTERACTIVE: /* Possibly retrieve data */ gimp_get_data ("plug_in_gauss_test", &bvals); /* First acquire information with a dialog */ if (! gauss_test_dialog ()) return; break; case RUN_NONINTERACTIVE: /* Make sure all the arguments are there! */ if (nparams != 6) status = STATUS_CALLING_ERROR; if (status == STATUS_SUCCESS) { bvals.radius = param[3].data.d_float; bvals.horizontal = (param[4].data.d_int32) ? TRUE : FALSE; bvals.vertical = (param[5].data.d_int32) ? TRUE : FALSE; } if (status == STATUS_SUCCESS && (bvals.radius < 1.0)) status = STATUS_CALLING_ERROR; break; case RUN_WITH_LAST_VALS: /* Possibly retrieve data */ gimp_get_data ("plug_in_gauss_test", &bvals); break; default: break; } /* Make sure that the drawable is gray or RGB color */ if (gimp_drawable_color (drawable->id) || gimp_drawable_gray (drawable->id)) { gimp_progress_init (_("Theo Verelst Gaussian (Cuda later on ??)")); /* set the tile cache size so that the gaussian blur works well */ /* gimp_tile_cache_ntiles (2 * (MAX (drawable->width, drawable->height) / gimp_tile_width () + 1)); */ radius = fabs (bvals.radius) + 1.0; cutoff = .000001; std_dev = sqrt (-(radius * radius) / (2 * log (cutoff))); /* run the gaussian blur */ switch (gimp_drawable_type (drawable->id)) { case GRAY_IMAGE: case GRAYA_IMAGE: case RGB_IMAGE: case RGBA_IMAGE: gauss_test_u8(drawable, radius, radius); break; case U16_GRAY_IMAGE: case U16_GRAYA_IMAGE: case U16_RGB_IMAGE: case U16_RGBA_IMAGE: // d_printf ("16-bit\n"); gauss_test_u16(drawable, radius, radius); /* mainttt(); */ break; default: gimp_message (_("gauss_test: cannot operate on other than 8-/16-bit images.")); /*gauss_rle (drawable, bvals.horizontal, bvals.vertical, std_dev); */ } if (run_mode != RUN_NONINTERACTIVE) gimp_displays_flush (); /* Store data */ if (run_mode == RUN_INTERACTIVE) gimp_set_data ("plug_in_gauss_test", &bvals, sizeof (BlurValues)); } else { /* gimp_message ("gauss_rle: cannot operate on indexed color images"); */ status = STATUS_EXECUTION_ERROR; } values[0].data.d_status = status; gimp_drawable_detach (drawable); } static gint gauss_test_dialog () { GtkWidget *dlg; GtkWidget *label; GtkWidget *entry; GtkWidget *button; GtkWidget *toggle; GtkWidget *frame; GtkWidget *vbox; GtkWidget *hbox; gchar buffer[12]; gchar **argv; gint argc; argc = 1; argv = g_new (gchar *, 1); argv[0] = g_strdup ("gauss"); gtk_init (&argc, &argv); gtk_rc_parse (gimp_gtkrc ()); dlg = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (dlg), _("theover test plugin")); gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", (GtkSignalFunc) gauss_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) gauss_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); vbox = gtk_vbox_new (FALSE, 5); gtk_container_border_width (GTK_CONTAINER (vbox), 10); gtk_container_add (GTK_CONTAINER (frame), vbox); toggle = gtk_check_button_new_with_label (_("Blur Horizontally")); gtk_box_pack_start (GTK_BOX (vbox), toggle, TRUE, TRUE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) gauss_toggle_update, &bvals.horizontal); gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), bvals.horizontal); gtk_widget_show (toggle); toggle = gtk_check_button_new_with_label (_("Blur Vertically")); gtk_box_pack_start (GTK_BOX (vbox), toggle, TRUE, TRUE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) gauss_toggle_update, &bvals.vertical); gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), bvals.vertical); gtk_widget_show (toggle); hbox = gtk_hbox_new (FALSE, 5); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); label = gtk_label_new (_("Blur Radius: ")); gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0); gtk_widget_show (label); entry = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); gtk_widget_set_usize (entry, ENTRY_WIDTH, 0); sprintf (buffer, "%f", bvals.radius); gtk_entry_set_text (GTK_ENTRY (entry), buffer); gtk_signal_connect (GTK_OBJECT (entry), "changed", (GtkSignalFunc) gauss_entry_callback, NULL); gtk_widget_show (entry); gtk_widget_show (hbox); gtk_widget_show (vbox); gtk_widget_show (frame); gtk_widget_show (dlg); gtk_main (); gdk_flush (); return bint.run; } /* Convert from separated to premultiplied alpha, on a single scan line. */ static void multiply_alpha (guchar *buf, gint width, gint bytes) { gint i, j; gdouble alpha; for (i = 0; i < width * bytes; i += bytes) { alpha = buf[i + bytes - 1] * (1.0 / 255.0); for (j = 0; j < bytes - 1; j++) buf[i + j] *= alpha; } } /* Convert from premultiplied to separated alpha, on a single scan line. */ static void separate_alpha (guchar *buf, gint width, gint bytes) { gint i, j; guchar alpha; gdouble recip_alpha; gint new_val; for (i = 0; i < width * bytes; i += bytes) { alpha = buf[i + bytes - 1]; if (alpha != 0 && alpha != 255) { recip_alpha = 255.0 / alpha; for (j = 0; j < bytes - 1; j++) { new_val = buf[i + j] * recip_alpha; buf[i + j] = MIN (255, new_val); } } } } static void gauss_test_u16 (TileDrawable *drawable, gdouble horz, gdouble vert) { GPixelRgn src_rgn, dest_rgn; //, dest_rgn2; gint width, height; gint bytes; gint has_alpha; guint16 *dest, *dp; guint16 *src, *sp; gint *buf, *bb; gint pixels; gint total = 1; gint x1, y1, x2, y2; gint i, row, col, b; gint start, end; gint progress, max_progress; gint *curve; gint *sum = NULL; gint val; gint length; gint initial_p, initial_m; gdouble std_dev; if (horz < 1.0 && vert < 1.0) return; gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2); width = (x2 - x1); height = (y2 - y1); width1 = (x2 - x1); height1 = (y2 - y1); bytes = drawable->bpp; has_alpha = gimp_drawable_has_alpha(drawable->id); fprintf(stderr, "x %d x2 %d y %d y2 %d\n", x1, x2, y1, y2); fprintf(stderr, "width %d height %d hasalpha %d Bytes %d\n", width, height, has_alpha, bytes); progress = 0; max_progress = 1; if (0==1) { buf = g_new (gint, MAX (width, height) * 2); /* allocate buffers for source and destination pixels */ src = g_new (guint16, MAX (width, height) * bytes); dest = g_new (guint16, MAX (width, height) * bytes); gimp_pixel_rgn_init (&src_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE); gimp_pixel_rgn_init (&dest_rgn, drawable, 0, 0, drawable->width, drawable->height, TRUE, TRUE); progress = 0; max_progress = (horz < 1.0 ) ? 0 : width * height * horz; max_progress += (vert < 1.0 ) ? 0 : width * height * vert; if (has_alpha) multiply_alpha ((guchar *) src, height, bytes); /* First the vertical pass */ if (vert >= 1.0) { vert = fabs (vert) + 1.0; std_dev = sqrt (-(vert * vert) / (2 * log (1.0 / 65535.0))); curve = make_curve_gint (std_dev, &length); sum = g_new (gint, 2 * length + 1); sum[0] = 0; for (i = 1; i <= length*2; i++) sum[i] = curve[i-length-1] + sum[i-1]; sum += length; total = sum[length] - sum[-length]; for (col = 0; col < width; col++) { gimp_pixel_rgn_get_col (&src_rgn, (guchar *) src, col + x1, y1, (y2 - y1)); sp = src; dp = dest; for (b = 0; b < bytes; b++) { initial_p = sp[b]; initial_m = sp[(height-1) * bytes + b]; /* Determine a run-length encoded version of the row */ run_length_encode_u16 (sp + b, buf, bytes, height); for (row = 0; row < height; row++) { start = (row < length) ? -row : -length; end = (height <= (row + length)) ? (height - row - 1) : length; val = 0; i = start; bb = buf + (row + i) * 2; if (start != -length) val += initial_p * (sum[start] - sum[-length]); while (i < end) { pixels = bb[0]; i += pixels; if (i > end) i = end; val += bb[1] * (sum[i] - sum[start]); bb += (pixels * 2); start = i; } if (end != length) val += initial_m * (sum[length] - sum[end]); dp[row * bytes + b] = val / total; } } gimp_pixel_rgn_set_col (&dest_rgn, (guchar *) dest, col + x1, y1, (y2 - y1)); progress += height * vert; if ((col % 5) == 0) gimp_progress_update ((double) progress / (double) max_progress); } /* prepare for the horizontal pass */ gimp_pixel_rgn_init (&src_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, TRUE); } /* Now the horizontal pass */ if (horz >= 1.0) { horz = fabs (horz) + 1.0; if (horz != vert) { std_dev = sqrt (-(horz * horz) / (2 * log (1.0 / 65535.0))); curve = make_curve_gint (std_dev, &length); sum = g_new (gint, 2 * length + 1); sum[0] = 0; for (i = 1; i <= length*2; i++) sum[i] = curve[i-length-1] + sum[i-1]; sum += length; total = sum[length] - sum[-length]; } for (row = 0; row < height; row++) { gimp_pixel_rgn_get_row (&src_rgn, (guchar *) src, x1, row + y1, (x2 - x1)); sp = src; dp = dest; for (b = 0; b < bytes; b++) { initial_p = sp[b]; initial_m = sp[(width-1) * bytes + b]; /* Determine a run-length encoded version of the row */ run_length_encode_u16 (sp + b, buf, bytes, width); for (col = 0; col < width; col++) { start = (col < length) ? -col : -length; end = (width <= (col + length)) ? (width - col - 1) : length; val = 0; i = start; bb = buf + (col + i) * 2; if (start != -length) val += initial_p * (sum[start] - sum[-length]); while (i < end) { pixels = bb[0]; i += pixels; if (i > end) i = end; val += bb[1] * (sum[i] - sum[start]); bb += (pixels * 2); start = i; } if (end != length) val += initial_m * (sum[length] - sum[end]); dp[col * bytes + b] = val / total; } } gimp_pixel_rgn_set_row (&dest_rgn, (guchar *) dest, x1, row + y1, (x2 - x1)); progress += width * horz; if ((row % 5) == 0) gimp_progress_update ((double) progress / (double) max_progress); } } if (has_alpha) separate_alpha ((guchar *) dest, width, bytes); /* merge the shadow, update the drawable */ /* Allocate buffer for full image */ /* bu = g_new ( width*height * bytes); */ gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->id, TRUE); gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1)); } width1 = drawable->width; height1 = drawable->height; bu = (guint16 *) malloc(width1*height1*bytes); if (bu == NULL) { fprintf(stderr,"No malloc of full image today...\n") ; return; }; gimp_pixel_rgn_init (&src_rgn , drawable, 0, 0, width1, height1, FALSE, FALSE); gimp_pixel_rgn_init (&dest_rgn2 , drawable, 0, 0, width1, height1, TRUE, TRUE); gimp_pixel_rgn_get_rect( &src_rgn , (guchar*) bu, 0,0, width1,height1); gimp_progress_update (0.25); /* for (col = 0; col < width1; col++) for (row = 0; row < height1; row++) { bu[col*3+width1*row*3 ]= 65535; bu[col*3+width1*row*3+1]= 65535/2; bu[col*3+width1*row*3+2]= 0; } */ drawable1 = drawable; mainttt(bu, width1, height1); gimp_progress_update (0.75); gimp_pixel_rgn_set_rect( &dest_rgn2 , (guchar*) bu, 0,0, width1,height1); gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->id, TRUE); gimp_drawable_update (drawable->id, 0, 0, width1, height1); /* free buffers */ if (0==1) { g_free (buf); g_free (src); g_free (dest); } free(bu); } static void gauss_test_u8 (TileDrawable *drawable, gdouble horz, gdouble vert) { GPixelRgn src_rgn, dest_rgn; gint width, height; gint bytes; gint has_alpha; guchar *dest, *dp; guchar *src, *sp; gint *buf, *bb; gint pixels; gint total = 1; gint x1, y1, x2, y2; gint i, row, col, b; gint start, end; gint progress, max_progress; gint *curve; gint *sum = NULL; gint val; gint length; gint initial_p, initial_m; gdouble std_dev; if (horz < 1.0 && vert < 1.0) return; gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2); width = (x2 - x1); height = (y2 - y1); bytes = drawable->bpp; has_alpha = gimp_drawable_has_alpha(drawable->id); buf = g_new (gint, MAX (width, height) * 2); /* allocate buffers for source and destination pixels */ src = g_new (guchar, MAX (width, height) * bytes); dest = g_new (guchar, MAX (width, height) * bytes); gimp_pixel_rgn_init (&src_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE); gimp_pixel_rgn_init (&dest_rgn, drawable, 0, 0, drawable->width, drawable->height, TRUE, TRUE); progress = 0; max_progress = (horz < 1.0 ) ? 0 : width * height * horz; max_progress += (vert < 1.0 ) ? 0 : width * height * vert; if (has_alpha) multiply_alpha (src, height, bytes); /* First the vertical pass */ if (vert >= 1.0) { vert = fabs (vert) + 1.0; std_dev = sqrt (-(vert * vert) / (2 * log (1.0 / 255.0))); curve = make_curve_gint (std_dev, &length); sum = g_new (gint, 2 * length + 1); sum[0] = 0; for (i = 1; i <= length*2; i++) sum[i] = curve[i-length-1] + sum[i-1]; sum += length; total = sum[length] - sum[-length]; for (col = 0; col < width; col++) { gimp_pixel_rgn_get_col (&src_rgn, src, col + x1, y1, (y2 - y1)); sp = src; dp = dest; for (b = 0; b < bytes; b++) { initial_p = sp[b]; initial_m = sp[(height-1) * bytes + b]; /* Determine a run-length encoded version of the row */ run_length_encode_u8 (sp + b, buf, bytes, height); for (row = 0; row < height; row++) { start = (row < length) ? -row : -length; end = (height <= (row + length)) ? (height - row - 1) : length; val = 0; i = start; bb = buf + (row + i) * 2; if (start != -length) val += initial_p * (sum[start] - sum[-length]); while (i < end) { pixels = bb[0]; i += pixels; if (i > end) i = end; val += bb[1] * (sum[i] - sum[start]); bb += (pixels * 2); start = i; } if (end != length) val += initial_m * (sum[length] - sum[end]); dp[row * bytes + b] = val / total; } } gimp_pixel_rgn_set_col (&dest_rgn, dest, col + x1, y1, (y2 - y1)); progress += height * vert; if ((col % 5) == 0) gimp_progress_update ((double) progress / (double) max_progress); } /* prepare for the horizontal pass */ gimp_pixel_rgn_init (&src_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, TRUE); } /* Now the horizontal pass */ if (horz >= 1.0) { horz = fabs (horz) + 1.0; if (horz != vert) { std_dev = sqrt (-(horz * horz) / (2 * log (1.0 / 255.0))); curve = make_curve_gint (std_dev, &length); sum = g_new (gint, 2 * length + 1); sum[0] = 0; for (i = 1; i <= length*2; i++) sum[i] = curve[i-length-1] + sum[i-1]; sum += length; total = sum[length] - sum[-length]; } for (row = 0; row < height; row++) { gimp_pixel_rgn_get_row (&src_rgn, src, x1, row + y1, (x2 - x1)); sp = src; dp = dest; for (b = 0; b < bytes; b++) { initial_p = sp[b]; initial_m = sp[(width-1) * bytes + b]; /* Determine a run-length encoded version of the row */ run_length_encode_u8 (sp + b, buf, bytes, width); for (col = 0; col < width; col++) { start = (col < length) ? -col : -length; end = (width <= (col + length)) ? (width - col - 1) : length; val = 0; i = start; bb = buf + (col + i) * 2; if (start != -length) val += initial_p * (sum[start] - sum[-length]); while (i < end) { pixels = bb[0]; i += pixels; if (i > end) i = end; val += bb[1] * (sum[i] - sum[start]); bb += (pixels * 2); start = i; } if (end != length) val += initial_m * (sum[length] - sum[end]); dp[col * bytes + b] = val / total; } } gimp_pixel_rgn_set_row (&dest_rgn, dest, x1, row + y1, (x2 - x1)); progress += width * horz; if ((row % 5) == 0) gimp_progress_update ((double) progress / (double) max_progress); } } if (has_alpha) separate_alpha (dest, width, bytes); /* merge the shadow, update the drawable */ gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->id, TRUE); gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1)); /* free buffers */ g_free (buf); g_free (src); g_free (dest); } static void gauss_test (TileDrawable *drawable, gint horz, gint vert, gdouble std_dev) { GPixelRgn src_rgn, dest_rgn; gint width, height; gint bytes; gint channel_bytes; gint num_channels; gint drawable_type; gint has_alpha; gfloat *f_dest, *f_dp; gfloat *f_src, *f_sp; guint16 *f16_dest, *f16_dp; guint16 *f16_src, *f16_sp; guint16 *u16_dest, *u16_dp; guint16 *u16_src, *u16_sp; gfloat *f_buf_values; /*rle values array*/ guint16 *f16_buf_values; /*rle values array*/ gfloat *f_bb_values; guint16 *f16_bb_values; gfloat f_initial_p, f_initial_m; guint16 f16_initial_p, f16_initial_m; gint *buf_number; /*rle numbers array*/ gint *bb_number; gint pixels; gint x1, y1, x2, y2; gint i, row, col, b; gint start, end; gint progress, max_progress; gfloat *curve; gfloat *sum; gfloat val; gfloat total; gint length; gfloat cutoff = .000001; ShortsFloat u; curve = make_curve (std_dev, &length, cutoff); sum = (gfloat *) malloc (sizeof (gfloat) * (2 * length + 1)); sum[0] = 0; for (i = 1; i <= length*2; i++) sum[i] = curve[i-length-1] + sum[i-1]; sum += length; gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2); drawable_type = gimp_drawable_type (drawable->id); /* merge the shadow, update the drawable */ gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->id, TRUE); gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1)); /* free buffers */ free (buf_number); free (f16_buf_values); free (f_buf_values); free (f_src); free (f16_src); free (f_dest); free (f16_dest); } /* * The equations: g(r) = exp (- r^2 / (2 * sigma^2)) * r = sqrt (x^2 + y ^2) */ static gfloat * make_curve (gdouble sigma, gint *length, gfloat cutoff) { gfloat *curve; gdouble sigma2; gdouble l; gfloat temp; gint i, n; sigma2 = 2 * sigma * sigma; l = sqrt (-sigma2 * log (cutoff)); n = ceil (l) * 2; if ((n % 2) == 0) n += 1; curve = (gfloat *) malloc (sizeof (gfloat) * n); *length = n / 2; curve += *length; curve[0] = 1.0; for (i = 1; i <= *length; i++) { temp = (gfloat) exp (- (i * i) / sigma2); curve[-i] = temp; curve[i] = temp; } return curve; } static void run_length_encode_u8 (guchar *src, gint *dest, gint bytes, gint width) { gint start; gint i; gint j; guchar last; last = *src; src += bytes; start = 0; for (i = 1; i < width; i++) { if (*src != last) { for (j = start; j < i; j++) { *dest++ = (i - j); *dest++ = last; } start = i; last = *src; } src += bytes; } for (j = start; j < i; j++) { *dest++ = (i - j); *dest++ = last; } } static void run_length_encode_u16 (guint16 *src, gint *dest, gint bytes, gint width) { gint start; gint i; gint j; guint16 last; last = *src; src += bytes; start = 0; for (i = 1; i < width; i++) { if (*src != last) { for (j = start; j < i; j++) { *dest++ = (i - j); *dest++ = last; } start = i; last = *src; } src += bytes; } for (j = start; j < i; j++) { *dest++ = (i - j); *dest++ = last; } } static void run_length_encode_float (gfloat *src, gint *number, /*dest*/ gfloat *values, /*dest*/ gint num_channels, gint width) { gint start; gint i; gint j; gfloat last; last = *src; src += num_channels; start = 0; for (i = 1; i < width; i++) { if (*src != last) { for (j = start; j < i; j++) { *number++ = i - j; *values++ = last; } start = i; last = *src; } src += num_channels; } for (j = start; j < i; j++) { *number++ = i - j; *values++ = last; } } static void run_length_encode_float16 (guint16 *src, gint *number, /*dest*/ guint16 *values, /*dest*/ gint num_channels, gint width) { gint start; gint i; gint j; gfloat last; last = *src; src += num_channels; start = 0; for (i = 1; i < width; i++) { if (*src != last) { for (j = start; j < i; j++) { *number++ = i - j; *values++ = last; } start = i; last = *src; } src += num_channels; } for (j = start; j < i; j++) { *number++ = i - j; *values++ = last; } } /* Gauss interface functions */ static void gauss_close_callback (GtkWidget *widget, gpointer data) { gtk_main_quit (); } static void gauss_ok_callback (GtkWidget *widget, gpointer data) { bint.run = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); } static void gauss_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 gauss_entry_callback (GtkWidget *widget, gpointer data) { bvals.radius = atof (gtk_entry_get_text (GTK_ENTRY (widget))); if (bvals.radius < 1.0) bvals.radius = 1.0; } static gint * make_curve_gint (gdouble sigma, gint *length) { gint *curve; gdouble sigma2; gdouble l; gint temp; gint i, n; sigma2 = 2 * sigma * sigma; l = sqrt (-sigma2 * log (1.0 / 255.0)); n = ceil (l) * 2; if ((n % 2) == 0) n += 1; curve = g_new (gint, n); *length = n / 2; curve += *length; curve[0] = 255; for (i = 1; i <= *length; i++) { temp = (gint) (exp (- (i * i) / sigma2) * 255); curve[-i] = temp; curve[i] = temp; } return curve; } /* * Copyright 1993-2007 NVIDIA Corporation. All rights reserved. * * NOTICE TO USER: * * This source code is subject to NVIDIA ownership rights under U.S. and * international Copyright laws. * * NVIDIA MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THIS SOURCE * CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR * IMPLIED WARRANTY OF ANY KIND. NVIDIA DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOURCE CODE, INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. * IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL, * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE * OR PERFORMANCE OF THIS SOURCE CODE. * * U.S. Government End Users. This source code is a "commercial item" as * that term is defined at 48 C.F.R. 2.101 (OCT 1995), consisting of * "commercial computer software" and "commercial computer software * documentation" as such terms are used in 48 C.F.R. 12.212 (SEPT 1995) * and is provided to the U.S. Government only as a commercial end item. * Consistent with 48 C.F.R.12.212 and 48 C.F.R. 227.7202-1 through * 227.7202-4 (JUNE 1995), all U.S. Government End Users acquire the * source code with only those rights set forth herein. */ /* Recursive Gaussian filter sgreen 8/1/08 This code sample implements a Gaussian blur using Deriche's recursive method: http://citeseer.ist.psu.edu/deriche93recursively.html This is similar to the box filter sample in the SDK, but it uses the previous outputs of the filter as well as the previous inputs. This is also known as an IIR (infinite impulse response) filter, since its response to an input impulse can last forever. The main advantage of this method is that the execution time is independent of the filter width. The GPU processes columns of the image in parallel. To avoid uncoalesced reads for the row pass we transpose the image and then transpose it back again afterwards. The implementation is based on code from the CImg library: http://cimg.sourceforge.net/ Thanks to David Tschumperlé and all the CImg contributors! */ #include #include #include #include #include #if defined (__APPLE__) || defined(MACOSX) #include #else #include #endif #include #include #include #define USE_SIMPLE_FILTER 0 /* extern int mainttt( int , char** ) ; */ char *image_filename = "lena.ppm"; float sigma = 10.0f; int order = 0; int nthreads = 64; // number of threads per block unsigned int width, height; uint* h_img = NULL; uint* h_result = NULL; uint* d_img = NULL; uint* d_temp = NULL; GLuint pbo = 0; // OpenGL pixel buffer object GLuint texid = 0; // texture unsigned int timer = 0; int fpsCount = 0; // FPS count for averaging int fpsLimit = 1; // FPS limit for sampling //Round a / b to nearest higher integer value int iDivUp(int a, int b){ return (a % b != 0) ? (a / b + 1) : (a / b); } /* Transpose a 2D array (see SDK transpose example) */ void transpose(uint *d_src, uint *d_dest, uint width, int height) { dim3 grid(iDivUp(width, BLOCK_DIM), iDivUp(height, BLOCK_DIM), 1); dim3 threads(BLOCK_DIM, BLOCK_DIM, 1); d_transpose<<< grid, threads >>>(d_dest, d_src, width, height); cutilCheckMsg("Kernel execution failed"); } /* Perform Gaussian filter on a 2D image using CUDA Parameters: d_src - pointer to input image in device memory d_dest - pointer to destination image in device memory d_temp - pointer to temporary storage in device memory width - image width height - image height sigma - sigma of Gaussian order - filter order (0, 1 or 2) */ // 8-bit RGBA version void gaussianFilterRGBA(uint *d_src, uint *d_dest, uint *d_temp, int width, int height, float sigma, int order) { const float nsigma = sigma < 0.1f ? 0.1f : sigma, alpha = 1.695f / nsigma, ema = (float)std::exp(-alpha), ema2 = (float)std::exp(-2*alpha), b1 = -2*ema, b2 = ema2; float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0; switch (order) { case 0: { const float k = (1-ema)*(1-ema)/(1+2*alpha*ema-ema2); a0 = k; a1 = k*(alpha-1)*ema; a2 = k*(alpha+1)*ema; a3 = -k*ema2; } break; case 1: { const float k = (1-ema)*(1-ema)/ema; a0 = k*ema; a1 = a3 = 0; a2 = -a0; } break; case 2: { const float ea = (float)std::exp(-alpha), k = -(ema2-1)/(2*alpha*ema), kn = (-2*(-1+3*ea-3*ea*ea+ea*ea*ea)/(3*ea+1+3*ea*ea+ea*ea*ea)); a0 = kn; a1 = -kn*(1+k*alpha)*ema; a2 = kn*(1-k*alpha)*ema; a3 = -kn*ema2; } break; default: fprintf(stderr, "gaussianFilter: invalid order parameter!\n"); return; } coefp = (a0+a1)/(1+b1+b2); coefn = (a2+a3)/(1+b1+b2); // process columns #if USE_SIMPLE_FILTER d_simpleRecursive_rgba<<< iDivUp(width, nthreads), nthreads >>>(d_src, d_temp, width, height, ema); #else d_recursiveGaussian_rgba<<< iDivUp(width, nthreads), nthreads >>>(d_src, d_temp, width, height, a0, a1, a2, a3, b1, b2, coefp, coefn); #endif cutilCheckMsg("Kernel execution failed"); transpose(d_temp, d_dest, width, height); cutilCheckMsg("transpose: Kernel execution failed"); // process rows #if USE_SIMPLE_FILTER d_simpleRecursive_rgba<<< iDivUp(height, nthreads), nthreads >>>(d_dest, d_temp, height, width, ema); #else d_recursiveGaussian_rgba<<< iDivUp(height, nthreads), nthreads >>>(d_dest, d_temp, height, width, a0, a1, a2, a3, b1, b2, coefp, coefn); #endif cutilCheckMsg("Kernel execution failed"); transpose(d_temp, d_dest, height, width); } // display results using OpenGL void display() { uint *d_result; cutilCheckError(cutStartTimer(timer)); // execute filter, writing results to pbo cutilSafeCall(cudaGLMapBufferObject((void**)&d_result, pbo)); gaussianFilterRGBA(d_img, d_result, d_temp, width, height, sigma, order); cutilSafeCall(cudaGLUnmapBufferObject(pbo)); // load texture from pbo glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbo); glBindTexture(GL_TEXTURE_2D, texid); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); // display results glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glBegin(GL_QUADS); glVertex2f(0, 0); glTexCoord2f(0, 0); glVertex2f(0, 1); glTexCoord2f(1, 0); glVertex2f(1, 1); glTexCoord2f(1, 1); glVertex2f(1, 0); glTexCoord2f(0, 1); glEnd(); glDisable(GL_TEXTURE_2D); glutSwapBuffers(); cutilCheckError(cutStopTimer(timer)); fpsCount++; if (fpsCount == fpsLimit) { char fps[256]; float ifps = 1.f / (cutGetAverageTimerValue(timer) / 1000.f); sprintf(fps, "CUDA Recursive Gaussian filter: %3.1f fps", ifps); glutSetWindowTitle(fps); fpsCount = 0; fpsLimit = (int)max(ifps, 1.f); cutilCheckError(cutResetTimer(timer)); } } void idle() { glutPostRedisplay(); } void cleanup(); void keyboard(unsigned char key, int x, int y) { switch(key) { case 27: cleanup(); // cudaThreadExit(); // gtk_main_quit (); // exit(0); // glutPostRedisplay(); return; /* exit(0); */ break; case '=': sigma+=0.1f; break; case '-': sigma-=0.1f; if (sigma < 0.0) sigma = 0.0f; break; case '+': sigma+=1.0f; break; case '_': sigma-=1.0f; if (sigma < 0.0) sigma = 0.0f; break; case '0': order = 0; break; case '1': order = 1; sigma = 0.5f; break; case '2': order = 2; sigma = 0.5f; break; default: break; } fprintf(stderr, "sigma = %f\n", sigma); glutPostRedisplay(); } void reshape(int x, int y) { glViewport(0, 0, x, y); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0); } void initCuda() { unsigned int size = width * height * sizeof(uint); // allocate device memory cutilSafeCall( cudaMalloc( (void**) &d_img, size)); cutilSafeCall( cudaMalloc( (void**) &d_temp, size)); cutilSafeCall( cudaMemcpy( d_img, h_img, size, cudaMemcpyHostToDevice)); cutilCheckError( cutCreateTimer( &timer)); } uint *d_result1; void cleanup() { /* This is the OpenGL/Glut standard cleanup routine, called throught invokation from the interactor function directly. It ends the communication with the wire protocol peer, without waiting for the callback, so an error (but no coredump) is generated. The data is communicated back to the cinepain main program though. */ int col, row; unsigned int t,c=0; unsigned int size = width * height * sizeof(uint); /* The global ones */ cutilSafeCall( cudaMalloc( (void**) &d_result1, size)); if (d_result1 == NULL) fprintf(stderr, "Null\n"); fflush(stderr); gaussianFilterRGBA(d_img, d_result1, d_temp, width, height, sigma, order); cutilSafeCall( cudaMemcpy( h_result, d_result1, size, cudaMemcpyDeviceToHost)); for (col = 0; col < width1; col++) for (row = 0; row < height1; row++) { t = h_result[col+width1*row]; t &=0x0ff0000; t/=256; if (t!=0) c++; bu[col*3+width1*row*3+2]= t; t = h_result[col+width1*row]; t &=0x000ff00; bu[col*3+width1*row*3+1]= t; t = h_result[col+width1*row]; t &=0x00000ff; t*=256; bu[col*3+width1*row*3+0]= t; // bu[col*3+width1*row*3+0]= ((guint16) ((h_result[col+width1*row]>>16) && 255))<8; /* bu[col*3+width1*row*3+1]= 65535/2; */ // bu[col*3+width1*row*3+1]= ((guint16) ((h_result[col+width1*row]>>8) && 255))<8; // bu[col*3+width1*row*3+2]= ((guint16) (h_result[col+width1*row] && 255))<8; } // fprintf(stderr, "%x %x %d\n", // (int) h_result[100+width1*100], bu[100*3+width1*100*3], c); // fflush(stderr); // for (col = 0; col < width1/6; col++) // for (row = 0; row < height1/6; row++) // bu[col*3+width1*row*3 ]= 0x7000; // bu[100+width1*100+1] = 0x0ffff; gimp_progress_update (0.75); gimp_pixel_rgn_set_rect( &dest_rgn2 , (guchar*) bu, 0,0, width1,height1); gimp_drawable_flush (drawable1); gimp_drawable_merge_shadow (drawable1->id, TRUE); gimp_drawable_update (drawable1->id, 0, 0, width1, height1); free(bu); values[0].data.d_status = STATUS_SUCCESS; gimp_drawable_detach (drawable1); glutDestroyWindow(glutwin); cutilCheckError( cutDeleteTimer( timer)); cutilSafeCall(cudaFree(d_img)); cutilSafeCall(cudaFree(d_temp)); cutilSafeCall(cudaFree(d_result1)); if (pbo) { cutilSafeCall(cudaGLUnregisterBufferObject(pbo)); glDeleteBuffersARB(1, &pbo); } if (texid) { glDeleteTextures(1, &texid); } // free(h_img); /* free buffers */ } void initOpenGL() { // create pixel buffer object to store final image glGenBuffersARB(1, &pbo); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbo); glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, width*height*sizeof(GLubyte)*4, h_img, GL_STREAM_DRAW_ARB); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); cutilSafeCall(cudaGLRegisterBufferObject(pbo)); // create texture for display glGenTextures(1, &texid); glBindTexture(GL_TEXTURE_2D, texid); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); } void benchmark(int iterations) { // allocate memory for result uint *d_result; unsigned int size = width * height * sizeof(uint); cutilSafeCall( cudaMalloc( (void**) &d_result, size)); // warm-up gaussianFilterRGBA(d_img, d_result, d_temp, width, height, sigma, order); cutilSafeCall( cudaThreadSynchronize() ); cutilCheckError( cutStartTimer( timer)); // execute the kernel for(int i=0; i>8)<<0 | (bu[col*3+width1*row*3+1]>>8)<<8 | (bu[col*3+width1*row*3+2]>>8)<<16 | (128)<< 24; } // for (col = 0; col < width1/10; col++) // for (row = 0; row < height/8; row++) { // h_img[col+width1*row]= 0xfe045577; // } initCuda(); if (runBenchmark) { benchmark(100); cleanup(); // cudaThreadExit(); exit(0); } // initialize GLUT glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); glutInitWindowSize(width1, height1); glutwin = glutCreateWindow("CUDA Recursive Gaussian filter"); glutDisplayFunc(display); glutKeyboardFunc(keyboard); glutReshapeFunc(reshape); glutIdleFunc(idle); fprintf(stderr, "Press '+' and '-' to change filter width1\n"); fprintf(stderr, "0, 1, 2 - change filter order\n"); glewInit(); if (!glewIsSupported("GL_VERSION_2_0 GL_ARB_vertex_buffer_object GL_ARB_pixel_buffer_object")) { fprintf(stderr, "Required OpenGL extensions missing."); cudaThreadExit(); exit(-1); } initOpenGL(); // atexit(cleanup); glutMainLoop(); cudaThreadExit(); return 0; }