home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / unofficial-plug-ins / sel_gauss / sel_gauss.c
Encoding:
C/C++ Source or Header  |  1999-10-20  |  12.3 KB  |  480 lines

  1. /* Selective gaussian blur filter for the GIMP, version 0.1
  2.  * Adapted from the original gaussian blur filter by Spencer Kimball and
  3.  * Peter Mattis.
  4.  *
  5.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  6.  * Copyright (C) 1999 Thom van Os <thom@vanos.com>
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 2 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program; if not, write to the Free Software
  20.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21.  *
  22.  *
  23.  * Changelog:
  24.  *
  25.  * v0.1     990202, TVO
  26.  *     First release
  27.  *
  28.  * To do:
  29.  *    - support for horizontal or vertical only blur
  30.  *    - use memory more efficiently, smaller regions at a time
  31.  *    - integrating with other convolution matrix based filters ?
  32.  *    - create more selective and adaptive filters
  33.  *    - threading
  34.  *    - optimization
  35.  *
  36.  */
  37. #include <math.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include "gtk/gtk.h"
  42. #include "libgimp/gimp.h"
  43.  
  44. #ifndef M_PI
  45. #define M_PI    3.14159265358979323846
  46. #endif /* M_PI */
  47.  
  48. #define ENTRY_WIDTH 100
  49.  
  50. typedef struct
  51. {
  52.   gdouble radius;
  53.   gint maxdelta;
  54. } BlurValues;
  55.  
  56. typedef struct
  57. {
  58.   gint run;
  59. } BlurInterface;
  60.  
  61.  
  62. /* Declare local functions.
  63.  */
  64. static void      query  (void);
  65. static void      run    (gchar     *name,
  66.              gint       nparams,
  67.              GParam     *param,
  68.              gint      *nreturn_vals,
  69.              GParam   **return_vals);
  70.  
  71. static void      sel_gauss        (GDrawable *drawable,
  72.                    gdouble    radius,
  73.                    gint       maxdelta);
  74.  
  75. /*
  76.  * Gaussian blur interface
  77.  */
  78. static gint      sel_gauss_dialog (void);
  79.  
  80. /*
  81.  * Gaussian blur helper functions
  82.  */
  83. static void sel_gauss_close_callback  (GtkWidget *widget, gpointer data);
  84. static void sel_gauss_ok_callback     (GtkWidget *widget, gpointer data);
  85. static void sel_gauss_entry_callback  (GtkWidget *widget, gpointer data);
  86. static void sel_gauss_delta_callback  (GtkWidget *widget, gpointer data);
  87.  
  88. GPlugInInfo PLUG_IN_INFO =
  89. {
  90.     NULL,    /* init_proc */
  91.     NULL,    /* quit_proc */
  92.     query,    /* query_proc */
  93.     run,    /* run_proc */
  94. };
  95.  
  96. static BlurValues bvals =
  97. {
  98.     5.0,    /*  radius  */
  99.     50    /* maxdelta */
  100. };
  101.  
  102. static BlurInterface bint =
  103. {
  104.     FALSE    /* run */
  105. };
  106.  
  107. MAIN ()
  108.  
  109. static void query ()
  110. {
  111.   static GParamDef args[] =
  112.   {
  113.     { PARAM_INT32, "run_mode", "Interactive, non-interactive" },
  114.     { PARAM_IMAGE, "image", "Input image (unused)" },
  115.     { PARAM_DRAWABLE, "drawable", "Input drawable" },
  116.     { PARAM_FLOAT, "radius", "Radius of gaussian blur (in pixels > 1.0)" },
  117.     { PARAM_INT32, "maxdelta", "Maximum delta" },
  118.   };
  119.   static GParamDef *return_vals = NULL;
  120.   static gint nargs = sizeof (args) / sizeof (args[0]);
  121.   static gint nreturn_vals = 0;
  122.  
  123.   gimp_install_procedure ("plug_in_sel_gauss",
  124.               "Applies a selective gaussian blur to the specified drawable.",
  125.               "This filter functions similar to the regular gaussian blur filter except that neighbouring pixels that differ more than the given maxdelta parameter will not be blended with. This way with the correct parameters, an image can be smoothed out without losing details. However, this filter can be rather slow.",
  126.               "Thom van Os",
  127.               "Thom van Os",
  128.               "1999",
  129.               "<Image>/Filters/Blur/Selective Gaussian Blur",
  130.               "RGB*, GRAY*",
  131.               PROC_PLUG_IN,
  132.               nargs, nreturn_vals,
  133.               args, return_vals);
  134. }
  135.  
  136. static void run (
  137.     gchar    *name,
  138.     gint    nparams,
  139.     GParam    *param,
  140.     gint    *nreturn_vals,
  141.     GParam    **return_vals)
  142. {
  143.     static GParam    values[1];
  144.     GRunModeType    run_mode;
  145.     GStatusType    status = STATUS_SUCCESS;
  146.     GDrawable    *drawable;
  147.     gdouble    radius;
  148.  
  149.     run_mode = param[0].data.d_int32;
  150.  
  151.     *nreturn_vals = 1;
  152.     *return_vals = values;
  153.  
  154.     values[0].type = PARAM_STATUS;
  155.     values[0].data.d_status = status;
  156.  
  157.     switch (run_mode)
  158.     {
  159.       case RUN_INTERACTIVE:
  160.         /* Possibly retrieve data */
  161.         gimp_get_data ("plug_in_sel_gauss", &bvals);
  162.  
  163.         /* First acquire information with a dialog */
  164.         if (! sel_gauss_dialog ())
  165.             return;
  166.         break;
  167.  
  168.       case RUN_NONINTERACTIVE:
  169.         /* Make sure all the arguments are there! */
  170.         if (nparams != 7)
  171.             status = STATUS_CALLING_ERROR;
  172.         if (status == STATUS_SUCCESS)
  173.         {
  174.             bvals.radius = param[3].data.d_float;
  175.             bvals.maxdelta = (param[4].data.d_int32);
  176.             if (bvals.maxdelta < 0) bvals.maxdelta = 0;
  177.             else if (bvals.maxdelta > 255) bvals.maxdelta = 255;
  178.         }
  179.         if (status == STATUS_SUCCESS && (bvals.radius < 1.0))
  180.             status = STATUS_CALLING_ERROR;
  181.         break;
  182.  
  183.       case RUN_WITH_LAST_VALS:
  184.         /* Possibly retrieve data */
  185.         gimp_get_data ("plug_in_sel_gauss", &bvals);
  186.         break;
  187.  
  188.       default:
  189.         break;
  190.     }
  191.  
  192.     if (status != STATUS_SUCCESS) {
  193.         values[0].data.d_status = status;
  194.         return;
  195.     }
  196.  
  197.     /* Get the specified drawable */
  198.     drawable = gimp_drawable_get (param[2].data.d_drawable);
  199.  
  200.     /* Make sure that the drawable is gray or RGB color */
  201.     if (gimp_drawable_color (drawable->id) ||
  202.         gimp_drawable_is_gray (drawable->id)) {
  203.  
  204.         gimp_progress_init ("Selective Gaussian Blur");
  205.  
  206.         radius = fabs (bvals.radius) + 1.0;
  207.  
  208.         /* run the gaussian blur */
  209.         sel_gauss (drawable, radius, bvals.maxdelta);
  210.  
  211.         /* Store data */
  212.         if (run_mode == RUN_INTERACTIVE)
  213.             gimp_set_data ("plug_in_sel_gauss",
  214.                 &bvals, sizeof (BlurValues));
  215.  
  216.         if (run_mode != RUN_NONINTERACTIVE)
  217.             gimp_displays_flush ();
  218.     } else {
  219.         gimp_message ("sel_gauss: cannot operate on indexed color images");
  220.         status = STATUS_EXECUTION_ERROR;
  221.     }
  222.  
  223.     gimp_drawable_detach (drawable);
  224.     values[0].data.d_status = status;
  225. }
  226.  
  227. static gint sel_gauss_dialog ()
  228. {
  229.     GtkWidget *dlg;
  230.     GtkWidget *label;
  231.     GtkWidget *entry;
  232.     GtkWidget *button;
  233.     GtkWidget *frame;
  234.     GtkWidget *vbox;
  235.     GtkWidget *hbox;
  236.     gchar buffer[12];
  237.     gchar **argv;
  238.     gint argc;
  239.  
  240.     argc = 1;
  241.     argv = g_new (gchar *, 1);
  242.     argv[0] = g_strdup ("gauss");
  243.  
  244.     gtk_init (&argc, &argv);
  245.     gtk_rc_parse (gimp_gtkrc ());
  246.  
  247.     dlg = gtk_dialog_new ();
  248.     gtk_window_set_title (GTK_WINDOW (dlg), "Selective Gaussian Blur");
  249.     gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
  250.     gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  251.         (GtkSignalFunc) sel_gauss_close_callback, NULL);
  252.  
  253.     /* Action area */
  254.     button = gtk_button_new_with_label ("OK");
  255.     GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  256.     gtk_signal_connect (GTK_OBJECT (button), "clicked",
  257.         (GtkSignalFunc) sel_gauss_ok_callback, dlg);
  258.     gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area),
  259.         button, TRUE, TRUE, 0);
  260.     gtk_widget_grab_default (button);
  261.     gtk_widget_show (button);
  262.  
  263.     button = gtk_button_new_with_label ("Cancel");
  264.     GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  265.     gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
  266.         (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (dlg));
  267.     gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area),
  268.         button, TRUE, TRUE, 0);
  269.     gtk_widget_show (button);
  270.  
  271.     /* parameter settings */
  272.     frame = gtk_frame_new ("Parameter Settings");
  273.     gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  274.     gtk_container_border_width (GTK_CONTAINER (frame), 10);
  275.     gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame,
  276.         TRUE, TRUE, 0);
  277.     vbox = gtk_vbox_new (FALSE, 5);
  278.     gtk_container_border_width (GTK_CONTAINER (vbox), 10);
  279.     gtk_container_add (GTK_CONTAINER (frame), vbox);
  280.  
  281.     hbox = gtk_hbox_new (FALSE, 5);
  282.     gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
  283.  
  284.     label = gtk_label_new ("Blur Radius: ");
  285.     gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
  286.     gtk_widget_show (label);
  287.  
  288.     entry = gtk_entry_new ();
  289.     gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
  290.     gtk_widget_set_usize (entry, ENTRY_WIDTH, 0);
  291.     sprintf (buffer, "%f", bvals.radius);
  292.     gtk_entry_set_text (GTK_ENTRY (entry), buffer);
  293.     gtk_signal_connect (GTK_OBJECT (entry), "changed",
  294.         (GtkSignalFunc) sel_gauss_entry_callback, NULL);
  295.     gtk_widget_show (entry);
  296.     gtk_widget_show (hbox);
  297.  
  298.     hbox = gtk_hbox_new (FALSE, 5);
  299.     gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
  300.  
  301.     label = gtk_label_new ("Max. delta: ");
  302.     gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
  303.     gtk_widget_show (label);
  304.  
  305.     entry = gtk_entry_new ();
  306.     gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
  307.     gtk_widget_set_usize (entry, ENTRY_WIDTH, 0);
  308.     sprintf (buffer, "%d", bvals.maxdelta);
  309.     gtk_entry_set_text (GTK_ENTRY (entry), buffer);
  310.     gtk_signal_connect (GTK_OBJECT (entry), "changed",
  311.         (GtkSignalFunc) sel_gauss_delta_callback, NULL);
  312.     gtk_widget_show (entry);
  313.     gtk_widget_show (hbox);
  314.  
  315.     gtk_widget_show (vbox);
  316.     gtk_widget_show (frame);
  317.     gtk_widget_show (dlg);
  318.  
  319.     gtk_main ();
  320.     gdk_flush ();
  321.  
  322.     return bint.run;
  323. }
  324.  
  325. void init_matrix(gdouble radius, gdouble **mat, gint num)
  326. {
  327.     int dx, dy;
  328.     gdouble sd, c1, c2;
  329.  
  330.     /* This formula isn't really correct, but it'll do */
  331.     sd = radius / 3.329042969;
  332.     c1 = 1.0 / sqrt(2.0 * M_PI * sd);
  333.     c2 = -2.0 * (sd * sd);
  334.  
  335.     for (dy=0; dy<num; dy++) {
  336.         for (dx=dy; dx<num; dx++) {
  337.             mat[dx][dy] = c1 * exp(((dx*dx)+ (dy*dy))/ c2);
  338.             mat[dy][dx] = mat[dx][dy];
  339.         }
  340.     }
  341. }
  342.  
  343. static void matrixmult(guchar *src, guchar *dest, gint width, gint height,
  344.     gdouble **mat, gint numrad, gint bytes, gint has_alpha, gint maxdelta)
  345. {
  346.     gint i, j, b, nb, x, y;
  347.     gint six, dix, tmp;
  348.     gdouble sum, fact, d, alpha=1.0;
  349.  
  350.     nb = bytes - (has_alpha?1:0);
  351.  
  352.     for (y = 0; y< height; y++) {
  353.       for (x = 0; x< width; x++) {
  354.         dix = (bytes*((width*y)+x));
  355.         if (has_alpha)
  356.         dest[dix+nb] = src[dix+nb];
  357.         for (b=0; b<nb; b++) {
  358.         sum = 0.0;
  359.         fact = 0.0;
  360.         for (i= 1-numrad; i<numrad; i++) {
  361.             if (((x+i)< 0) || ((x+i)>= width))
  362.                 continue;
  363.             for (j= 1-numrad; j<numrad; j++) {
  364.                 if (((y+j)<0)||((y+j)>=height))
  365.                     continue;
  366.                 six = (bytes*((width*(y+j))+x+i));
  367.                 if (has_alpha) {
  368.                     if (!src[six+nb])
  369.                         continue;
  370.                     alpha = (double)src[six+nb] / 255.0;
  371.                 }
  372.                 tmp = src[dix+b] - src[six+b];
  373.                 if ((tmp>maxdelta)||
  374.                     (tmp<-maxdelta))
  375.                     continue;
  376.                 d = mat[ABS(i)][ABS(j)];
  377.                 if (has_alpha) {
  378.                     d *= alpha;
  379.                 }
  380.                 sum += d * src[six+b];
  381.                 fact += d;
  382.             }
  383.         }
  384.         if (fact == 0.0)
  385.             dest[dix+b] = src[dix+b];
  386.         else
  387.             dest[dix+b] = sum/fact;
  388.         }
  389.       }
  390.       if (!(y%5))
  391.           gimp_progress_update((double)y / (double)height);
  392.     }
  393. }
  394.  
  395. static void sel_gauss (GDrawable *drawable, gdouble radius, gint maxdelta)
  396. {
  397.     GPixelRgn src_rgn, dest_rgn;
  398.     gint width, height;
  399.     gint bytes;
  400.     gint has_alpha;
  401.     guchar *dest;
  402.     guchar *src;
  403.     gint x1, y1, x2, y2;
  404.     gint i;
  405.     gdouble **mat;
  406.     gint numrad;
  407.  
  408.     gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  409.  
  410.     width = (x2 - x1);
  411.     height = (y2 - y1);
  412.     bytes = drawable->bpp;
  413.     has_alpha = gimp_drawable_has_alpha(drawable->id);
  414.  
  415.     if ((width<1)||(height<1)||(bytes<1))
  416.         return;
  417.  
  418.     numrad = (gint)(radius + 1.0);
  419.     mat = (gdouble **) g_malloc (numrad * sizeof(gdouble *));
  420.     for (i=0; i<numrad; i++)
  421.         mat[i] = (gdouble *) g_malloc (numrad * sizeof(gdouble));
  422.       init_matrix(radius, mat, numrad);
  423.  
  424.     src = (guchar *) g_malloc (width * height * bytes);
  425.     dest = (guchar *) g_malloc (width * height * bytes);
  426.  
  427.     gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, drawable->width,
  428.         drawable->height, FALSE, FALSE);
  429.  
  430.     gimp_pixel_rgn_get_rect (&src_rgn, src, x1, y1, width, height);
  431.  
  432.     matrixmult(src, dest, width, height, mat, numrad,
  433.         bytes, has_alpha, maxdelta);
  434.  
  435.     gimp_pixel_rgn_init (&dest_rgn, drawable, x1, y1, width, height,
  436.         TRUE, TRUE);
  437.     gimp_pixel_rgn_set_rect (&dest_rgn, dest, x1, y1, width, height);
  438.  
  439.     /*  merge the shadow, update the drawable  */
  440.     gimp_drawable_flush (drawable);
  441.     gimp_drawable_merge_shadow (drawable->id, TRUE);
  442.     gimp_drawable_update (drawable->id, x1, y1, width, height);
  443.  
  444.     /* free up buffers */
  445.     g_free (src);
  446.     g_free (dest);
  447.     for (i=0; i<numrad; i++)
  448.         g_free (mat[i]);
  449.     g_free (mat);
  450. }
  451.  
  452. /*  Gauss interface functions  */
  453.  
  454. static void sel_gauss_close_callback (GtkWidget *widget, gpointer data)
  455. {
  456.     gtk_main_quit ();
  457. }
  458.  
  459. static void sel_gauss_ok_callback(GtkWidget *widget, gpointer data)
  460. {
  461.     bint.run = TRUE;
  462.     gtk_widget_destroy (GTK_WIDGET (data));
  463. }
  464.  
  465. static void sel_gauss_entry_callback(GtkWidget *widget, gpointer data)
  466. {
  467.     bvals.radius = atof(gtk_entry_get_text(GTK_ENTRY(widget)));
  468.     if (bvals.radius < 1.0)
  469.         bvals.radius = 1.0;
  470. }
  471.  
  472. static void sel_gauss_delta_callback(GtkWidget *widget, gpointer data)
  473. {
  474.     bvals.maxdelta = atoi(gtk_entry_get_text(GTK_ENTRY(widget)));
  475.     if (bvals.maxdelta < 0)
  476.         bvals.maxdelta = 0;
  477.     if (bvals.maxdelta > 255)
  478.         bvals.maxdelta = 255;
  479. }
  480.