home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / common / sel_gauss.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-28  |  11.4 KB  |  453 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 "config.h"
  38.  
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41.  
  42. #include <gtk/gtk.h>
  43.  
  44. #include <libgimp/gimp.h>
  45. #include <libgimp/gimpui.h>
  46.  
  47. #include "libgimp/stdplugins-intl.h"
  48.  
  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.               GimpParam     *param,
  68.               gint      *nreturn_vals,
  69.               GimpParam   **return_vals);
  70.  
  71. static void   sel_gauss (GimpDrawable *drawable,
  72.              gdouble    radius,
  73.              gint       maxdelta);
  74.  
  75. static gint   sel_gauss_dialog      (void);
  76. static void   sel_gauss_ok_callback (GtkWidget *widget,
  77.                      gpointer   data);
  78.  
  79. GimpPlugInInfo PLUG_IN_INFO =
  80. {
  81.   NULL,  /* init_proc  */
  82.   NULL,  /* quit_proc  */
  83.   query, /* query_proc */
  84.   run,   /* run_proc   */
  85. };
  86.  
  87. static BlurValues bvals =
  88. {
  89.   5.0, /* radius   */
  90.   50   /* maxdelta */
  91. };
  92.  
  93. static BlurInterface bint =
  94. {
  95.   FALSE  /* run */
  96. };
  97.  
  98. MAIN ()
  99.  
  100. static void
  101. query (void)
  102. {
  103.   static GimpParamDef args[] =
  104.   {
  105.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  106.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  107.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  108.     { GIMP_PDB_FLOAT, "radius", "Radius of gaussian blur (in pixels > 1.0)" },
  109.     { GIMP_PDB_INT32, "maxdelta", "Maximum delta" }
  110.   };
  111.   static gint nargs = sizeof (args) / sizeof (args[0]);
  112.  
  113.   gimp_install_procedure ("plug_in_sel_gauss",
  114.               "Applies a selective gaussian blur to the specified drawable.",
  115.               "This filter functions similar to the regular "
  116.               "gaussian blur filter except that neighbouring "
  117.               "pixels that differ more than the given maxdelta "
  118.               "parameter will not be blended with. This way with "
  119.               "the correct parameters, an image can be smoothed "
  120.               "out without losing details. However, this filter "
  121.               "can be rather slow.",
  122.               "Thom van Os",
  123.               "Thom van Os",
  124.               "1999",
  125.               N_("<Image>/Filters/Blur/Selective Gaussian Blur..."),
  126.               "RGB*, GRAY*",
  127.               GIMP_PLUGIN,
  128.               nargs, 0,
  129.               args, NULL);
  130. }
  131.  
  132. static void
  133. run (gchar   *name,
  134.      gint     nparams,
  135.      GimpParam  *param,
  136.      gint    *nreturn_vals,
  137.      GimpParam **return_vals)
  138. {
  139.   static GimpParam    values[1];
  140.   GimpRunModeType    run_mode;
  141.   GimpPDBStatusType    status = GIMP_PDB_SUCCESS;
  142.   GimpDrawable    *drawable;
  143.   gdouble    radius;
  144.  
  145.   run_mode = param[0].data.d_int32;
  146.  
  147.   *nreturn_vals = 1;
  148.   *return_vals = values;
  149.  
  150.   INIT_I18N_UI(); 
  151.  
  152.   values[0].type = GIMP_PDB_STATUS;
  153.   values[0].data.d_status = status;
  154.  
  155.   switch (run_mode)
  156.     {
  157.     case GIMP_RUN_INTERACTIVE:
  158.       /* Possibly retrieve data */
  159.       gimp_get_data ("plug_in_sel_gauss", &bvals);
  160.  
  161.       /* First acquire information with a dialog */
  162.       if (! sel_gauss_dialog ())
  163.     return;
  164.       break;
  165.  
  166.     case GIMP_RUN_NONINTERACTIVE:
  167.       /* Make sure all the arguments are there! */
  168.       if (nparams != 7)
  169.     status = GIMP_PDB_CALLING_ERROR;
  170.       if (status == GIMP_PDB_SUCCESS)
  171.     {
  172.       bvals.radius   = param[3].data.d_float;
  173.       bvals.maxdelta = CLAMP (param[4].data.d_int32, 0, 255);
  174.     }
  175.       if (status == GIMP_PDB_SUCCESS && (bvals.radius < 1.0))
  176.     status = GIMP_PDB_CALLING_ERROR;
  177.       break;
  178.  
  179.     case GIMP_RUN_WITH_LAST_VALS:
  180.       /* Possibly retrieve data */
  181.       gimp_get_data ("plug_in_sel_gauss", &bvals);
  182.       break;
  183.  
  184.     default:
  185.       break;
  186.     }
  187.  
  188.   if (status != GIMP_PDB_SUCCESS)
  189.     {
  190.       values[0].data.d_status = status;
  191.       return;
  192.     }
  193.  
  194.   /* Get the specified drawable */
  195.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  196.  
  197.   /* Make sure that the drawable is gray or RGB color */
  198.   if (gimp_drawable_is_rgb (drawable->id) ||
  199.       gimp_drawable_is_gray (drawable->id))
  200.     {
  201.       gimp_progress_init (_("Selective Gaussian Blur"));
  202.  
  203.       radius = fabs (bvals.radius) + 1.0;
  204.  
  205.       /* run the gaussian blur */
  206.       sel_gauss (drawable, radius, bvals.maxdelta);
  207.  
  208.       /* Store data */
  209.       if (run_mode == GIMP_RUN_INTERACTIVE)
  210.     gimp_set_data ("plug_in_sel_gauss",
  211.                &bvals, sizeof (BlurValues));
  212.  
  213.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  214.     gimp_displays_flush ();
  215.     }
  216.   else
  217.     {
  218.       gimp_message (_("sel_gauss: Cannot operate on indexed color images"));
  219.       status = GIMP_PDB_EXECUTION_ERROR;
  220.     }
  221.  
  222.   gimp_drawable_detach (drawable);
  223.   values[0].data.d_status = status;
  224. }
  225.  
  226. static gint
  227. sel_gauss_dialog (void)
  228. {
  229.   GtkWidget *dlg;
  230.   GtkWidget *frame;
  231.   GtkWidget *table;
  232.   GtkWidget *spinbutton;
  233.   GtkObject *adj;
  234.  
  235.   gimp_ui_init ("sel_gauss", FALSE);
  236.  
  237.   dlg = gimp_dialog_new (_("Selective Gaussian Blur"), "sel_gauss",
  238.              gimp_standard_help_func, "filters/sel_gauss.html",
  239.              GTK_WIN_POS_MOUSE,
  240.              FALSE, TRUE, FALSE,
  241.  
  242.              _("OK"), sel_gauss_ok_callback,
  243.              NULL, NULL, NULL, TRUE, FALSE,
  244.              _("Cancel"), gtk_widget_destroy,
  245.              NULL, 1, NULL, FALSE, TRUE,
  246.  
  247.              NULL);
  248.  
  249.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  250.               GTK_SIGNAL_FUNC (gtk_main_quit),
  251.               NULL);
  252.  
  253.   /* parameter settings */
  254.   frame = gtk_frame_new (_("Parameter Settings"));
  255.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  256.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  257.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame,
  258.               TRUE, TRUE, 0);
  259.  
  260.   table = gtk_table_new (2, 3, FALSE);
  261.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  262.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  263.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  264.   gtk_container_add (GTK_CONTAINER (frame), table);
  265.  
  266.   spinbutton = gimp_spin_button_new (&adj,
  267.                      bvals.radius, 1.0, G_MAXINT, 1.0, 5.0,
  268.                      0, 1, 2);
  269.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
  270.                  _("Blur Radius:"), 1.0, 0.5,
  271.                  spinbutton, 1, TRUE);
  272.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  273.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  274.               &bvals.radius);
  275.  
  276.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  277.                   _("Max. Delta:"), 128, 0,
  278.                   bvals.maxdelta, 0, 255, 1, 8, 0,
  279.                   TRUE, 0, 0,
  280.                   FALSE, FALSE);
  281.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  282.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  283.               &bvals.maxdelta);
  284.  
  285.   gtk_widget_show (table);
  286.   gtk_widget_show (frame);
  287.   gtk_widget_show (dlg);
  288.  
  289.   gtk_main ();
  290.   gdk_flush ();
  291.  
  292.   return bint.run;
  293. }
  294.  
  295. static void
  296. sel_gauss_ok_callback (GtkWidget *widget,
  297.                gpointer   data)
  298. {
  299.   bint.run = TRUE;
  300.  
  301.   gtk_widget_destroy (GTK_WIDGET (data));
  302. }
  303.  
  304. static void
  305. init_matrix (gdouble   radius,
  306.          gdouble **mat,
  307.          gint      num)
  308. {
  309.   gint    dx, dy;
  310.   gdouble sd, c1, c2;
  311.  
  312.   /* This formula isn't really correct, but it'll do */
  313.   sd = radius / 3.329042969;
  314.   c1 = 1.0 / sqrt(2.0 * G_PI * sd);
  315.   c2 = -2.0 * (sd * sd);
  316.  
  317.   for (dy=0; dy<num; dy++)
  318.     {
  319.       for (dx=dy; dx<num; dx++)
  320.     {
  321.       mat[dx][dy] = c1 * exp(((dx*dx)+ (dy*dy))/ c2);
  322.       mat[dy][dx] = mat[dx][dy];
  323.     }
  324.     }
  325. }
  326.  
  327. static void
  328. matrixmult (guchar   *src,
  329.         guchar   *dest,
  330.         gint      width,
  331.         gint      height,
  332.         gdouble **mat,
  333.         gint      numrad,
  334.         gint      bytes,
  335.         gint      has_alpha,
  336.         gint      maxdelta)
  337. {
  338.   gint    i, j, b, nb, x, y;
  339.   gint    six, dix, tmp;
  340.   gdouble sum, fact, d, alpha=1.0;
  341.  
  342.   nb = bytes - (has_alpha?1:0);
  343.  
  344.   for (y = 0; y< height; y++)
  345.     {
  346.       for (x = 0; x< width; x++)
  347.     {
  348.       dix = (bytes*((width*y)+x));
  349.       if (has_alpha)
  350.         dest[dix+nb] = src[dix+nb];
  351.       for (b=0; b<nb; b++)
  352.         {
  353.           sum = 0.0;
  354.           fact = 0.0;
  355.           for (i= 1-numrad; i<numrad; i++)
  356.         {
  357.           if (((x+i)< 0) || ((x+i)>= width))
  358.             continue;
  359.           for (j= 1-numrad; j<numrad; j++)
  360.             {
  361.               if (((y+j)<0)||((y+j)>=height))
  362.             continue;
  363.               six = (bytes*((width*(y+j))+x+i));
  364.               if (has_alpha)
  365.             {
  366.               if (!src[six+nb])
  367.                 continue;
  368.               alpha = (double)src[six+nb] / 255.0;
  369.             }
  370.               tmp = src[dix+b] - src[six+b];
  371.               if ((tmp>maxdelta)||
  372.               (tmp<-maxdelta))
  373.             continue;
  374.               d = mat[ABS(i)][ABS(j)];
  375.               if (has_alpha)
  376.             {
  377.               d *= alpha;
  378.             }
  379.               sum += d * src[six+b];
  380.               fact += d;
  381.             }
  382.         }
  383.           if (fact == 0.0)
  384.         dest[dix+b] = src[dix+b];
  385.           else
  386.         dest[dix+b] = sum/fact;
  387.         }
  388.     }
  389.       if (!(y % 5))
  390.     gimp_progress_update((double)y / (double)height);
  391.     }
  392. }
  393.  
  394. static void
  395. sel_gauss (GimpDrawable *drawable,
  396.        gdouble    radius,
  397.        gint       maxdelta)
  398. {
  399.   GimpPixelRgn src_rgn, dest_rgn;
  400.   gint      width, height;
  401.   gint      bytes;
  402.   gint      has_alpha;
  403.   guchar   *dest;
  404.   guchar   *src;
  405.   gint      x1, y1, x2, y2;
  406.   gint      i;
  407.   gdouble **mat;
  408.   gint      numrad;
  409.  
  410.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  411.  
  412.   width  = (x2 - x1);
  413.   height = (y2 - y1);
  414.   bytes  = drawable->bpp;
  415.   has_alpha = gimp_drawable_has_alpha(drawable->id);
  416.  
  417.   if ((width < 1) || (height < 1) || (bytes < 1))
  418.     return;
  419.  
  420.   numrad = (gint) (radius + 1.0);
  421.   mat = g_new (gdouble *, numrad);
  422.   for (i = 0; i < numrad; i++)
  423.     mat[i] = g_new (gdouble, numrad);
  424.   init_matrix(radius, mat, numrad);
  425.  
  426.   src  = g_new (guchar, width * height * bytes);
  427.   dest = g_new (guchar, width * height * bytes);
  428.  
  429.   gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, drawable->width,
  430.                drawable->height, FALSE, FALSE);
  431.  
  432.   gimp_pixel_rgn_get_rect (&src_rgn, src, x1, y1, width, height);
  433.  
  434.   matrixmult (src, dest, width, height, mat, numrad,
  435.           bytes, has_alpha, maxdelta);
  436.  
  437.   gimp_pixel_rgn_init (&dest_rgn, drawable, x1, y1, width, height,
  438.                TRUE, TRUE);
  439.   gimp_pixel_rgn_set_rect (&dest_rgn, dest, x1, y1, width, height);
  440.  
  441.   /*  merge the shadow, update the drawable  */
  442.   gimp_drawable_flush (drawable);
  443.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  444.   gimp_drawable_update (drawable->id, x1, y1, width, height);
  445.  
  446.   /* free up buffers */
  447.   g_free (src);
  448.   g_free (dest);
  449.   for (i = 0; i < numrad; i++)
  450.     g_free (mat[i]);
  451.   g_free (mat);
  452. }
  453.