/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include "libgimpmath/gimpmath.h" #include "apptypes.h" #include "draw_core.h" #include "errors.h" #include "floating_sel.h" #include "gdisplay.h" #include "gimage_mask.h" #include "gimpchannel.h" #include "gimpimage.h" #include "scan_convert.h" #include "edit_selection.h" #include "free_select.h" #include "rect_select.h" #include "selection_options.h" #include "tool_options.h" #include "tools.h" #define DEFAULT_MAX_INC 1024 #define SUPERSAMPLE 3 #define SUPERSAMPLE2 9 /* the free selection structures */ typedef struct _FreeSelect FreeSelect; struct _FreeSelect { DrawCore *core; /* Core select object */ SelectOps op; /* selection operation (ADD, SUB, etc) */ gint current_x; /* these values are updated on every motion event */ gint current_y; /* (enables immediate cursor updating on modifier * key events). */ gint num_pts; /* Number of points in the polygon */ }; /* local function prototypes */ static void free_select_button_press (Tool *tool, GdkEventButton *bevent, GDisplay *gdisp); static void free_select_button_release (Tool *tool, GdkEventButton *bevent, GDisplay *gdisp); static void free_select_motion (Tool *tool, GdkEventMotion *mevent, GDisplay *gdisp); /* the free selection tool options */ static SelectionOptions * free_options = NULL; /* The global array of XPoints for drawing the polygon... */ static GdkPoint *global_pts = NULL; static gint max_segs = 0; /* functions */ static gint add_point (gint num_pts, gint x, gint y) { if (num_pts >= max_segs) { max_segs += DEFAULT_MAX_INC; global_pts = (GdkPoint *) g_realloc ((void *) global_pts, sizeof (GdkPoint) * max_segs); if (!global_pts) gimp_fatal_error ("add_point(): Unable to reallocate points array in free_select."); } global_pts[num_pts].x = x; global_pts[num_pts].y = y; return 1; } static GimpChannel * scan_convert (GimpImage *gimage, gint num_pts, ScanConvertPoint *pts, gint width, gint height, gboolean antialias) { GimpChannel *mask; ScanConverter *sc; sc = scan_converter_new (width, height, antialias ? SUPERSAMPLE : 1); scan_converter_add_points (sc, num_pts, pts); mask = scan_converter_to_channel (sc, gimage); scan_converter_free (sc); return mask; } /*************************************/ /* Polygonal selection apparatus */ void free_select (GImage *gimage, gint num_pts, ScanConvertPoint *pts, SelectOps op, gboolean antialias, gboolean feather, gdouble feather_radius) { GimpChannel *mask; /* if applicable, replace the current selection */ /* or insure that a floating selection is anchored down... */ if (op == SELECTION_REPLACE) gimage_mask_clear (gimage); else gimage_mask_undo (gimage); mask = scan_convert (gimage, num_pts, pts, gimage->width, gimage->height, antialias); if (mask) { if (feather) gimp_channel_feather (mask, gimp_image_get_mask (gimage), feather_radius, feather_radius, op, 0, 0); else gimp_channel_combine_mask (gimp_image_get_mask (gimage), mask, op, 0, 0); gtk_object_unref (GTK_OBJECT (mask)); } } static void free_select_button_press (Tool *tool, GdkEventButton *bevent, GDisplay *gdisp) { FreeSelect *free_sel; free_sel = (FreeSelect *) tool->private; gdk_pointer_grab (gdisp->canvas->window, FALSE, GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, NULL, bevent->time); tool->state = ACTIVE; tool->gdisp = gdisp; switch (free_sel->op) { case SELECTION_MOVE_MASK: init_edit_selection (tool, gdisp, bevent, EDIT_MASK_TRANSLATE); return; case SELECTION_MOVE: init_edit_selection (tool, gdisp, bevent, EDIT_MASK_TO_LAYER_TRANSLATE); return; default: break; } add_point (0, bevent->x, bevent->y); free_sel->num_pts = 1; draw_core_start (free_sel->core, gdisp->canvas->window, tool); } static void free_select_button_release (Tool *tool, GdkEventButton *bevent, GDisplay *gdisp) { FreeSelect *free_sel; ScanConvertPoint *pts; gint i; free_sel = (FreeSelect *) tool->private; gdk_pointer_ungrab (bevent->time); gdk_flush (); draw_core_stop (free_sel->core, tool); tool->state = INACTIVE; /* First take care of the case where the user "cancels" the action */ if (! (bevent->state & GDK_BUTTON3_MASK)) { if (free_sel->op == SELECTION_ANCHOR) { /* If there is a floating selection, anchor it */ if (gimp_image_floating_sel (gdisp->gimage)) floating_sel_anchor (gimp_image_floating_sel (gdisp->gimage)); /* Otherwise, clear the selection mask */ else gimage_mask_clear (gdisp->gimage); gdisplays_flush (); return; } pts = g_new (ScanConvertPoint, free_sel->num_pts); for (i = 0; i < free_sel->num_pts; i++) { gdisplay_untransform_coords_f (gdisp, global_pts[i].x, global_pts[i].y, &pts[i].x, &pts[i].y, FALSE); } free_select (gdisp->gimage, free_sel->num_pts, pts, free_sel->op, free_options->antialias, free_options->feather, free_options->feather_radius); g_free (pts); gdisplays_flush (); } } static void free_select_motion (Tool *tool, GdkEventMotion *mevent, GDisplay *gdisp) { FreeSelect *free_sel; free_sel = (FreeSelect *) tool->private; /* needed for immediate cursor update on modifier event */ free_sel->current_x = mevent->x; free_sel->current_y = mevent->y; if (tool->state != ACTIVE) return; if (free_sel->op == SELECTION_ANCHOR) { free_sel->op = SELECTION_REPLACE; rect_select_cursor_update (tool, mevent, gdisp); } if (add_point (free_sel->num_pts, mevent->x, mevent->y)) { gdk_draw_line (free_sel->core->win, free_sel->core->gc, global_pts[free_sel->num_pts - 1].x, global_pts[free_sel->num_pts - 1].y, global_pts[free_sel->num_pts].x, global_pts[free_sel->num_pts].y); free_sel->num_pts ++; } } static void free_select_control (Tool *tool, ToolAction action, GDisplay *gdisp) { FreeSelect *free_sel; free_sel = (FreeSelect *) tool->private; switch (action) { case PAUSE: draw_core_pause (free_sel->core, tool); break; case RESUME: draw_core_resume (free_sel->core, tool); break; case HALT: draw_core_stop (free_sel->core, tool); break; default: break; } } void free_select_draw (Tool *tool) { FreeSelect *free_sel; gint i; free_sel = (FreeSelect *) tool->private; for (i = 1; i < free_sel->num_pts; i++) gdk_draw_line (free_sel->core->win, free_sel->core->gc, global_pts[i - 1].x, global_pts[i - 1].y, global_pts[i].x, global_pts[i].y); } static void free_select_options_reset (void) { selection_options_reset (free_options); } Tool * tools_new_free_select (void) { Tool *tool; FreeSelect *private; /* The tool options */ if (!free_options) { free_options = selection_options_new (FREE_SELECT, free_select_options_reset); tools_register (FREE_SELECT, (ToolOptions *) free_options); } tool = tools_new_tool (FREE_SELECT); private = g_new0 (FreeSelect, 1); private->core = draw_core_new (free_select_draw); private->num_pts = 0; private->op = SELECTION_REPLACE; tool->scroll_lock = TRUE; /* Do not allow scrolling */ tool->private = (void *) private; tool->tool_cursor = GIMP_FREE_SELECT_TOOL_CURSOR; tool->button_press_func = free_select_button_press; tool->button_release_func = free_select_button_release; tool->motion_func = free_select_motion; tool->modifier_key_func = rect_select_modifier_update; tool->cursor_update_func = rect_select_cursor_update; tool->oper_update_func = rect_select_oper_update; tool->control_func = free_select_control; return tool; } void tools_free_free_select (Tool *tool) { FreeSelect *free_sel; free_sel = (FreeSelect *) tool->private; draw_core_free (free_sel->core); g_free (free_sel); }