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 / sobel.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  12.6 KB  |  455 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19.  
  20. /* This plugin by thorsten@arch.usyd.edu.au           */
  21. /* Based on S&P's Gauss and Blur filters              */
  22.  
  23.  
  24. /* updated 11/04/97:
  25.    don't use rint;
  26.    if gamma-channel: set to white if at least one colour channel is >15 */
  27.  
  28. /* Update 3/10/97:
  29.    #ifdef Max and Min, 
  30.    save old values
  31.    correct 'cancel' behaviour */
  32.  
  33. #include "config.h"
  34.  
  35. #include <stdio.h>
  36. #include <stdlib.h>
  37.  
  38. #include <gtk/gtk.h>
  39.  
  40. #include <libgimp/gimp.h>
  41. #include <libgimp/gimpui.h>
  42.  
  43. #include "libgimp/stdplugins-intl.h"
  44.  
  45.  
  46. typedef struct
  47. {
  48.   gint horizontal;
  49.   gint vertical;
  50.   gint keep_sign;
  51. } SobelValues;
  52.  
  53. typedef struct
  54. {
  55.   gint run;
  56. } SobelInterface;
  57.  
  58.  
  59. /* Declare local functions.
  60.  */
  61. static void      query  (void);
  62. static void      run    (gchar   *name,
  63.              gint     nparams,
  64.              GimpParam  *param,
  65.              gint    *nreturn_vals,
  66.              GimpParam **return_vals);
  67.  
  68. static void      sobel  (GimpDrawable *drawable,
  69.              gint       horizontal,
  70.              gint       vertical,
  71.              gint       keep_sign);
  72.  
  73. /*
  74.  * Sobel interface
  75.  */
  76. static gint      sobel_dialog (void);
  77.  
  78. /*
  79.  * Sobel helper functions
  80.  */
  81. static void      sobel_ok_callback     (GtkWidget *widget,
  82.                     gpointer   data);
  83.  
  84. static void      sobel_prepare_row (GimpPixelRgn  *pixel_rgn,
  85.                     guchar     *data,
  86.                     gint        x,
  87.                     gint        y,
  88.                     gint        w);
  89.  
  90.  
  91. GimpPlugInInfo PLUG_IN_INFO =
  92. {
  93.   NULL,  /* init_proc  */
  94.   NULL,  /* quit_proc  */
  95.   query, /* query_proc */
  96.   run,   /* run_proc   */
  97. };
  98.  
  99. static SobelValues bvals =
  100. {
  101.   TRUE,  /*  horizontal sobel  */
  102.   TRUE,  /*  vertical sobel    */
  103.   TRUE   /*  keep sign         */
  104. };
  105.  
  106. static SobelInterface bint =
  107. {
  108.   FALSE  /*  run  */
  109. };
  110.  
  111.  
  112. MAIN ()
  113.  
  114. static void
  115. query (void)
  116. {
  117.   static GimpParamDef args[] =
  118.   {
  119.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  120.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  121.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  122.     { GIMP_PDB_INT32, "horizontal", "Sobel in horizontal direction" },
  123.     { GIMP_PDB_INT32, "vertical", "Sobel in vertical direction" },
  124.     { GIMP_PDB_INT32, "keep_sign", "Keep sign of result (one direction only)" }
  125.   };
  126.   static gint nargs = sizeof (args) / sizeof (args[0]);
  127.  
  128.   gimp_install_procedure ("plug_in_sobel",
  129.               "Edge Detection with Sobel Operation",
  130.               "This plugin calculates the gradient with a sobel "
  131.               "operator. The user can specify which direction to "
  132.               "use. When both directions are used, the result is "
  133.               "the RMS of the two gradients; if only one direction "
  134.               "is used, the result either the absolut value of the "
  135.               "gradient, or 127 + gradient (if the 'keep sign' "
  136.               "switch is on). This way, information about the "
  137.               "direction of the gradient is preserved. Resulting "
  138.               "images are not autoscaled.",
  139.               "Thorsten Schnier",
  140.               "Thorsten Schnier",
  141.               "1997",
  142.               N_("<Image>/Filters/Edge-Detect/Sobel..."),
  143.               "RGB*, GRAY*",
  144.               GIMP_PLUGIN,
  145.               nargs, 0,
  146.               args, NULL);
  147. }
  148.  
  149. static void
  150. run (gchar   *name,
  151.      gint     nparams,
  152.      GimpParam  *param,
  153.      gint    *nreturn_vals,
  154.      GimpParam **return_vals)
  155. {
  156.   static GimpParam values[1];
  157.   GimpDrawable *drawable;
  158.   GimpRunModeType run_mode;
  159.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  160.  
  161.   run_mode = param[0].data.d_int32;
  162.  
  163.   *nreturn_vals = 1;
  164.   *return_vals = values;
  165.  
  166.   values[0].type = GIMP_PDB_STATUS;
  167.   values[0].data.d_status = status;
  168.  
  169.   switch (run_mode)
  170.    {
  171.     case GIMP_RUN_INTERACTIVE:
  172.       INIT_I18N_UI();
  173.       /*  Possibly retrieve data  */
  174.       gimp_get_data ("plug_in_sobel", &bvals);
  175.  
  176.       /*  First acquire information with a dialog  */
  177.       if (! sobel_dialog ())
  178.     return;
  179.       break;
  180.  
  181.     case GIMP_RUN_NONINTERACTIVE:
  182.       INIT_I18N();
  183.       /*  Make sure all the arguments are there!  */
  184.       if (nparams != 6)
  185.     {
  186.       status = GIMP_PDB_CALLING_ERROR;
  187.     }
  188.       else
  189.     {
  190.       bvals.horizontal = (param[4].data.d_int32) ? TRUE : FALSE;
  191.       bvals.vertical   = (param[5].data.d_int32) ? TRUE : FALSE;
  192.       bvals.keep_sign  = (param[6].data.d_int32) ? TRUE : FALSE;
  193.     }
  194.       break;
  195.  
  196.     case GIMP_RUN_WITH_LAST_VALS:
  197.       INIT_I18N();
  198.       /*  Possibly retrieve data  */
  199.       gimp_get_data ("plug_in_sobel", &bvals);
  200.       break;
  201.  
  202.     default:
  203.       break;
  204.     }
  205.  
  206.  
  207.   /*  Get the specified drawable  */
  208.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  209.  
  210.   /*  Make sure that the drawable is gray or RGB color  */
  211.   if (gimp_drawable_is_rgb (drawable->id) ||
  212.       gimp_drawable_is_gray (drawable->id))
  213.     {
  214.       gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
  215.       sobel (drawable, bvals.horizontal, bvals.vertical, bvals.keep_sign);
  216.  
  217.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  218.     gimp_displays_flush ();
  219.  
  220.       
  221.       /*  Store data  */
  222.       if (run_mode == GIMP_RUN_INTERACTIVE)
  223.     gimp_set_data ("plug_in_sobel", &bvals, sizeof (bvals));    
  224.     }
  225.   else
  226.     {
  227.       /* g_message ("sobel: cannot operate on indexed color images"); */
  228.       status = GIMP_PDB_EXECUTION_ERROR;
  229.     }
  230.  
  231.   gimp_drawable_detach (drawable);
  232.  
  233.   values[0].data.d_status = status;
  234. }
  235.  
  236. static gint
  237. sobel_dialog (void)
  238. {
  239.   GtkWidget *dlg;
  240.   GtkWidget *toggle;
  241.   GtkWidget *frame;
  242.   GtkWidget *vbox;
  243.  
  244.   gimp_ui_init ("sobel", FALSE);
  245.  
  246.   dlg = gimp_dialog_new (_("Sobel Edge Detection"), "sobel",
  247.              gimp_standard_help_func, "filters/sobel.html",
  248.              GTK_WIN_POS_MOUSE,
  249.              FALSE, TRUE, FALSE,
  250.  
  251.              _("OK"), sobel_ok_callback,
  252.              NULL, NULL, NULL, TRUE, FALSE,
  253.              _("Cancel"), gtk_widget_destroy,
  254.              NULL, 1, NULL, FALSE, TRUE,
  255.  
  256.              NULL);
  257.  
  258.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  259.               GTK_SIGNAL_FUNC (gtk_main_quit),
  260.               NULL);
  261.  
  262.   /*  parameter settings  */
  263.   frame = gtk_frame_new ( _("Parameter Settings"));
  264.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  265.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  266.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  267.  
  268.   vbox = gtk_vbox_new (FALSE, 1);
  269.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
  270.   gtk_container_add (GTK_CONTAINER (frame), vbox);
  271.  
  272.   toggle = gtk_check_button_new_with_label (_("Sobel Horizontally"));
  273.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  274.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  275.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  276.               &bvals.horizontal);
  277.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.horizontal);
  278.   gtk_widget_show (toggle);
  279.  
  280.   toggle = gtk_check_button_new_with_label (_("Sobel Vertically"));
  281.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  282.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  283.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  284.               &bvals.vertical);
  285.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.vertical);
  286.   gtk_widget_show (toggle);
  287.  
  288.   toggle = gtk_check_button_new_with_label (_("Keep Sign of Result (one Direction only)"));
  289.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  290.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  291.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  292.               &bvals.keep_sign);
  293.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.vertical);
  294.   gtk_widget_show (toggle);
  295.  
  296.   gtk_widget_show (vbox);
  297.   gtk_widget_show (frame);
  298.   gtk_widget_show (dlg);
  299.  
  300.   gtk_main ();
  301.   gdk_flush ();
  302.  
  303.   return bint.run;
  304. }
  305.  
  306. static void
  307. sobel_prepare_row (GimpPixelRgn *pixel_rgn,
  308.            guchar    *data,
  309.            gint       x,
  310.            gint       y,
  311.            gint       w)
  312. {
  313.   gint b;
  314.  
  315.   if (y == 0)
  316.     gimp_pixel_rgn_get_row (pixel_rgn, data, x, (y + 1), w);
  317.   else if (y == pixel_rgn->h)
  318.     gimp_pixel_rgn_get_row (pixel_rgn, data, x, (y - 1), w);
  319.   else
  320.     gimp_pixel_rgn_get_row (pixel_rgn, data, x, y, w);
  321.  
  322.   /*  Fill in edge pixels  */
  323.   for (b = 0; b < pixel_rgn->bpp; b++)
  324.     {
  325.       data[-(int)pixel_rgn->bpp + b] = data[b];
  326.       data[w * pixel_rgn->bpp + b] = data[(w - 1) * pixel_rgn->bpp + b];
  327.     }
  328. }
  329.  
  330. #define SIGN(a) (((a) > 0) ? 1 : -1)
  331. #define RMS(a,b) (sqrt (pow ((a),2) + pow ((b), 2)))
  332.  
  333. static void
  334. sobel (GimpDrawable *drawable,
  335.        gint       do_horizontal,
  336.        gint       do_vertical,
  337.        gint       keep_sign)
  338. {
  339.   GimpPixelRgn srcPR, destPR;
  340.   gint    width, height;
  341.   gint    bytes;
  342.   gint    gradient, hor_gradient, ver_gradient;
  343.   guchar *dest, *d;
  344.   guchar *prev_row, *pr;
  345.   guchar *cur_row, *cr;
  346.   guchar *next_row, *nr;
  347.   guchar *tmp;
  348.   gint    row, col;
  349.   gint    x1, y1, x2, y2;
  350.   gint    alpha;
  351.   gint    counter;
  352.  
  353.   /* Get the input area. This is the bounding box of the selection in
  354.    *  the image (or the entire image if there is no selection). Only
  355.    *  operating on the input area is simply an optimization. It doesn't
  356.    *  need to be done for correct operation. (It simply makes it go
  357.    *  faster, since fewer pixels need to be operated on).
  358.    */
  359.  
  360.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  361.   gimp_progress_init (_("Sobel Edge Detecting..."));
  362.  
  363.   /* Get the size of the input image. (This will/must be the same
  364.    *  as the size of the output image.
  365.    */
  366.   width  = drawable->width;
  367.   height = drawable->height;
  368.   bytes  = drawable->bpp;
  369.   alpha  = gimp_drawable_has_alpha (drawable -> id);
  370.  
  371.   /*  allocate row buffers  */
  372.   prev_row = g_new (guchar, (x2 - x1 + 2) * bytes);
  373.   cur_row  = g_new (guchar, (x2 - x1 + 2) * bytes);
  374.   next_row = g_new (guchar, (x2 - x1 + 2) * bytes);
  375.   dest     = g_new (guchar, (x2 - x1) * bytes);
  376.  
  377.   /*  initialize the pixel regions  */
  378.   gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE);
  379.   gimp_pixel_rgn_init (&destPR, drawable, 0, 0, width, height, TRUE, TRUE);
  380.  
  381.   pr = prev_row + bytes;
  382.   cr = cur_row  + bytes;
  383.   nr = next_row + bytes;
  384.  
  385.   sobel_prepare_row (&srcPR, pr, x1, y1 - 1, (x2 - x1));
  386.   sobel_prepare_row (&srcPR, cr, x1, y1, (x2 - x1));
  387.   counter =0;
  388.   /*  loop through the rows, applying the sobel convolution  */
  389.   for (row = y1; row < y2; row++)
  390.     {
  391.       /*  prepare the next row  */
  392.       sobel_prepare_row (&srcPR, nr, x1, row + 1, (x2 - x1));
  393.  
  394.       d = dest;
  395.       for (col = 0; col < (x2 - x1) * bytes; col++)
  396.     {
  397.       hor_gradient = (do_horizontal ?
  398.               ((pr[col - bytes] +  2 * pr[col] + pr[col + bytes]) -
  399.                (nr[col - bytes] + 2 * nr[col] + nr[col + bytes]))
  400.               : 0);
  401.       ver_gradient = (do_vertical ?
  402.               ((pr[col - bytes] + 2 * cr[col - bytes] + nr[col - bytes]) -
  403.                (pr[col + bytes] + 2 * cr[col + bytes] + nr[col + bytes]))
  404.               : 0);
  405.       gradient = (do_vertical && do_horizontal) ?
  406.         (ROUND (RMS (hor_gradient, ver_gradient)) / 5.66) /* always >0 */
  407.         : (keep_sign ? (127 + (ROUND ((hor_gradient + ver_gradient) / 8.0)))
  408.            : (ROUND (abs (hor_gradient + ver_gradient) / 4.0)));
  409.  
  410.       if (alpha && (((col + 1) % bytes) == 0))
  411.         { /* the alpha channel */
  412.           *d++ = (counter == 0) ? 0 : 255;
  413.           counter = 0;
  414.         }
  415.       else
  416.         {
  417.           *d++ = gradient;
  418.           if (gradient > 10) counter ++;
  419.         }
  420.     }
  421.       /*  store the dest  */
  422.       gimp_pixel_rgn_set_row (&destPR, dest, x1, row, (x2 - x1));
  423.  
  424.       /*  shuffle the row pointers  */
  425.       tmp = pr;
  426.       pr = cr;
  427.       cr = nr;
  428.       nr = tmp;
  429.  
  430.       if ((row % 5) == 0)
  431.     gimp_progress_update ((double) row / (double) (y2 - y1));
  432.     }
  433.  
  434.   /*  update the sobeled region  */
  435.   gimp_drawable_flush (drawable);
  436.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  437.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  438.  
  439.   g_free (prev_row);
  440.   g_free (cur_row);
  441.   g_free (next_row);
  442.   g_free (dest);
  443. }
  444.  
  445. /*  Sobel interface functions  */
  446.  
  447. static void
  448. sobel_ok_callback (GtkWidget *widget,
  449.            gpointer   data)
  450. {
  451.   bint.run = TRUE;
  452.  
  453.   gtk_widget_destroy (GTK_WIDGET (data));
  454. }
  455.