home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / app / fuzzy_select.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-17  |  17.1 KB  |  670 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. #include "config.h"
  20.  
  21. #include <stdlib.h>
  22.  
  23. #include <gtk/gtk.h>
  24.  
  25. #include "apptypes.h"
  26.  
  27. #include "appenv.h"
  28. #include "boundary.h"
  29. #include "cursorutil.h"
  30. #include "draw_core.h"
  31. #include "drawable.h"
  32. #include "edit_selection.h"
  33. #include "fuzzy_select.h"
  34. #include "gimage_mask.h"
  35. #include "gimprc.h"
  36. #include "gimpui.h"
  37. #include "gdisplay.h"
  38. #include "rect_select.h"
  39. #include "selection_options.h"
  40.  
  41. #include "tile.h"            /* ick. */
  42.  
  43. #include "libgimp/gimpmath.h"
  44. #include "libgimp/gimpintl.h"
  45.  
  46. /*  the fuzzy selection structures  */
  47.  
  48. typedef struct _FuzzySelect FuzzySelect;
  49. struct _FuzzySelect
  50. {
  51.   DrawCore *core;       /*  Core select object                      */
  52.  
  53.   gint      op;         /*  selection operation (ADD, SUB, etc)     */
  54.  
  55.   gint      current_x;  /*  these values are updated on every motion event  */
  56.   gint      current_y;  /*  (enables immediate cursor updating on modifier
  57.              *   key events).  */
  58.  
  59.   gint      x, y;             /*  Point from which to execute seed fill  */
  60.   gint      first_x;          /*                                         */
  61.   gint      first_y;          /*  variables to keep track of sensitivity */
  62.   gdouble   first_threshold;  /* initial value of threshold slider   */
  63. };
  64.  
  65.  
  66. /*  the fuzzy selection tool options  */
  67. static SelectionOptions  *fuzzy_options = NULL;
  68.  
  69. /*  XSegments which make up the fuzzy selection boundary  */
  70. static GdkSegment *segs     = NULL;
  71. static gint        num_segs = 0;
  72.  
  73. Channel * fuzzy_mask = NULL;
  74.  
  75.  
  76. /*  fuzzy select action functions  */
  77. static void   fuzzy_select_button_press    (Tool *, GdkEventButton *, gpointer);
  78. static void   fuzzy_select_button_release  (Tool *, GdkEventButton *, gpointer);
  79. static void   fuzzy_select_motion          (Tool *, GdkEventMotion *, gpointer);
  80. static void   fuzzy_select_control         (Tool *, ToolAction,       gpointer);
  81.  
  82. static void   fuzzy_select_draw            (Tool *);
  83.  
  84. /*  fuzzy select action functions  */
  85. static GdkSegment * fuzzy_select_calculate (Tool *, void *, int *);
  86.  
  87.  
  88. /*************************************/
  89. /*  Fuzzy selection apparatus  */
  90.  
  91. static gint
  92. is_pixel_sufficiently_different (guchar   *col1, 
  93.                  guchar   *col2,
  94.                  gboolean  antialias, 
  95.                  gint      threshold, 
  96.                  gint      bytes,
  97.                  gboolean  has_alpha)
  98. {
  99.   gint diff;
  100.   gint max;
  101.   gint b;
  102.   gint alpha;
  103.  
  104.   max = 0;
  105.   alpha = (has_alpha) ? bytes - 1 : bytes;
  106.  
  107.   /*  if there is an alpha channel, never select transparent regions  */
  108.   if (has_alpha && col2[alpha] == 0)
  109.     return 0;
  110.  
  111.   for (b = 0; b < bytes; b++)
  112.     {
  113.       diff = col1[b] - col2[b];
  114.       diff = abs (diff);
  115.       if (diff > max)
  116.     max = diff;
  117.     }
  118.  
  119.   if (antialias)
  120.     {
  121.       float aa;
  122.  
  123.       aa = 1.5 - ((float) max / threshold);
  124.       if (aa <= 0)
  125.     return 0;
  126.       else if (aa < 0.5)
  127.     return (unsigned char) (aa * 512);
  128.       else
  129.     return 255;
  130.     }
  131.   else
  132.     {
  133.       if (max > threshold)
  134.     return 0;
  135.       else
  136.     return 255;
  137.     }
  138. }
  139.  
  140. static void
  141. ref_tiles (TileManager  *src, 
  142.        TileManager  *mask, 
  143.        Tile        **s_tile, 
  144.        Tile        **m_tile,
  145.        gint          x, 
  146.        gint          y, 
  147.        guchar      **s, 
  148.        guchar      **m)
  149. {
  150.   if (*s_tile != NULL)
  151.     tile_release (*s_tile, FALSE);
  152.   if (*m_tile != NULL)
  153.     tile_release (*m_tile, TRUE);
  154.  
  155.   *s_tile = tile_manager_get_tile (src, x, y, TRUE, FALSE);
  156.   *m_tile = tile_manager_get_tile (mask, x, y, TRUE, TRUE);
  157.  
  158.   *s = tile_data_pointer (*s_tile, x % TILE_WIDTH, y % TILE_HEIGHT);
  159.   *m = tile_data_pointer (*m_tile, x % TILE_WIDTH, y % TILE_HEIGHT);
  160. }
  161.  
  162. static int
  163. find_contiguous_segment (guchar      *col, 
  164.              PixelRegion *src,
  165.              PixelRegion *mask, 
  166.              gint         width, 
  167.              gint         bytes,
  168.              gboolean     has_alpha, 
  169.              gboolean     antialias, 
  170.              gint         threshold,
  171.              gint         initial, 
  172.              gint        *start, 
  173.              gint        *end)
  174. {
  175.   guchar *s;
  176.   guchar *m;
  177.   guchar  diff;
  178.   Tile   *s_tile = NULL;
  179.   Tile   *m_tile = NULL;
  180.  
  181.   ref_tiles (src->tiles, mask->tiles, &s_tile, &m_tile, src->x, src->y, &s, &m);
  182.  
  183.   /* check the starting pixel */
  184.   if (! (diff = is_pixel_sufficiently_different (col, s, antialias,
  185.                          threshold, bytes, has_alpha)))
  186.     {
  187.       tile_release (s_tile, FALSE);
  188.       tile_release (m_tile, TRUE);
  189.       return FALSE;
  190.     }
  191.  
  192.   *m-- = diff;
  193.   s -= bytes;
  194.   *start = initial - 1;
  195.  
  196.   while (*start >= 0 && diff)
  197.     {
  198.       if (! ((*start + 1) % TILE_WIDTH))
  199.     ref_tiles (src->tiles, mask->tiles, &s_tile, &m_tile, *start, src->y, &s, &m);
  200.  
  201.       diff = is_pixel_sufficiently_different (col, s, antialias,
  202.                           threshold, bytes, has_alpha);
  203.       if ((*m-- = diff))
  204.     {
  205.       s -= bytes;
  206.       (*start)--;
  207.     }
  208.     }
  209.  
  210.   diff = 1;
  211.   *end = initial + 1;
  212.   if (*end % TILE_WIDTH && *end < width)
  213.     ref_tiles (src->tiles, mask->tiles, &s_tile, &m_tile, *end, src->y, &s, &m);
  214.  
  215.   while (*end < width && diff)
  216.     {
  217.       if (! (*end % TILE_WIDTH))
  218.     ref_tiles (src->tiles, mask->tiles, &s_tile, &m_tile, *end, src->y, &s, &m);
  219.  
  220.       diff = is_pixel_sufficiently_different (col, s, antialias,
  221.                           threshold, bytes, has_alpha);
  222.       if ((*m++ = diff))
  223.     {
  224.       s += bytes;
  225.       (*end)++;
  226.     }
  227.     }
  228.  
  229.   tile_release (s_tile, FALSE);
  230.   tile_release (m_tile, TRUE);
  231.   return TRUE;
  232. }
  233.  
  234. static void
  235. find_contiguous_region_helper (PixelRegion *mask, 
  236.                    PixelRegion *src,
  237.                    gboolean     has_alpha, 
  238.                    gboolean     antialias, 
  239.                    gint         threshold, 
  240.                    gboolean     indexed,
  241.                    gint         x, 
  242.                    gint         y, 
  243.                    guchar      *col)
  244. {
  245.   gint start, end, i;
  246.   gint val;
  247.   gint bytes;
  248.  
  249.   Tile *tile;
  250.  
  251.   if (threshold == 0) threshold = 1;
  252.   if (x < 0 || x >= src->w) return;
  253.   if (y < 0 || y >= src->h) return;
  254.  
  255.   tile = tile_manager_get_tile (mask->tiles, x, y, TRUE, FALSE);
  256.   val = *(guchar *)(tile_data_pointer (tile, 
  257.                        x % TILE_WIDTH, y % TILE_HEIGHT));
  258.   tile_release (tile, FALSE);
  259.   if (val != 0)
  260.     return;
  261.  
  262.   src->x = x;
  263.   src->y = y;
  264.  
  265.   bytes = src->bytes;
  266.   if(indexed)
  267.     {
  268.       bytes = has_alpha ? 4 : 3;
  269.     }
  270.  
  271.   if (! find_contiguous_segment (col, src, mask, src->w,
  272.                  src->bytes, has_alpha,
  273.                  antialias, threshold, x, &start, &end))
  274.     return;
  275.  
  276.   for (i = start + 1; i < end; i++)
  277.     {
  278.       find_contiguous_region_helper (mask, src, has_alpha, antialias, 
  279.                      threshold, indexed, i, y - 1, col);
  280.       find_contiguous_region_helper (mask, src, has_alpha, antialias, 
  281.                      threshold, indexed, i, y + 1, col);
  282.     }
  283. }
  284.  
  285. Channel *
  286. find_contiguous_region (GImage       *gimage, 
  287.             GimpDrawable *drawable, 
  288.             gboolean      antialias,
  289.             gint          threshold, 
  290.             gint          x, 
  291.             gint          y, 
  292.             gboolean      sample_merged)
  293. {
  294.   PixelRegion srcPR, maskPR;
  295.   Channel  *mask;
  296.   guchar   *start;
  297.   gboolean  has_alpha;
  298.   gboolean  indexed;
  299.   gint      type;
  300.   gint      bytes;
  301.   Tile     *tile;
  302.  
  303.   if (sample_merged)
  304.     {
  305.       pixel_region_init (&srcPR, gimage_composite (gimage), 0, 0,
  306.              gimage->width, gimage->height, FALSE);
  307.       type = gimage_composite_type (gimage);
  308.       has_alpha = (type == RGBA_GIMAGE ||
  309.            type == GRAYA_GIMAGE ||
  310.            type == INDEXEDA_GIMAGE);
  311.     }
  312.   else
  313.     {
  314.       pixel_region_init (&srcPR, drawable_data (drawable), 0, 0,
  315.              drawable_width (drawable), drawable_height (drawable),
  316.              FALSE);
  317.       has_alpha = drawable_has_alpha (drawable);
  318.     }
  319.   indexed = drawable_indexed (drawable);
  320.   bytes = drawable_bytes (drawable);
  321.   
  322.   if (indexed)
  323.     {
  324.       bytes = has_alpha ? 4 : 3;
  325.     }
  326.   mask = channel_new_mask (gimage, srcPR.w, srcPR.h);
  327.   pixel_region_init (&maskPR, drawable_data (GIMP_DRAWABLE(mask)), 0, 0, 
  328.              drawable_width (GIMP_DRAWABLE(mask)), 
  329.              drawable_height (GIMP_DRAWABLE(mask)), 
  330.              TRUE);
  331.  
  332.   tile = tile_manager_get_tile (srcPR.tiles, x, y, TRUE, FALSE);
  333.   if (tile)
  334.     {
  335.       start = tile_data_pointer (tile, x%TILE_WIDTH, y%TILE_HEIGHT);
  336.  
  337.       find_contiguous_region_helper (&maskPR, &srcPR, has_alpha, antialias, 
  338.                      threshold, bytes, x, y, start);
  339.  
  340.       tile_release (tile, FALSE);
  341.     }
  342.  
  343.   return mask;
  344. }
  345.  
  346. void
  347. fuzzy_select (GImage       *gimage, 
  348.           GimpDrawable *drawable, 
  349.           gint          op, 
  350.           gboolean      feather,
  351.           gdouble       feather_radius)
  352. {
  353.   gint off_x, off_y;
  354.  
  355.   /*  if applicable, replace the current selection  */
  356.   if (op == REPLACE)
  357.     gimage_mask_clear (gimage);
  358.   else
  359.     gimage_mask_undo (gimage);
  360.  
  361.   if (drawable)     /* NULL if sample_merged is active */
  362.     drawable_offsets (drawable, &off_x, &off_y);
  363.   else
  364.     off_x = off_y = 0;
  365.   
  366.   if (feather)
  367.     channel_feather (fuzzy_mask, gimage_get_mask (gimage),
  368.              feather_radius,
  369.              feather_radius,
  370.              op, off_x, off_y);
  371.   else
  372.     channel_combine_mask (gimage_get_mask (gimage),
  373.               fuzzy_mask, op, off_x, off_y);
  374.  
  375.   /*  free the fuzzy region struct  */
  376.   channel_delete (fuzzy_mask);
  377.   fuzzy_mask = NULL;
  378. }
  379.  
  380. /*  fuzzy select action functions  */
  381.  
  382. static void
  383. fuzzy_select_button_press (Tool           *tool, 
  384.                GdkEventButton *bevent,
  385.                gpointer        gdisp_ptr)
  386. {
  387.   GDisplay    *gdisp;
  388.   FuzzySelect *fuzzy_sel;
  389.  
  390.   gdisp = (GDisplay *) gdisp_ptr;
  391.   fuzzy_sel = (FuzzySelect *) tool->private;
  392.  
  393.   fuzzy_sel->x = bevent->x;
  394.   fuzzy_sel->y = bevent->y;
  395.   fuzzy_sel->first_x = fuzzy_sel->x;
  396.   fuzzy_sel->first_y = fuzzy_sel->y;
  397.   fuzzy_sel->first_threshold = fuzzy_options->threshold;
  398.  
  399.   gdk_pointer_grab (gdisp->canvas->window, FALSE,
  400.             GDK_POINTER_MOTION_HINT_MASK |
  401.             GDK_BUTTON1_MOTION_MASK |
  402.             GDK_BUTTON_RELEASE_MASK,
  403.             NULL, NULL, bevent->time);
  404.  
  405.   tool->state = ACTIVE;
  406.   tool->gdisp_ptr = gdisp;
  407.  
  408.   if (fuzzy_sel->op == SELECTION_MOVE_MASK)
  409.     {
  410.       init_edit_selection (tool, gdisp_ptr, bevent, EDIT_MASK_TRANSLATE);
  411.       return;
  412.     }
  413.   else if (fuzzy_sel->op == SELECTION_MOVE)
  414.     {
  415.       init_edit_selection (tool, gdisp_ptr, bevent, EDIT_MASK_TO_LAYER_TRANSLATE);
  416.       return;
  417.     }
  418.  
  419.   /*  calculate the region boundary  */
  420.   segs = fuzzy_select_calculate (tool, gdisp_ptr, &num_segs);
  421.  
  422.   draw_core_start (fuzzy_sel->core,
  423.            gdisp->canvas->window,
  424.            tool);
  425. }
  426.  
  427. static void
  428. fuzzy_select_button_release (Tool           *tool, 
  429.                  GdkEventButton *bevent,
  430.                  gpointer        gdisp_ptr)
  431. {
  432.   FuzzySelect  *fuzzy_sel;
  433.   GDisplay     *gdisp;
  434.   GimpDrawable *drawable;
  435.  
  436.   gdisp = (GDisplay *) gdisp_ptr;
  437.   fuzzy_sel = (FuzzySelect *) tool->private;
  438.  
  439.   gdk_pointer_ungrab (bevent->time);
  440.   gdk_flush ();
  441.  
  442.   draw_core_stop (fuzzy_sel->core, tool);
  443.   tool->state = INACTIVE;
  444.  
  445.   /*  First take care of the case where the user "cancels" the action  */
  446.   if (! (bevent->state & GDK_BUTTON3_MASK))
  447.     {
  448.       drawable = (fuzzy_options->sample_merged ?
  449.           NULL : gimage_active_drawable (gdisp->gimage));
  450.  
  451.       fuzzy_select (gdisp->gimage, drawable, fuzzy_sel->op,
  452.             fuzzy_options->feather, 
  453.             fuzzy_options->feather_radius);
  454.       gdisplays_flush ();
  455.     }
  456.  
  457.   /*  If the segment array is allocated, free it  */
  458.   if (segs)
  459.     g_free (segs);
  460.   segs = NULL;
  461. }
  462.  
  463. static void
  464. fuzzy_select_motion (Tool           *tool, 
  465.              GdkEventMotion *mevent, 
  466.              gpointer        gdisp_ptr)
  467. {
  468.   FuzzySelect *fuzzy_sel;
  469.   GdkSegment  *new_segs;
  470.   gint    num_new_segs;
  471.   gint    diff_x, diff_y;
  472.   gdouble diff;
  473.  
  474.   static guint last_time = 0;
  475.  
  476.   fuzzy_sel = (FuzzySelect *) tool->private;
  477.  
  478.   /*  needed for immediate cursor update on modifier event  */
  479.   fuzzy_sel->current_x = mevent->x;
  480.   fuzzy_sel->current_y = mevent->y;
  481.  
  482.   if (tool->state != ACTIVE)
  483.     return;
  484.  
  485.   /* don't let the events come in too fast, ignore below a delay of 100 ms */
  486.   if (ABS (mevent->time - last_time) < 100)
  487.     return;
  488.   
  489.   last_time = mevent->time;
  490.  
  491.   diff_x = mevent->x - fuzzy_sel->first_x;
  492.   diff_y = mevent->y - fuzzy_sel->first_y;
  493.  
  494.   diff = ((ABS (diff_x) > ABS (diff_y)) ? diff_x : diff_y) / 2.0;
  495.  
  496.   gtk_adjustment_set_value (GTK_ADJUSTMENT (fuzzy_options->threshold_w), 
  497.                 fuzzy_sel->first_threshold + diff);
  498.       
  499.   /*  calculate the new fuzzy boundary  */
  500.   new_segs = fuzzy_select_calculate (tool, gdisp_ptr, &num_new_segs);
  501.  
  502.   /*  stop the current boundary  */
  503.   draw_core_pause (fuzzy_sel->core, tool);
  504.  
  505.   /*  make sure the XSegment array is freed before we assign the new one  */
  506.   if (segs)
  507.     g_free (segs);
  508.   segs = new_segs;
  509.   num_segs = num_new_segs;
  510.  
  511.   /*  start the new boundary  */
  512.   draw_core_resume (fuzzy_sel->core, tool);
  513. }
  514.  
  515.  
  516. static GdkSegment *
  517. fuzzy_select_calculate (Tool *tool, 
  518.             void *gdisp_ptr, 
  519.             gint *nsegs)
  520. {
  521.   PixelRegion   maskPR;
  522.   FuzzySelect  *fuzzy_sel;
  523.   GDisplay     *gdisp;
  524.   Channel      *new;
  525.   GdkSegment   *segs;
  526.   BoundSeg     *bsegs;
  527.   GimpDrawable *drawable;
  528.   gint     i;
  529.   gint     x, y;
  530.   gboolean use_offsets;
  531.  
  532.   fuzzy_sel = (FuzzySelect *) tool->private;
  533.   gdisp = (GDisplay *) gdisp_ptr;
  534.   drawable = gimage_active_drawable (gdisp->gimage);
  535.  
  536.   gimp_add_busy_cursors ();
  537.  
  538.   use_offsets = fuzzy_options->sample_merged ? FALSE : TRUE;
  539.  
  540.   gdisplay_untransform_coords (gdisp, fuzzy_sel->x,
  541.                    fuzzy_sel->y, &x, &y, FALSE, use_offsets);
  542.  
  543.   new = find_contiguous_region (gdisp->gimage, drawable, 
  544.                 fuzzy_options->antialias,
  545.                 fuzzy_options->threshold, x, y, 
  546.                 fuzzy_options->sample_merged);
  547.  
  548.   if (fuzzy_mask)
  549.     channel_delete (fuzzy_mask);
  550.   fuzzy_mask = channel_ref (new);
  551.  
  552.   /*  calculate and allocate a new XSegment array which represents the boundary
  553.    *  of the color-contiguous region
  554.    */
  555.   pixel_region_init (&maskPR, drawable_data (GIMP_DRAWABLE (fuzzy_mask)), 0, 0, 
  556.              drawable_width (GIMP_DRAWABLE (fuzzy_mask)), 
  557.              drawable_height (GIMP_DRAWABLE (fuzzy_mask)), 
  558.              FALSE);
  559.   bsegs = find_mask_boundary (&maskPR, nsegs, WithinBounds,
  560.                   0, 0,
  561.                   drawable_width (GIMP_DRAWABLE (fuzzy_mask)),
  562.                   drawable_height (GIMP_DRAWABLE (fuzzy_mask)));
  563.  
  564.   segs = g_new (GdkSegment, *nsegs);
  565.  
  566.   for (i = 0; i < *nsegs; i++)
  567.     {
  568.       gdisplay_transform_coords (gdisp, bsegs[i].x1, bsegs[i].y1, &x, &y, use_offsets);
  569.       segs[i].x1 = x;  segs[i].y1 = y;
  570.       gdisplay_transform_coords (gdisp, bsegs[i].x2, bsegs[i].y2, &x, &y, use_offsets);
  571.       segs[i].x2 = x;  segs[i].y2 = y;
  572.     }
  573.  
  574.   /*  free boundary segments  */
  575.   g_free (bsegs);
  576.  
  577.   gimp_remove_busy_cursors (NULL);
  578.  
  579.   return segs;
  580. }
  581.  
  582. static void
  583. fuzzy_select_draw (Tool *tool)
  584. {
  585.   FuzzySelect *fuzzy_sel;
  586.  
  587.   fuzzy_sel = (FuzzySelect *) tool->private;
  588.  
  589.   if (segs)
  590.     gdk_draw_segments (fuzzy_sel->core->win, fuzzy_sel->core->gc, segs, num_segs);
  591. }
  592.  
  593. static void
  594. fuzzy_select_control (Tool       *tool,
  595.               ToolAction  action,
  596.               gpointer    gdisp_ptr)
  597. {
  598.   FuzzySelect *fuzzy_sel;
  599.  
  600.   fuzzy_sel = (FuzzySelect *) tool->private;
  601.  
  602.   switch (action)
  603.     {
  604.     case PAUSE :
  605.       draw_core_pause (fuzzy_sel->core, tool);
  606.       break;
  607.  
  608.     case RESUME :
  609.       draw_core_resume (fuzzy_sel->core, tool);
  610.       break;
  611.  
  612.     case HALT :
  613.       draw_core_stop (fuzzy_sel->core, tool);
  614.       break;
  615.  
  616.     default:
  617.       break;
  618.     }
  619. }
  620.  
  621. static void
  622. fuzzy_select_options_reset (void)
  623. {
  624.   selection_options_reset (fuzzy_options);
  625. }
  626.  
  627. Tool *
  628. tools_new_fuzzy_select (void)
  629. {
  630.   Tool        *tool;
  631.   FuzzySelect *private;
  632.  
  633.   /*  The tool options  */
  634.   if (! fuzzy_options)
  635.     {
  636.       fuzzy_options = selection_options_new (FUZZY_SELECT,
  637.                          fuzzy_select_options_reset);
  638.       tools_register (FUZZY_SELECT, (ToolOptions *) fuzzy_options);
  639.     }
  640.  
  641.   tool = tools_new_tool (FUZZY_SELECT);
  642.   private = g_new0 (FuzzySelect, 1);
  643.  
  644.   private->core = draw_core_new (fuzzy_select_draw);
  645.  
  646.   tool->scroll_lock = TRUE;  /*  Disallow scrolling  */
  647.  
  648.   tool->private = (void *) private;
  649.  
  650.   tool->button_press_func   = fuzzy_select_button_press;
  651.   tool->button_release_func = fuzzy_select_button_release;
  652.   tool->motion_func         = fuzzy_select_motion;
  653.   tool->modifier_key_func   = rect_select_modifier_update;
  654.   tool->cursor_update_func  = rect_select_cursor_update;
  655.   tool->oper_update_func    = rect_select_oper_update;
  656.   tool->control_func        = fuzzy_select_control;
  657.  
  658.   return tool;
  659. }
  660.  
  661. void
  662. tools_free_fuzzy_select (Tool *tool)
  663. {
  664.   FuzzySelect *fuzzy_sel;
  665.  
  666.   fuzzy_sel = (FuzzySelect *) tool->private;
  667.   draw_core_free (fuzzy_sel->core);
  668.   g_free (fuzzy_sel);
  669. }
  670.