2006-12-10 05:33:38 +08:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
1997-11-25 06:05:25 +08:00
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
2009-01-18 06:28:01 +08:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
1997-11-25 06:05:25 +08:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
2009-01-18 06:28:01 +08:00
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
1997-11-25 06:05:25 +08:00
|
|
|
* (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
|
2018-07-12 05:27:07 +08:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
1997-11-25 06:05:25 +08:00
|
|
|
*/
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* This tool is based on a paper from SIGGRAPH '95:
|
|
|
|
* "Intelligent Scissors for Image Composition", Eric N. Mortensen and
|
|
|
|
* William A. Barrett, Brigham Young University.
|
|
|
|
*
|
|
|
|
* thanks to Professor D. Forsyth for prompting us to implement this tool. */
|
|
|
|
|
2002-04-21 15:31:12 +08:00
|
|
|
/* Personal note: Dr. Barrett, one of the authors of the paper written above
|
|
|
|
* is not only one of the most brilliant people I have ever met, he is an
|
|
|
|
* incredible professor who genuinely cares about his students and wants them
|
|
|
|
* to learn as much as they can about the topic.
|
|
|
|
*
|
|
|
|
* I didn't even notice I was taking a class from the person who wrote the
|
|
|
|
* paper until halfway through the semester.
|
|
|
|
* -- Rockwalrus
|
|
|
|
*/
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* The history of this implementation is lonog and varied. It was
|
2013-01-27 23:52:38 +08:00
|
|
|
* originally done by Spencer and Peter, and worked fine in the 0.54
|
2006-12-22 18:09:09 +08:00
|
|
|
* (motif only) release of GIMP. Later revisions (0.99.something
|
1999-08-16 11:43:48 +08:00
|
|
|
* until about 1.1.4) completely changed the algorithm used, until it
|
|
|
|
* bore little resemblance to the one described in the paper above.
|
|
|
|
* The 0.54 version of the algorithm was then forwards ported to 1.1.4
|
2000-12-29 23:22:01 +08:00
|
|
|
* by Austin Donnelly.
|
|
|
|
*/
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-01-02 22:50:11 +08:00
|
|
|
/* Livewire boundary implementation done by Laramie Leavitt */
|
|
|
|
|
app/appenv.h New file. Includes <math.h>. Move G_PI, RINT(), ROUND() etc
1999-09-01 Tor Lillqvist <tml@iki.fi>
* app/appenv.h
* libgimp/gimpmath.h: New file. Includes <math.h>. Move G_PI,
RINT(), ROUND() etc from app/appenv.h here, so plug-ins can
use them, too. Remove some commented-out old stuff in appenv.h.
* libgimp/gimp.h: Include gimpmath.h.
* libgimp/gimp.c (gimp_main): Win32: Don't install signal
handlers, we can't do anything useful in the handler ourselves
anyway (it would be nice to print out a backtrace, but that seems
pretty hard to do, even if not impossible). Let Windows inform the
user about the crash. If the plug-in was compiled with MSVC, and
the user also has it, she is offered a chance to start the
debugger automatically anyway.
* app/*several*.c: Include gimpmath.h for G_PI etc. Don't include
<math.h>, as gimpmath.h includes it.
* plug-ins/*/*many*.c: Include config.h. Don't include <math.h>.
Remove all the duplicated definitions of G_PI and rint(). Use
RINT() instead of rint().
* app/app_procs.[ch]: app_exit() takes a gboolean.
* app/batch.c
* app/commands.c
* app/interface.c: Call app_exit() with FALSE or TRUE.
* app/main.c (on_error): Call gimp_fatal_error. (main): Don't
install any signal handler on Win32 here, either.
* app/errors.c (gimp_fatal_error, gimp_terminate): Win32: Format
the message and call MessageBox with it. g_on_error_query doesn't
do anything useful on Win32, and printf'ing a message to stdout or
stderr doesn't do anything, either, in a windowing application.
1999-09-02 04:30:56 +08:00
|
|
|
#include "config.h"
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
#include <stdlib.h>
|
1999-08-05 07:22:29 +08:00
|
|
|
|
2008-10-10 04:24:04 +08:00
|
|
|
#include <gegl.h>
|
2000-12-17 05:37:03 +08:00
|
|
|
#include <gtk/gtk.h>
|
2000-03-03 04:09:12 +08:00
|
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
|
2001-01-24 07:56:18 +08:00
|
|
|
#include "libgimpmath/gimpmath.h"
|
2001-08-06 00:08:19 +08:00
|
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
2001-01-24 07:56:18 +08:00
|
|
|
|
2001-05-10 06:34:59 +08:00
|
|
|
#include "tools-types.h"
|
2000-12-17 05:37:03 +08:00
|
|
|
|
2012-05-06 07:01:54 +08:00
|
|
|
#include "gegl/gimp-gegl-utils.h"
|
|
|
|
|
2001-05-09 10:32:03 +08:00
|
|
|
#include "core/gimpchannel.h"
|
2003-10-06 20:17:11 +08:00
|
|
|
#include "core/gimpchannel-select.h"
|
2001-05-09 10:32:03 +08:00
|
|
|
#include "core/gimpimage.h"
|
2005-07-12 03:21:52 +08:00
|
|
|
#include "core/gimppickable.h"
|
2001-05-26 02:10:38 +08:00
|
|
|
#include "core/gimpscanconvert.h"
|
2012-04-09 06:59:20 +08:00
|
|
|
#include "core/gimptempbuf.h"
|
2001-11-20 21:53:21 +08:00
|
|
|
#include "core/gimptoolinfo.h"
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2003-08-22 09:42:57 +08:00
|
|
|
#include "widgets/gimphelp-ids.h"
|
2007-01-19 17:32:35 +08:00
|
|
|
#include "widgets/gimpwidgets-utils.h"
|
2003-08-22 09:42:57 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
#include "display/gimpcanvasitem.h"
|
2001-09-26 07:23:09 +08:00
|
|
|
#include "display/gimpdisplay.h"
|
|
|
|
|
2008-03-09 23:05:54 +08:00
|
|
|
#include "gimpiscissorsoptions.h"
|
2001-03-12 06:19:06 +08:00
|
|
|
#include "gimpiscissorstool.h"
|
2015-03-18 05:19:29 +08:00
|
|
|
#include "gimptilehandleriscissors.h"
|
2003-04-16 00:05:52 +08:00
|
|
|
#include "gimptoolcontrol.h"
|
2001-01-25 06:36:18 +08:00
|
|
|
|
2003-03-26 00:38:19 +08:00
|
|
|
#include "gimp-intl.h"
|
2000-12-29 23:22:01 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2006-11-07 19:09:51 +08:00
|
|
|
/* defines */
|
1999-08-16 11:43:48 +08:00
|
|
|
#define GRADIENT_SEARCH 32 /* how far to look when snapping to an edge */
|
|
|
|
#define EXTEND_BY 0.2 /* proportion to expand cost map by */
|
|
|
|
#define FIXED 5 /* additional fixed size to expand cost map */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2006-11-07 19:09:51 +08:00
|
|
|
#define COST_WIDTH 2 /* number of bytes for each pixel in cost map */
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2001-01-03 01:01:30 +08:00
|
|
|
/* weight to give between gradient (_G) and direction (_D) */
|
|
|
|
#define OMEGA_D 0.2
|
|
|
|
#define OMEGA_G 0.8
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* sentinel to mark seed point in ?cost? map */
|
|
|
|
#define SEED_POINT 9
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Functional defines */
|
2004-06-13 20:48:40 +08:00
|
|
|
#define PIXEL_COST(x) ((x) >> 8)
|
|
|
|
#define PIXEL_DIR(x) ((x) & 0x000000ff)
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
struct _ISegment
|
2001-11-20 21:53:21 +08:00
|
|
|
{
|
|
|
|
gint x1, y1;
|
|
|
|
gint x2, y2;
|
|
|
|
GPtrArray *points;
|
|
|
|
};
|
|
|
|
|
2015-03-28 04:37:57 +08:00
|
|
|
struct _ICurve
|
|
|
|
{
|
|
|
|
GQueue *segments;
|
|
|
|
gboolean first_point;
|
2016-10-09 23:19:42 +08:00
|
|
|
gboolean closed;
|
2015-03-28 04:37:57 +08:00
|
|
|
};
|
|
|
|
|
2001-11-20 21:53:21 +08:00
|
|
|
|
|
|
|
/* local function prototypes */
|
2001-03-12 06:19:06 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
static void gimp_iscissors_tool_finalize (GObject *object);
|
|
|
|
|
|
|
|
static void gimp_iscissors_tool_control (GimpTool *tool,
|
|
|
|
GimpToolAction action,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_iscissors_tool_button_press (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpButtonPressType press_type,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_iscissors_tool_button_release (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpButtonReleaseType release_type,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_iscissors_tool_motion (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_iscissors_tool_oper_update (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GdkModifierType state,
|
|
|
|
gboolean proximity,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_iscissors_tool_cursor_update (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static gboolean gimp_iscissors_tool_key_press (GimpTool *tool,
|
|
|
|
GdkEventKey *kevent,
|
|
|
|
GimpDisplay *display);
|
2017-07-05 00:48:03 +08:00
|
|
|
static const gchar * gimp_iscissors_tool_can_undo (GimpTool *tool,
|
2015-03-29 04:31:03 +08:00
|
|
|
GimpDisplay *display);
|
2017-07-05 00:48:03 +08:00
|
|
|
static const gchar * gimp_iscissors_tool_can_redo (GimpTool *tool,
|
2015-03-29 04:31:03 +08:00
|
|
|
GimpDisplay *display);
|
|
|
|
static gboolean gimp_iscissors_tool_undo (GimpTool *tool,
|
|
|
|
GimpDisplay *display);
|
|
|
|
static gboolean gimp_iscissors_tool_redo (GimpTool *tool,
|
|
|
|
GimpDisplay *display);
|
|
|
|
|
|
|
|
static void gimp_iscissors_tool_draw (GimpDrawTool *draw_tool);
|
|
|
|
|
|
|
|
static void gimp_iscissors_tool_push_undo (GimpIscissorsTool *iscissors);
|
|
|
|
static void gimp_iscissors_tool_pop_undo (GimpIscissorsTool *iscissors);
|
|
|
|
static void gimp_iscissors_tool_free_redo (GimpIscissorsTool *iscissors);
|
|
|
|
|
2016-10-07 07:18:16 +08:00
|
|
|
static void gimp_iscissors_tool_halt (GimpIscissorsTool *iscissors,
|
2015-03-29 04:31:03 +08:00
|
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_iscissors_tool_commit (GimpIscissorsTool *iscissors,
|
|
|
|
GimpDisplay *display);
|
2001-03-12 06:19:06 +08:00
|
|
|
|
|
|
|
static void iscissors_convert (GimpIscissorsTool *iscissors,
|
2006-03-29 01:55:52 +08:00
|
|
|
GimpDisplay *display);
|
2016-10-07 07:18:16 +08:00
|
|
|
static GeglBuffer * gradient_map_new (GimpPickable *pickable);
|
2001-03-12 06:19:06 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
static void find_optimal_path (GeglBuffer *gradient_map,
|
2012-04-09 02:25:49 +08:00
|
|
|
GimpTempBuf *dp_buf,
|
2006-04-12 20:49:29 +08:00
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2,
|
|
|
|
gint xs,
|
|
|
|
gint ys);
|
2001-03-12 06:19:06 +08:00
|
|
|
static void find_max_gradient (GimpIscissorsTool *iscissors,
|
2016-10-07 07:18:16 +08:00
|
|
|
GimpPickable *pickable,
|
2006-04-12 20:49:29 +08:00
|
|
|
gint *x,
|
|
|
|
gint *y);
|
2015-03-27 17:45:28 +08:00
|
|
|
static void calculate_segment (GimpIscissorsTool *iscissors,
|
|
|
|
ISegment *segment);
|
2015-03-29 04:31:03 +08:00
|
|
|
static GimpCanvasItem * iscissors_draw_segment (GimpDrawTool *draw_tool,
|
2015-03-27 17:45:28 +08:00
|
|
|
ISegment *segment);
|
2001-03-12 06:19:06 +08:00
|
|
|
|
|
|
|
static gint mouse_over_vertex (GimpIscissorsTool *iscissors,
|
2006-04-12 20:49:29 +08:00
|
|
|
gdouble x,
|
|
|
|
gdouble y);
|
2006-08-15 04:32:42 +08:00
|
|
|
static gboolean clicked_on_vertex (GimpIscissorsTool *iscissors,
|
|
|
|
gdouble x,
|
|
|
|
gdouble y);
|
2015-03-27 17:45:28 +08:00
|
|
|
static GList * mouse_over_segment (GimpIscissorsTool *iscissors,
|
2006-04-12 20:49:29 +08:00
|
|
|
gdouble x,
|
|
|
|
gdouble y);
|
2015-03-27 17:45:28 +08:00
|
|
|
static gboolean clicked_on_segment (GimpIscissorsTool *iscissors,
|
2006-08-15 04:32:42 +08:00
|
|
|
gdouble x,
|
|
|
|
gdouble y);
|
2001-03-12 06:19:06 +08:00
|
|
|
|
2016-10-07 07:58:37 +08:00
|
|
|
static GPtrArray * plot_pixels (GimpTempBuf *dp_buf,
|
2006-04-12 20:49:29 +08:00
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint xs,
|
|
|
|
gint ys,
|
|
|
|
gint xe,
|
|
|
|
gint ye);
|
2001-03-12 06:19:06 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
static ISegment * isegment_new (gint x1,
|
2015-03-21 04:18:23 +08:00
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2);
|
2015-03-29 04:31:03 +08:00
|
|
|
static ISegment * isegment_copy (ISegment *segment);
|
2015-03-27 17:45:28 +08:00
|
|
|
static void isegment_free (ISegment *segment);
|
2015-03-21 04:18:23 +08:00
|
|
|
|
2015-03-28 04:37:57 +08:00
|
|
|
static ICurve * icurve_new (void);
|
2015-03-29 04:31:03 +08:00
|
|
|
static ICurve * icurve_copy (ICurve *curve);
|
2015-03-28 04:37:57 +08:00
|
|
|
static void icurve_clear (ICurve *curve);
|
|
|
|
static void icurve_free (ICurve *curve);
|
|
|
|
|
2016-10-09 23:19:42 +08:00
|
|
|
static ISegment * icurve_append_segment (ICurve *curve,
|
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2);
|
|
|
|
static ISegment * icurve_insert_segment (ICurve *curve,
|
|
|
|
GList *sibling,
|
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2);
|
|
|
|
static void icurve_delete_segment (ICurve *curve,
|
|
|
|
ISegment *segment);
|
|
|
|
|
|
|
|
static void icurve_close (ICurve *curve);
|
|
|
|
|
|
|
|
static GimpScanConvert *
|
|
|
|
icurve_create_scan_convert (ICurve *curve);
|
|
|
|
|
2001-03-12 06:19:06 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/* static variables */
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* where to move on a given link direction */
|
2006-11-07 19:09:51 +08:00
|
|
|
static const gint move[8][2] =
|
1999-08-16 11:43:48 +08:00
|
|
|
{
|
2001-11-09 03:14:51 +08:00
|
|
|
{ 1, 0 },
|
|
|
|
{ 0, 1 },
|
|
|
|
{ -1, 1 },
|
|
|
|
{ 1, 1 },
|
|
|
|
{ -1, 0 },
|
|
|
|
{ 0, -1 },
|
|
|
|
{ 1, -1 },
|
1999-08-16 11:43:48 +08:00
|
|
|
{ -1, -1 },
|
|
|
|
};
|
|
|
|
|
|
|
|
/* IE:
|
|
|
|
* '---+---+---`
|
|
|
|
* | 7 | 5 | 6 |
|
|
|
|
* +---+---+---+
|
|
|
|
* | 4 | | 0 |
|
|
|
|
* +---+---+---+
|
|
|
|
* | 2 | 1 | 3 |
|
|
|
|
* `---+---+---'
|
|
|
|
*/
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2006-08-15 04:32:42 +08:00
|
|
|
static gfloat distance_weights[GRADIENT_SEARCH * GRADIENT_SEARCH];
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2006-08-15 04:32:42 +08:00
|
|
|
static gint diagonal_weight[256];
|
|
|
|
static gint direction_value[256][4];
|
2015-03-18 05:19:29 +08:00
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (GimpIscissorsTool, gimp_iscissors_tool,
|
|
|
|
GIMP_TYPE_SELECTION_TOOL)
|
|
|
|
|
|
|
|
#define parent_class gimp_iscissors_tool_parent_class
|
1999-10-06 02:05:34 +08:00
|
|
|
|
2002-03-03 18:38:37 +08:00
|
|
|
|
2001-03-12 06:19:06 +08:00
|
|
|
void
|
2002-03-29 11:50:29 +08:00
|
|
|
gimp_iscissors_tool_register (GimpToolRegisterCallback callback,
|
2002-05-03 19:31:08 +08:00
|
|
|
gpointer data)
|
2001-03-12 06:19:06 +08:00
|
|
|
{
|
2002-03-29 11:50:29 +08:00
|
|
|
(* callback) (GIMP_TYPE_ISCISSORS_TOOL,
|
2008-03-09 23:05:54 +08:00
|
|
|
GIMP_TYPE_ISCISSORS_OPTIONS,
|
|
|
|
gimp_iscissors_options_gui,
|
2003-06-28 19:20:37 +08:00
|
|
|
0,
|
2002-03-21 20:17:17 +08:00
|
|
|
"gimp-iscissors-tool",
|
2019-10-16 00:37:26 +08:00
|
|
|
_("Scissors Select"),
|
2006-09-19 02:00:22 +08:00
|
|
|
_("Scissors Select Tool: Select shapes using intelligent edge-fitting"),
|
2004-10-18 19:29:58 +08:00
|
|
|
N_("Intelligent _Scissors"),
|
2001-11-21 07:00:47 +08:00
|
|
|
"I",
|
2003-08-22 09:42:57 +08:00
|
|
|
NULL, GIMP_HELP_TOOL_ISCISSORS,
|
2017-03-05 23:01:59 +08:00
|
|
|
GIMP_ICON_TOOL_ISCISSORS,
|
2002-05-03 19:31:08 +08:00
|
|
|
data);
|
2001-03-12 06:19:06 +08:00
|
|
|
}
|
1998-02-08 08:43:39 +08:00
|
|
|
|
2001-03-12 06:19:06 +08:00
|
|
|
static void
|
|
|
|
gimp_iscissors_tool_class_init (GimpIscissorsToolClass *klass)
|
|
|
|
{
|
2004-06-05 07:08:29 +08:00
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
|
|
|
|
GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
|
2012-05-21 01:19:12 +08:00
|
|
|
gint i, j;
|
|
|
|
gint radius;
|
2001-03-12 06:19:06 +08:00
|
|
|
|
2001-08-14 22:53:55 +08:00
|
|
|
object_class->finalize = gimp_iscissors_tool_finalize;
|
2001-03-12 06:19:06 +08:00
|
|
|
|
|
|
|
tool_class->control = gimp_iscissors_tool_control;
|
|
|
|
tool_class->button_press = gimp_iscissors_tool_button_press;
|
|
|
|
tool_class->button_release = gimp_iscissors_tool_button_release;
|
|
|
|
tool_class->motion = gimp_iscissors_tool_motion;
|
2015-03-29 04:31:03 +08:00
|
|
|
tool_class->key_press = gimp_iscissors_tool_key_press;
|
2001-03-12 06:19:06 +08:00
|
|
|
tool_class->oper_update = gimp_iscissors_tool_oper_update;
|
2001-11-09 03:14:51 +08:00
|
|
|
tool_class->cursor_update = gimp_iscissors_tool_cursor_update;
|
2017-07-05 00:48:03 +08:00
|
|
|
tool_class->can_undo = gimp_iscissors_tool_can_undo;
|
|
|
|
tool_class->can_redo = gimp_iscissors_tool_can_redo;
|
2015-03-29 04:31:03 +08:00
|
|
|
tool_class->undo = gimp_iscissors_tool_undo;
|
|
|
|
tool_class->redo = gimp_iscissors_tool_redo;
|
2001-03-12 06:19:06 +08:00
|
|
|
|
|
|
|
draw_tool_class->draw = gimp_iscissors_tool_draw;
|
2006-08-15 04:32:42 +08:00
|
|
|
|
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
{
|
|
|
|
/* The diagonal weight array */
|
|
|
|
diagonal_weight[i] = (int) (i * G_SQRT2);
|
|
|
|
|
|
|
|
/* The direction value array */
|
|
|
|
direction_value[i][0] = (127 - abs (127 - i)) * 2;
|
|
|
|
direction_value[i][1] = abs (127 - i) * 2;
|
|
|
|
direction_value[i][2] = abs (191 - i) * 2;
|
|
|
|
direction_value[i][3] = abs (63 - i) * 2;
|
|
|
|
}
|
|
|
|
|
2018-03-25 04:49:01 +08:00
|
|
|
/* set the 256th index of the direction_values to the highest cost */
|
2006-08-15 04:32:42 +08:00
|
|
|
direction_value[255][0] = 255;
|
|
|
|
direction_value[255][1] = 255;
|
|
|
|
direction_value[255][2] = 255;
|
|
|
|
direction_value[255][3] = 255;
|
2012-05-21 01:19:12 +08:00
|
|
|
|
|
|
|
/* compute the distance weights */
|
|
|
|
radius = GRADIENT_SEARCH >> 1;
|
|
|
|
|
|
|
|
for (i = 0; i < GRADIENT_SEARCH; i++)
|
|
|
|
for (j = 0; j < GRADIENT_SEARCH; j++)
|
|
|
|
distance_weights[i * GRADIENT_SEARCH + j] =
|
|
|
|
1.0 / (1 + sqrt (SQR (i - radius) + SQR (j - radius)));
|
2001-03-12 06:19:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_iscissors_tool_init (GimpIscissorsTool *iscissors)
|
|
|
|
{
|
2004-06-05 07:08:29 +08:00
|
|
|
GimpTool *tool = GIMP_TOOL (iscissors);
|
2001-03-12 06:19:06 +08:00
|
|
|
|
2002-05-03 05:03:27 +08:00
|
|
|
gimp_tool_control_set_scroll_lock (tool->control, TRUE);
|
|
|
|
gimp_tool_control_set_snap_to (tool->control, FALSE);
|
|
|
|
gimp_tool_control_set_preserve (tool->control, FALSE);
|
2012-09-07 05:55:35 +08:00
|
|
|
gimp_tool_control_set_dirty_mask (tool->control,
|
|
|
|
GIMP_DIRTY_IMAGE_SIZE |
|
|
|
|
GIMP_DIRTY_ACTIVE_DRAWABLE);
|
2015-03-21 04:18:23 +08:00
|
|
|
gimp_tool_control_set_tool_cursor (tool->control,
|
|
|
|
GIMP_TOOL_CURSOR_ISCISSORS);
|
2001-11-09 03:14:51 +08:00
|
|
|
|
2015-03-28 04:37:57 +08:00
|
|
|
iscissors->op = ISCISSORS_OP_NONE;
|
|
|
|
iscissors->curve = icurve_new ();
|
|
|
|
iscissors->state = NO_ACTION;
|
2001-03-12 06:19:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2001-08-14 22:53:55 +08:00
|
|
|
gimp_iscissors_tool_finalize (GObject *object)
|
2001-03-12 06:19:06 +08:00
|
|
|
{
|
2003-10-09 19:30:49 +08:00
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (object);
|
2001-03-12 06:19:06 +08:00
|
|
|
|
2015-03-28 04:37:57 +08:00
|
|
|
icurve_free (iscissors->curve);
|
|
|
|
iscissors->curve = NULL;
|
2007-05-23 22:56:21 +08:00
|
|
|
|
2001-08-14 22:53:55 +08:00
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
2001-03-12 06:19:06 +08:00
|
|
|
}
|
1998-02-08 08:43:39 +08:00
|
|
|
|
2001-03-12 06:19:06 +08:00
|
|
|
static void
|
2002-02-19 01:00:09 +08:00
|
|
|
gimp_iscissors_tool_control (GimpTool *tool,
|
|
|
|
GimpToolAction action,
|
2006-03-29 01:55:52 +08:00
|
|
|
GimpDisplay *display)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2004-06-05 07:08:29 +08:00
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-03-12 06:19:06 +08:00
|
|
|
switch (action)
|
|
|
|
{
|
2006-05-22 05:12:01 +08:00
|
|
|
case GIMP_TOOL_ACTION_PAUSE:
|
|
|
|
case GIMP_TOOL_ACTION_RESUME:
|
2001-03-12 06:19:06 +08:00
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2006-05-22 05:12:01 +08:00
|
|
|
case GIMP_TOOL_ACTION_HALT:
|
2015-03-18 05:19:29 +08:00
|
|
|
gimp_iscissors_tool_halt (iscissors, display);
|
|
|
|
break;
|
2011-04-02 17:58:29 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
case GIMP_TOOL_ACTION_COMMIT:
|
|
|
|
gimp_iscissors_tool_commit (iscissors, display);
|
2001-03-12 06:19:06 +08:00
|
|
|
break;
|
|
|
|
}
|
2011-04-02 17:40:56 +08:00
|
|
|
|
|
|
|
GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2009-06-20 23:37:31 +08:00
|
|
|
gimp_iscissors_tool_button_press (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpButtonPressType press_type,
|
|
|
|
GimpDisplay *display)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
|
|
|
|
GimpIscissorsOptions *options = GIMP_ISCISSORS_TOOL_GET_OPTIONS (tool);
|
|
|
|
GimpImage *image = gimp_display_get_image (display);
|
|
|
|
ISegment *segment;
|
2001-11-20 21:53:21 +08:00
|
|
|
|
2006-08-15 23:13:08 +08:00
|
|
|
iscissors->x = RINT (coords->x);
|
|
|
|
iscissors->y = RINT (coords->y);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* If the tool was being used in another image...reset it */
|
2011-03-31 03:47:27 +08:00
|
|
|
if (display != tool->display)
|
2011-04-02 17:58:29 +08:00
|
|
|
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2011-03-31 03:47:27 +08:00
|
|
|
gimp_tool_control_activate (tool->control);
|
2006-03-29 01:55:52 +08:00
|
|
|
tool->display = display;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
switch (iscissors->state)
|
|
|
|
{
|
2000-03-03 04:09:12 +08:00
|
|
|
case NO_ACTION:
|
1999-08-16 11:43:48 +08:00
|
|
|
iscissors->state = SEED_PLACEMENT;
|
2001-11-09 03:14:51 +08:00
|
|
|
|
2015-10-17 21:31:08 +08:00
|
|
|
if (! (state & gimp_get_extend_selection_mask ()))
|
2016-10-07 07:18:16 +08:00
|
|
|
find_max_gradient (iscissors, GIMP_PICKABLE (image),
|
2015-03-29 04:31:03 +08:00
|
|
|
&iscissors->x, &iscissors->y);
|
2000-03-03 04:09:12 +08:00
|
|
|
|
2009-10-08 01:00:42 +08:00
|
|
|
iscissors->x = CLAMP (iscissors->x, 0, gimp_image_get_width (image) - 1);
|
|
|
|
iscissors->y = CLAMP (iscissors->y, 0, gimp_image_get_height (image) - 1);
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
gimp_iscissors_tool_push_undo (iscissors);
|
|
|
|
|
2016-10-09 23:19:42 +08:00
|
|
|
segment = icurve_append_segment (iscissors->curve,
|
|
|
|
iscissors->x,
|
|
|
|
iscissors->y,
|
|
|
|
iscissors->x,
|
|
|
|
iscissors->y);
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2015-03-21 04:18:23 +08:00
|
|
|
/* Initialize the draw tool only on starting the tool */
|
2006-03-29 01:55:52 +08:00
|
|
|
gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2000-03-03 04:09:12 +08:00
|
|
|
default:
|
2005-08-22 04:28:05 +08:00
|
|
|
/* Check if the mouse click occurred on a vertex or the curve itself */
|
2006-08-15 04:32:42 +08:00
|
|
|
if (clicked_on_vertex (iscissors, coords->x, coords->y))
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
|
|
|
iscissors->state = SEED_ADJUSTMENT;
|
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
/* recalculate both segments */
|
|
|
|
if (iscissors->segment1)
|
|
|
|
{
|
|
|
|
iscissors->segment1->x1 = iscissors->x;
|
|
|
|
iscissors->segment1->y1 = iscissors->y;
|
|
|
|
|
|
|
|
if (options->interactive)
|
|
|
|
calculate_segment (iscissors, iscissors->segment1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iscissors->segment2)
|
|
|
|
{
|
|
|
|
iscissors->segment2->x2 = iscissors->x;
|
|
|
|
iscissors->segment2->y2 = iscissors->y;
|
|
|
|
|
|
|
|
if (options->interactive)
|
|
|
|
calculate_segment (iscissors, iscissors->segment2);
|
|
|
|
}
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
2016-10-09 23:19:42 +08:00
|
|
|
/* If the iscissors is closed, check if the click was inside */
|
|
|
|
else if (iscissors->curve->closed && iscissors->mask &&
|
2006-04-12 20:49:29 +08:00
|
|
|
gimp_pickable_get_opacity_at (GIMP_PICKABLE (iscissors->mask),
|
2005-07-12 03:21:52 +08:00
|
|
|
iscissors->x,
|
|
|
|
iscissors->y))
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-18 05:19:29 +08:00
|
|
|
gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, display);
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
2016-10-09 23:19:42 +08:00
|
|
|
else if (! iscissors->curve->closed)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2016-10-09 23:19:42 +08:00
|
|
|
/* if we're not closed, we're adding a new point */
|
2002-03-03 18:38:37 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
ISegment *last = g_queue_peek_tail (iscissors->curve->segments);
|
2002-03-03 18:38:37 +08:00
|
|
|
|
2006-04-12 20:49:29 +08:00
|
|
|
iscissors->state = SEED_PLACEMENT;
|
2001-01-02 22:50:11 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
gimp_iscissors_tool_push_undo (iscissors);
|
|
|
|
|
|
|
|
if (last->x1 == last->x2 &&
|
|
|
|
last->y1 == last->y2)
|
|
|
|
{
|
|
|
|
last->x2 = iscissors->x;
|
|
|
|
last->y2 = iscissors->y;
|
|
|
|
|
|
|
|
if (options->interactive)
|
|
|
|
calculate_segment (iscissors, last);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-10-09 23:19:42 +08:00
|
|
|
segment = icurve_append_segment (iscissors->curve,
|
|
|
|
last->x2,
|
|
|
|
last->y2,
|
|
|
|
iscissors->x,
|
|
|
|
iscissors->y);
|
2015-03-29 04:31:03 +08:00
|
|
|
|
|
|
|
if (options->interactive)
|
|
|
|
calculate_segment (iscissors, segment);
|
|
|
|
}
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
|
|
|
}
|
2015-03-29 04:31:03 +08:00
|
|
|
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
static void
|
2001-03-12 06:19:06 +08:00
|
|
|
iscissors_convert (GimpIscissorsTool *iscissors,
|
2006-04-12 20:49:29 +08:00
|
|
|
GimpDisplay *display)
|
1999-10-06 02:05:34 +08:00
|
|
|
{
|
2006-09-06 02:25:31 +08:00
|
|
|
GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (iscissors);
|
2009-10-08 01:00:42 +08:00
|
|
|
GimpImage *image = gimp_display_get_image (display);
|
2003-10-09 19:30:49 +08:00
|
|
|
GimpScanConvert *sc;
|
1999-10-06 02:05:34 +08:00
|
|
|
|
2016-10-09 23:19:42 +08:00
|
|
|
sc = icurve_create_scan_convert (iscissors->curve);
|
2008-05-09 07:35:53 +08:00
|
|
|
|
2000-03-03 04:09:12 +08:00
|
|
|
if (iscissors->mask)
|
2003-01-06 06:07:10 +08:00
|
|
|
g_object_unref (iscissors->mask);
|
2001-03-12 06:19:06 +08:00
|
|
|
|
2009-10-08 01:00:42 +08:00
|
|
|
iscissors->mask = gimp_channel_new_mask (image,
|
|
|
|
gimp_image_get_width (image),
|
|
|
|
gimp_image_get_height (image));
|
2007-12-22 04:10:27 +08:00
|
|
|
gimp_scan_convert_render (sc,
|
2012-03-21 07:42:44 +08:00
|
|
|
gimp_drawable_get_buffer (GIMP_DRAWABLE (iscissors->mask)),
|
2003-10-09 19:30:49 +08:00
|
|
|
0, 0, options->antialias);
|
2015-03-21 04:18:23 +08:00
|
|
|
|
2001-05-26 02:10:38 +08:00
|
|
|
gimp_scan_convert_free (sc);
|
1999-10-06 02:05:34 +08:00
|
|
|
}
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static void
|
2007-02-28 02:55:12 +08:00
|
|
|
gimp_iscissors_tool_button_release (GimpTool *tool,
|
2008-11-01 23:17:36 +08:00
|
|
|
const GimpCoords *coords,
|
2007-02-28 02:55:12 +08:00
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpButtonReleaseType release_type,
|
|
|
|
GimpDisplay *display)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
|
|
|
|
GimpIscissorsOptions *options = GIMP_ISCISSORS_TOOL_GET_OPTIONS (tool);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2011-03-31 03:47:27 +08:00
|
|
|
gimp_tool_control_halt (tool->control);
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Make sure X didn't skip the button release event -- as it's known
|
2001-11-09 03:14:51 +08:00
|
|
|
* to do
|
|
|
|
*/
|
1999-08-16 11:43:48 +08:00
|
|
|
if (iscissors->state == WAITING)
|
|
|
|
return;
|
|
|
|
|
2002-03-03 18:38:37 +08:00
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2007-02-28 02:55:12 +08:00
|
|
|
if (release_type != GIMP_BUTTON_RELEASE_CANCEL)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
/* Progress to the next stage of intelligent selection */
|
|
|
|
switch (iscissors->state)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
|
|
|
case SEED_PLACEMENT:
|
2015-03-27 17:45:28 +08:00
|
|
|
/* Add a new segment */
|
2015-03-28 04:37:57 +08:00
|
|
|
if (! iscissors->curve->first_point)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
|
|
|
/* Determine if we're connecting to the first point */
|
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
ISegment *segment = g_queue_peek_head (iscissors->curve->segments);
|
2006-04-12 20:49:29 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (tool), display,
|
|
|
|
iscissors->x, iscissors->y,
|
|
|
|
GIMP_HANDLE_CIRCLE,
|
|
|
|
segment->x1, segment->y1,
|
|
|
|
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
|
|
|
|
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
|
|
|
|
GIMP_HANDLE_ANCHOR_CENTER))
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
iscissors->x = segment->x1;
|
|
|
|
iscissors->y = segment->y1;
|
|
|
|
|
2016-10-09 23:19:42 +08:00
|
|
|
icurve_close (iscissors->curve);
|
2006-04-12 20:49:29 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
if (! options->interactive)
|
2016-10-09 23:19:42 +08:00
|
|
|
{
|
|
|
|
segment = g_queue_peek_tail (iscissors->curve->segments);
|
|
|
|
calculate_segment (iscissors, segment);
|
|
|
|
}
|
2007-05-23 22:56:21 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
gimp_iscissors_tool_free_redo (iscissors);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
segment = g_queue_peek_tail (iscissors->curve->segments);
|
|
|
|
|
|
|
|
if (segment->x1 != segment->x2 ||
|
|
|
|
segment->y1 != segment->y2)
|
|
|
|
{
|
|
|
|
if (! options->interactive)
|
|
|
|
calculate_segment (iscissors, segment);
|
2007-05-23 22:56:21 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
gimp_iscissors_tool_free_redo (iscissors);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_iscissors_tool_pop_undo (iscissors);
|
|
|
|
}
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else /* this was our first point */
|
|
|
|
{
|
2015-03-28 04:37:57 +08:00
|
|
|
iscissors->curve->first_point = FALSE;
|
2015-03-29 04:31:03 +08:00
|
|
|
|
|
|
|
gimp_iscissors_tool_free_redo (iscissors);
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SEED_ADJUSTMENT:
|
2015-03-29 05:41:43 +08:00
|
|
|
if (state & gimp_get_modify_selection_mask ())
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-29 05:41:43 +08:00
|
|
|
if (iscissors->segment1 && iscissors->segment2)
|
|
|
|
{
|
2016-10-09 23:19:42 +08:00
|
|
|
icurve_delete_segment (iscissors->curve,
|
|
|
|
iscissors->segment2);
|
2015-03-29 05:41:43 +08:00
|
|
|
|
|
|
|
calculate_segment (iscissors, iscissors->segment1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-29 05:41:43 +08:00
|
|
|
/* recalculate both segments */
|
|
|
|
|
|
|
|
if (iscissors->segment1)
|
|
|
|
{
|
|
|
|
if (! options->interactive)
|
|
|
|
calculate_segment (iscissors, iscissors->segment1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iscissors->segment2)
|
|
|
|
{
|
|
|
|
if (! options->interactive)
|
|
|
|
calculate_segment (iscissors, iscissors->segment2);
|
|
|
|
}
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
2015-03-29 04:31:03 +08:00
|
|
|
|
|
|
|
gimp_iscissors_tool_free_redo (iscissors);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (iscissors->state)
|
|
|
|
{
|
|
|
|
case SEED_PLACEMENT:
|
|
|
|
case SEED_ADJUSTMENT:
|
|
|
|
gimp_iscissors_tool_pop_undo (iscissors);
|
2006-04-12 20:49:29 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
2015-03-29 04:52:26 +08:00
|
|
|
if (iscissors->curve->first_point)
|
|
|
|
iscissors->state = NO_ACTION;
|
|
|
|
else
|
|
|
|
iscissors->state = WAITING;
|
2001-03-12 06:19:06 +08:00
|
|
|
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* convert the curves into a region */
|
2016-10-09 23:19:42 +08:00
|
|
|
if (iscissors->curve->closed)
|
2006-03-29 01:55:52 +08:00
|
|
|
iscissors_convert (iscissors, display);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-11-01 23:17:36 +08:00
|
|
|
gimp_iscissors_tool_motion (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
guint32 time,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
|
|
|
|
GimpIscissorsOptions *options = GIMP_ISCISSORS_TOOL_GET_OPTIONS (tool);
|
|
|
|
GimpImage *image = gimp_display_get_image (display);
|
|
|
|
ISegment *segment;
|
2001-11-20 21:53:21 +08:00
|
|
|
|
2003-05-06 18:30:34 +08:00
|
|
|
if (iscissors->state == NO_ACTION)
|
1999-08-16 11:43:48 +08:00
|
|
|
return;
|
|
|
|
|
2001-03-12 06:19:06 +08:00
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2006-08-15 23:13:08 +08:00
|
|
|
iscissors->x = RINT (coords->x);
|
|
|
|
iscissors->y = RINT (coords->y);
|
2003-08-22 09:42:57 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
/* Hold the shift key down to disable the auto-edge snap feature */
|
2015-10-17 21:31:08 +08:00
|
|
|
if (! (state & gimp_get_extend_selection_mask ()))
|
2016-10-07 07:18:16 +08:00
|
|
|
find_max_gradient (iscissors, GIMP_PICKABLE (image),
|
2015-03-29 04:31:03 +08:00
|
|
|
&iscissors->x, &iscissors->y);
|
|
|
|
|
|
|
|
iscissors->x = CLAMP (iscissors->x, 0, gimp_image_get_width (image) - 1);
|
|
|
|
iscissors->y = CLAMP (iscissors->y, 0, gimp_image_get_height (image) - 1);
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
switch (iscissors->state)
|
|
|
|
{
|
2000-03-03 04:09:12 +08:00
|
|
|
case SEED_PLACEMENT:
|
2015-03-29 04:31:03 +08:00
|
|
|
segment = g_queue_peek_tail (iscissors->curve->segments);
|
2000-03-03 04:09:12 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
segment->x2 = iscissors->x;
|
|
|
|
segment->y2 = iscissors->y;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2015-03-28 04:37:57 +08:00
|
|
|
if (iscissors->curve->first_point)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
segment->x1 = segment->x2;
|
|
|
|
segment->y1 = segment->y2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (options->interactive)
|
|
|
|
calculate_segment (iscissors, segment);
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
break;
|
|
|
|
|
2000-03-03 04:09:12 +08:00
|
|
|
case SEED_ADJUSTMENT:
|
2015-03-29 04:31:03 +08:00
|
|
|
if (iscissors->segment1)
|
|
|
|
{
|
|
|
|
iscissors->segment1->x1 = iscissors->x;
|
|
|
|
iscissors->segment1->y1 = iscissors->y;
|
2000-03-03 04:09:12 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
if (options->interactive)
|
|
|
|
calculate_segment (iscissors, iscissors->segment1);
|
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
if (iscissors->segment2)
|
|
|
|
{
|
|
|
|
iscissors->segment2->x2 = iscissors->x;
|
|
|
|
iscissors->segment2->y2 = iscissors->y;
|
|
|
|
|
|
|
|
if (options->interactive)
|
|
|
|
calculate_segment (iscissors, iscissors->segment2);
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2000-03-03 04:09:12 +08:00
|
|
|
default:
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2001-03-12 06:19:06 +08:00
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2001-03-12 06:19:06 +08:00
|
|
|
gimp_iscissors_tool_draw (GimpDrawTool *draw_tool)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2011-04-02 17:40:56 +08:00
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (draw_tool);
|
|
|
|
GimpIscissorsOptions *options = GIMP_ISCISSORS_TOOL_GET_OPTIONS (draw_tool);
|
2015-03-29 04:31:03 +08:00
|
|
|
GimpCanvasItem *item;
|
|
|
|
GList *list;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
/* First, render all segments and lines */
|
2015-03-28 04:37:57 +08:00
|
|
|
if (! iscissors->curve->first_point)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2015-03-28 04:37:57 +08:00
|
|
|
for (list = g_queue_peek_head_link (iscissors->curve->segments);
|
2007-05-23 22:56:21 +08:00
|
|
|
list;
|
|
|
|
list = g_list_next (list))
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
ISegment *segment = list->data;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
/* plot the segment */
|
|
|
|
item = iscissors_draw_segment (draw_tool, segment);
|
|
|
|
|
|
|
|
/* if this segment is currently being added or adjusted */
|
|
|
|
if ((iscissors->state == SEED_PLACEMENT &&
|
|
|
|
! list->next)
|
|
|
|
||
|
|
|
|
(iscissors->state == SEED_ADJUSTMENT &&
|
|
|
|
(segment == iscissors->segment1 ||
|
|
|
|
segment == iscissors->segment2)))
|
2002-03-03 18:38:37 +08:00
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
if (! options->interactive)
|
|
|
|
item = gimp_draw_tool_add_line (draw_tool,
|
|
|
|
segment->x1, segment->y1,
|
|
|
|
segment->x2, segment->y2);
|
2002-03-03 18:38:37 +08:00
|
|
|
|
2017-06-11 23:23:52 +08:00
|
|
|
if (item)
|
|
|
|
gimp_canvas_item_set_highlight (item, TRUE);
|
2002-03-03 18:38:37 +08:00
|
|
|
}
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
/* Then, render the handles on top of the segments */
|
|
|
|
for (list = g_queue_peek_head_link (iscissors->curve->segments);
|
|
|
|
list;
|
|
|
|
list = g_list_next (list))
|
1999-08-16 11:43:48 +08:00
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
ISegment *segment = list->data;
|
|
|
|
|
|
|
|
if (! iscissors->curve->first_point)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
gboolean adjustment = (iscissors->state == SEED_ADJUSTMENT &&
|
|
|
|
segment == iscissors->segment1);
|
|
|
|
|
|
|
|
item = gimp_draw_tool_add_handle (draw_tool,
|
|
|
|
adjustment ?
|
|
|
|
GIMP_HANDLE_CROSS :
|
|
|
|
GIMP_HANDLE_FILLED_CIRCLE,
|
|
|
|
segment->x1,
|
|
|
|
segment->y1,
|
|
|
|
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
|
|
|
|
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
|
|
|
|
GIMP_HANDLE_ANCHOR_CENTER);
|
|
|
|
|
|
|
|
if (adjustment)
|
|
|
|
gimp_canvas_item_set_highlight (item, TRUE);
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
2010-09-26 01:02:22 +08:00
|
|
|
|
2016-10-09 23:19:42 +08:00
|
|
|
/* Draw the last point if the curve is not closed */
|
|
|
|
if (! list->next && ! iscissors->curve->closed)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
gboolean placement = (iscissors->state == SEED_PLACEMENT);
|
|
|
|
|
|
|
|
item = gimp_draw_tool_add_handle (draw_tool,
|
|
|
|
placement ?
|
|
|
|
GIMP_HANDLE_CROSS :
|
|
|
|
GIMP_HANDLE_FILLED_CIRCLE,
|
|
|
|
segment->x2,
|
|
|
|
segment->y2,
|
|
|
|
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
|
|
|
|
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
|
|
|
|
GIMP_HANDLE_ANCHOR_CENTER);
|
|
|
|
|
|
|
|
if (placement)
|
|
|
|
gimp_canvas_item_set_highlight (item, TRUE);
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
static GimpCanvasItem *
|
2015-03-27 17:45:28 +08:00
|
|
|
iscissors_draw_segment (GimpDrawTool *draw_tool,
|
|
|
|
ISegment *segment)
|
1999-08-16 11:43:48 +08:00
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
GimpCanvasItem *item;
|
|
|
|
GimpVector2 *points;
|
|
|
|
gpointer *point;
|
|
|
|
gint i, len;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
if (! segment->points)
|
2015-03-29 04:31:03 +08:00
|
|
|
return NULL;
|
2001-11-12 22:45:58 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
len = segment->points->len;
|
2006-08-09 19:15:15 +08:00
|
|
|
|
2008-02-27 01:30:33 +08:00
|
|
|
points = g_new (GimpVector2, len);
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
for (i = 0, point = segment->points->pdata; i < len; i++, point++)
|
2000-03-03 04:09:12 +08:00
|
|
|
{
|
2006-08-09 19:15:15 +08:00
|
|
|
guint32 coords = GPOINTER_TO_INT (*point);
|
2001-11-12 22:45:58 +08:00
|
|
|
|
2008-02-27 01:30:33 +08:00
|
|
|
points[i].x = (coords & 0x0000ffff);
|
|
|
|
points[i].y = (coords >> 16);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
2006-08-09 19:15:15 +08:00
|
|
|
|
2017-06-20 03:53:49 +08:00
|
|
|
item = gimp_draw_tool_add_lines (draw_tool, points, len, NULL, FALSE);
|
2007-06-19 03:58:06 +08:00
|
|
|
|
2006-08-09 19:15:15 +08:00
|
|
|
g_free (points);
|
2015-03-29 04:31:03 +08:00
|
|
|
|
|
|
|
return item;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
2000-03-03 04:09:12 +08:00
|
|
|
static void
|
2008-11-01 23:17:36 +08:00
|
|
|
gimp_iscissors_tool_oper_update (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GdkModifierType state,
|
|
|
|
gboolean proximity,
|
|
|
|
GimpDisplay *display)
|
2000-03-03 04:09:12 +08:00
|
|
|
{
|
2003-10-09 19:30:49 +08:00
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
|
2000-03-03 04:09:12 +08:00
|
|
|
|
2006-03-25 22:23:09 +08:00
|
|
|
GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity,
|
2006-03-29 01:55:52 +08:00
|
|
|
display);
|
2007-01-19 17:32:35 +08:00
|
|
|
/* parent sets a message in the status bar, but it will be replaced here */
|
2002-03-03 18:38:37 +08:00
|
|
|
|
2006-08-14 20:34:42 +08:00
|
|
|
if (mouse_over_vertex (iscissors, coords->x, coords->y) > 1)
|
2000-03-03 21:01:49 +08:00
|
|
|
{
|
2015-10-17 21:31:08 +08:00
|
|
|
GdkModifierType snap_mask = gimp_get_extend_selection_mask ();
|
2015-03-29 05:41:43 +08:00
|
|
|
GdkModifierType remove_mask = gimp_get_modify_selection_mask ();
|
|
|
|
|
|
|
|
if (state & remove_mask)
|
|
|
|
{
|
|
|
|
gimp_tool_replace_status (tool, display,
|
|
|
|
_("Click to remove this point"));
|
2015-03-29 05:46:49 +08:00
|
|
|
iscissors->op = ISCISSORS_OP_REMOVE_POINT;
|
2015-03-29 05:41:43 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gchar *status =
|
|
|
|
gimp_suggest_modifiers (_("Click-Drag to move this point"),
|
2015-10-17 21:31:08 +08:00
|
|
|
(snap_mask | remove_mask) & ~state,
|
2015-03-29 05:41:43 +08:00
|
|
|
_("%s: disable auto-snap"),
|
|
|
|
_("%s: remove this point"),
|
|
|
|
NULL);
|
|
|
|
gimp_tool_replace_status (tool, display, "%s", status);
|
|
|
|
g_free (status);
|
|
|
|
iscissors->op = ISCISSORS_OP_MOVE_POINT;
|
|
|
|
}
|
2000-03-03 21:01:49 +08:00
|
|
|
}
|
2015-03-27 17:45:28 +08:00
|
|
|
else if (mouse_over_segment (iscissors, coords->x, coords->y))
|
2000-06-14 18:59:16 +08:00
|
|
|
{
|
2015-03-28 04:37:57 +08:00
|
|
|
ISegment *segment = g_queue_peek_head (iscissors->curve->segments);
|
2007-01-19 17:32:35 +08:00
|
|
|
|
|
|
|
if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (tool), display,
|
|
|
|
RINT (coords->x), RINT (coords->y),
|
|
|
|
GIMP_HANDLE_CIRCLE,
|
2015-03-27 17:45:28 +08:00
|
|
|
segment->x1, segment->y1,
|
2010-11-09 05:37:00 +08:00
|
|
|
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
|
|
|
|
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
|
2010-10-19 02:13:09 +08:00
|
|
|
GIMP_HANDLE_ANCHOR_CENTER))
|
2007-01-19 17:32:35 +08:00
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
gimp_tool_replace_status (tool, display,
|
|
|
|
_("Click to close the curve"));
|
2007-01-19 17:32:35 +08:00
|
|
|
iscissors->op = ISCISSORS_OP_CONNECT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
gimp_tool_replace_status (tool, display,
|
|
|
|
_("Click to add a point on this segment"));
|
2007-01-19 17:32:35 +08:00
|
|
|
iscissors->op = ISCISSORS_OP_ADD_POINT;
|
|
|
|
}
|
2000-03-03 04:09:12 +08:00
|
|
|
}
|
2016-10-09 23:19:42 +08:00
|
|
|
else if (iscissors->curve->closed && iscissors->mask)
|
2000-03-03 21:01:49 +08:00
|
|
|
{
|
2005-07-12 03:21:52 +08:00
|
|
|
if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (iscissors->mask),
|
2006-08-15 23:13:08 +08:00
|
|
|
RINT (coords->x),
|
|
|
|
RINT (coords->y)))
|
2002-03-03 18:38:37 +08:00
|
|
|
{
|
2007-01-19 17:32:35 +08:00
|
|
|
if (proximity)
|
|
|
|
{
|
|
|
|
gimp_tool_replace_status (tool, display,
|
|
|
|
_("Click or press Enter to convert to"
|
|
|
|
" a selection"));
|
|
|
|
}
|
2002-03-03 18:38:37 +08:00
|
|
|
iscissors->op = ISCISSORS_OP_SELECT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-01-19 17:32:35 +08:00
|
|
|
if (proximity)
|
|
|
|
{
|
|
|
|
gimp_tool_replace_status (tool, display,
|
|
|
|
_("Press Enter to convert to a"
|
|
|
|
" selection"));
|
|
|
|
}
|
2002-03-03 18:38:37 +08:00
|
|
|
iscissors->op = ISCISSORS_OP_IMPOSSIBLE;
|
|
|
|
}
|
2000-03-03 21:01:49 +08:00
|
|
|
}
|
2000-03-03 04:09:12 +08:00
|
|
|
else
|
|
|
|
{
|
2002-03-03 18:38:37 +08:00
|
|
|
switch (iscissors->state)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
|
|
|
case WAITING:
|
2007-01-19 17:32:35 +08:00
|
|
|
if (proximity)
|
|
|
|
{
|
2015-10-17 21:31:08 +08:00
|
|
|
GdkModifierType snap_mask = gimp_get_extend_selection_mask ();
|
|
|
|
gchar *status;
|
2007-01-19 17:32:35 +08:00
|
|
|
|
|
|
|
status = gimp_suggest_modifiers (_("Click or Click-Drag to add a"
|
|
|
|
" point"),
|
2015-10-17 21:31:08 +08:00
|
|
|
snap_mask & ~state,
|
2007-01-19 17:32:35 +08:00
|
|
|
_("%s: disable auto-snap"),
|
|
|
|
NULL, NULL);
|
2008-11-04 20:33:09 +08:00
|
|
|
gimp_tool_replace_status (tool, display, "%s", status);
|
2007-01-19 17:32:35 +08:00
|
|
|
g_free (status);
|
|
|
|
}
|
2002-03-03 18:38:37 +08:00
|
|
|
iscissors->op = ISCISSORS_OP_ADD_POINT;
|
2006-04-12 20:49:29 +08:00
|
|
|
break;
|
2002-03-03 18:38:37 +08:00
|
|
|
|
2006-04-12 20:49:29 +08:00
|
|
|
default:
|
2007-01-19 17:32:35 +08:00
|
|
|
/* if NO_ACTION, keep parent's status bar message (selection tool) */
|
2002-03-03 18:38:37 +08:00
|
|
|
iscissors->op = ISCISSORS_OP_NONE;
|
|
|
|
break;
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
2000-03-03 04:09:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-11-01 23:17:36 +08:00
|
|
|
gimp_iscissors_tool_cursor_update (GimpTool *tool,
|
|
|
|
const GimpCoords *coords,
|
|
|
|
GdkModifierType state,
|
|
|
|
GimpDisplay *display)
|
2000-03-03 04:09:12 +08:00
|
|
|
{
|
2003-10-09 19:30:49 +08:00
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
|
2004-06-14 18:19:39 +08:00
|
|
|
GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_NONE;
|
2000-03-03 04:09:12 +08:00
|
|
|
|
2000-06-14 18:59:16 +08:00
|
|
|
switch (iscissors->op)
|
2000-03-03 04:09:12 +08:00
|
|
|
{
|
2002-03-03 18:38:37 +08:00
|
|
|
case ISCISSORS_OP_SELECT:
|
2008-04-12 19:40:52 +08:00
|
|
|
{
|
|
|
|
GimpSelectionOptions *options;
|
|
|
|
|
|
|
|
options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
|
|
|
|
|
|
|
|
/* Do not overwrite the modifiers for add, subtract, intersect */
|
2008-04-13 21:50:01 +08:00
|
|
|
if (options->operation == GIMP_CHANNEL_OP_REPLACE)
|
2008-04-12 19:40:52 +08:00
|
|
|
{
|
|
|
|
modifier = GIMP_CURSOR_MODIFIER_SELECT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2006-06-02 04:30:52 +08:00
|
|
|
|
2002-03-03 18:38:37 +08:00
|
|
|
case ISCISSORS_OP_MOVE_POINT:
|
2004-06-14 18:19:39 +08:00
|
|
|
modifier = GIMP_CURSOR_MODIFIER_MOVE;
|
2000-06-14 18:59:16 +08:00
|
|
|
break;
|
2006-06-02 04:30:52 +08:00
|
|
|
|
2002-03-03 18:38:37 +08:00
|
|
|
case ISCISSORS_OP_ADD_POINT:
|
2004-06-14 18:19:39 +08:00
|
|
|
modifier = GIMP_CURSOR_MODIFIER_PLUS;
|
2000-06-14 18:59:16 +08:00
|
|
|
break;
|
2006-06-02 04:30:52 +08:00
|
|
|
|
2015-03-29 05:41:43 +08:00
|
|
|
case ISCISSORS_OP_REMOVE_POINT:
|
|
|
|
modifier = GIMP_CURSOR_MODIFIER_MINUS;
|
|
|
|
break;
|
|
|
|
|
2007-01-19 17:32:35 +08:00
|
|
|
case ISCISSORS_OP_CONNECT:
|
|
|
|
modifier = GIMP_CURSOR_MODIFIER_JOIN;
|
|
|
|
break;
|
|
|
|
|
2002-03-03 18:38:37 +08:00
|
|
|
case ISCISSORS_OP_IMPOSSIBLE:
|
2006-06-02 04:30:52 +08:00
|
|
|
modifier = GIMP_CURSOR_MODIFIER_BAD;
|
2000-06-14 18:59:16 +08:00
|
|
|
break;
|
2006-06-02 04:30:52 +08:00
|
|
|
|
2000-06-14 18:59:16 +08:00
|
|
|
default:
|
2002-03-03 18:38:37 +08:00
|
|
|
break;
|
2000-03-03 04:09:12 +08:00
|
|
|
}
|
2002-02-05 01:43:01 +08:00
|
|
|
|
2008-04-12 19:40:52 +08:00
|
|
|
if (modifier != GIMP_CURSOR_MODIFIER_NONE)
|
|
|
|
{
|
|
|
|
gimp_tool_set_cursor (tool, display,
|
|
|
|
GIMP_CURSOR_MOUSE,
|
|
|
|
GIMP_TOOL_CURSOR_ISCISSORS,
|
|
|
|
modifier);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords,
|
|
|
|
state, display);
|
|
|
|
}
|
2000-03-03 04:09:12 +08:00
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2007-01-19 17:32:35 +08:00
|
|
|
static gboolean
|
|
|
|
gimp_iscissors_tool_key_press (GimpTool *tool,
|
|
|
|
GdkEventKey *kevent,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
|
|
|
|
|
|
|
|
if (display != tool->display)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
switch (kevent->keyval)
|
|
|
|
{
|
2016-11-08 20:45:12 +08:00
|
|
|
case GDK_KEY_BackSpace:
|
|
|
|
if (! iscissors->curve->closed &&
|
|
|
|
g_queue_peek_tail (iscissors->curve->segments))
|
|
|
|
{
|
Issue #2272 - Crash when using the intelligent-scissors tool ...
... after erasing all points
When erasing the last remaining point in the iscissors tool, halt
the tool, rather than leaving the tool active with an empty curve,
which it is not prepared to handle, and which results in a segfault
once trying to add a new point.
Additionally, when erasing the last remaining segment (i.e., the
two last remaining points), don't erase the entire segment (i.e.,
both points), but rather convert the segment to its initial point,
so that, in effect, we only erase the last point of the segment.
2018-09-26 14:06:40 +08:00
|
|
|
ISegment *segment = g_queue_peek_tail (iscissors->curve->segments);
|
2016-11-08 20:45:12 +08:00
|
|
|
|
Issue #2272 - Crash when using the intelligent-scissors tool ...
... after erasing all points
When erasing the last remaining point in the iscissors tool, halt
the tool, rather than leaving the tool active with an empty curve,
which it is not prepared to handle, and which results in a segfault
once trying to add a new point.
Additionally, when erasing the last remaining segment (i.e., the
two last remaining points), don't erase the entire segment (i.e.,
both points), but rather convert the segment to its initial point,
so that, in effect, we only erase the last point of the segment.
2018-09-26 14:06:40 +08:00
|
|
|
if (g_queue_get_length (iscissors->curve->segments) > 1)
|
|
|
|
{
|
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
|
|
|
|
|
|
|
|
gimp_iscissors_tool_push_undo (iscissors);
|
|
|
|
icurve_delete_segment (iscissors->curve, segment);
|
|
|
|
gimp_iscissors_tool_free_redo (iscissors);
|
|
|
|
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
|
|
|
}
|
|
|
|
else if (segment->x2 != segment->x1 || segment->y2 != segment->y1)
|
|
|
|
{
|
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
|
|
|
|
|
|
|
|
gimp_iscissors_tool_push_undo (iscissors);
|
|
|
|
segment->x2 = segment->x1;
|
|
|
|
segment->y2 = segment->y1;
|
|
|
|
g_ptr_array_remove_range (segment->points,
|
|
|
|
0, segment->points->len);
|
|
|
|
gimp_iscissors_tool_free_redo (iscissors);
|
2016-11-08 20:45:12 +08:00
|
|
|
|
Issue #2272 - Crash when using the intelligent-scissors tool ...
... after erasing all points
When erasing the last remaining point in the iscissors tool, halt
the tool, rather than leaving the tool active with an empty curve,
which it is not prepared to handle, and which results in a segfault
once trying to add a new point.
Additionally, when erasing the last remaining segment (i.e., the
two last remaining points), don't erase the entire segment (i.e.,
both points), but rather convert the segment to its initial point,
so that, in effect, we only erase the last point of the segment.
2018-09-26 14:06:40 +08:00
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
|
|
|
|
}
|
2016-11-08 20:45:12 +08:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
|
2011-03-29 21:27:25 +08:00
|
|
|
case GDK_KEY_Return:
|
|
|
|
case GDK_KEY_KP_Enter:
|
|
|
|
case GDK_KEY_ISO_Enter:
|
2016-10-09 23:19:42 +08:00
|
|
|
if (iscissors->curve->closed && iscissors->mask)
|
2007-01-19 17:32:35 +08:00
|
|
|
{
|
2015-03-18 05:19:29 +08:00
|
|
|
gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, display);
|
2007-01-19 17:32:35 +08:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
|
2011-03-29 21:27:25 +08:00
|
|
|
case GDK_KEY_Escape:
|
2007-01-19 17:32:35 +08:00
|
|
|
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
static const gchar *
|
2017-07-05 00:48:03 +08:00
|
|
|
gimp_iscissors_tool_can_undo (GimpTool *tool,
|
|
|
|
GimpDisplay *display)
|
2015-03-29 04:31:03 +08:00
|
|
|
{
|
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
|
|
|
|
|
2017-07-09 23:22:06 +08:00
|
|
|
if (! iscissors->undo_stack)
|
2015-03-29 04:31:03 +08:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return _("Modify Scissors Curve");
|
|
|
|
}
|
|
|
|
|
|
|
|
static const gchar *
|
2017-07-05 00:48:03 +08:00
|
|
|
gimp_iscissors_tool_can_redo (GimpTool *tool,
|
|
|
|
GimpDisplay *display)
|
2015-03-29 04:31:03 +08:00
|
|
|
{
|
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
|
|
|
|
|
2017-07-09 23:22:06 +08:00
|
|
|
if (! iscissors->redo_stack)
|
2015-03-29 04:31:03 +08:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return _("Modify Scissors Curve");
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gimp_iscissors_tool_undo (GimpTool *tool,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
|
|
|
|
|
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
|
|
|
|
|
|
|
|
iscissors->redo_stack = g_list_prepend (iscissors->redo_stack,
|
|
|
|
iscissors->curve);
|
|
|
|
|
|
|
|
iscissors->curve = iscissors->undo_stack->data;
|
|
|
|
|
|
|
|
iscissors->undo_stack = g_list_remove (iscissors->undo_stack,
|
|
|
|
iscissors->curve);
|
|
|
|
|
|
|
|
if (! iscissors->undo_stack)
|
|
|
|
{
|
|
|
|
iscissors->state = NO_ACTION;
|
|
|
|
|
|
|
|
gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gimp_iscissors_tool_redo (GimpTool *tool,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
|
|
|
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
|
|
|
|
|
|
|
|
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
|
|
|
|
|
|
|
|
if (! iscissors->undo_stack)
|
|
|
|
{
|
|
|
|
iscissors->state = WAITING;
|
|
|
|
|
|
|
|
gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
|
|
|
|
}
|
|
|
|
|
|
|
|
iscissors->undo_stack = g_list_prepend (iscissors->undo_stack,
|
|
|
|
iscissors->curve);
|
|
|
|
|
|
|
|
iscissors->curve = iscissors->redo_stack->data;
|
|
|
|
|
|
|
|
iscissors->redo_stack = g_list_remove (iscissors->redo_stack,
|
|
|
|
iscissors->curve);
|
|
|
|
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_iscissors_tool_push_undo (GimpIscissorsTool *iscissors)
|
|
|
|
{
|
|
|
|
iscissors->undo_stack = g_list_prepend (iscissors->undo_stack,
|
|
|
|
icurve_copy (iscissors->curve));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_iscissors_tool_pop_undo (GimpIscissorsTool *iscissors)
|
|
|
|
{
|
|
|
|
icurve_free (iscissors->curve);
|
|
|
|
iscissors->curve = iscissors->undo_stack->data;
|
|
|
|
|
|
|
|
iscissors->undo_stack = g_list_remove (iscissors->undo_stack,
|
|
|
|
iscissors->curve);
|
2015-03-29 04:52:26 +08:00
|
|
|
|
|
|
|
if (! iscissors->undo_stack)
|
|
|
|
{
|
|
|
|
iscissors->state = NO_ACTION;
|
|
|
|
|
|
|
|
gimp_draw_tool_stop (GIMP_DRAW_TOOL (iscissors));
|
|
|
|
}
|
2015-03-29 04:31:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_iscissors_tool_free_redo (GimpIscissorsTool *iscissors)
|
|
|
|
{
|
|
|
|
g_list_free_full (iscissors->redo_stack,
|
|
|
|
(GDestroyNotify) icurve_free);
|
|
|
|
iscissors->redo_stack = NULL;
|
|
|
|
|
|
|
|
/* update the undo actions / menu items */
|
|
|
|
gimp_image_flush (gimp_display_get_image (GIMP_TOOL (iscissors)->display));
|
|
|
|
}
|
|
|
|
|
2007-01-19 17:32:35 +08:00
|
|
|
static void
|
2015-03-18 05:19:29 +08:00
|
|
|
gimp_iscissors_tool_halt (GimpIscissorsTool *iscissors,
|
|
|
|
GimpDisplay *display)
|
2007-01-19 17:32:35 +08:00
|
|
|
{
|
2015-03-28 04:37:57 +08:00
|
|
|
icurve_clear (iscissors->curve);
|
2007-01-19 17:32:35 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
iscissors->segment1 = NULL;
|
|
|
|
iscissors->segment2 = NULL;
|
|
|
|
iscissors->state = NO_ACTION;
|
|
|
|
|
|
|
|
if (iscissors->undo_stack)
|
2015-03-18 05:19:29 +08:00
|
|
|
{
|
2015-03-29 04:31:03 +08:00
|
|
|
g_list_free_full (iscissors->undo_stack, (GDestroyNotify) icurve_free);
|
|
|
|
iscissors->undo_stack = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iscissors->redo_stack)
|
|
|
|
{
|
|
|
|
g_list_free_full (iscissors->redo_stack, (GDestroyNotify) icurve_free);
|
|
|
|
iscissors->redo_stack = NULL;
|
2015-03-18 05:19:29 +08:00
|
|
|
}
|
|
|
|
|
2017-07-16 00:38:01 +08:00
|
|
|
g_clear_object (&iscissors->gradient_map);
|
|
|
|
g_clear_object (&iscissors->mask);
|
2015-03-18 05:19:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_iscissors_tool_commit (GimpIscissorsTool *iscissors,
|
|
|
|
GimpDisplay *display)
|
|
|
|
{
|
|
|
|
GimpTool *tool = GIMP_TOOL (iscissors);
|
|
|
|
GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
|
|
|
|
GimpImage *image = gimp_display_get_image (display);
|
|
|
|
|
2016-10-31 06:08:13 +08:00
|
|
|
if (! iscissors->curve->closed)
|
|
|
|
{
|
|
|
|
ISegment *first = g_queue_peek_head (iscissors->curve->segments);
|
|
|
|
ISegment *last = g_queue_peek_tail (iscissors->curve->segments);
|
|
|
|
|
2016-11-25 14:44:49 +08:00
|
|
|
if (first && last && first != last)
|
2016-10-31 06:08:13 +08:00
|
|
|
{
|
|
|
|
ISegment *segment;
|
|
|
|
|
|
|
|
segment = icurve_append_segment (iscissors->curve,
|
|
|
|
last->x2,
|
|
|
|
last->y2,
|
|
|
|
first->x1,
|
|
|
|
first->y1);
|
|
|
|
icurve_close (iscissors->curve);
|
|
|
|
calculate_segment (iscissors, segment);
|
|
|
|
|
|
|
|
iscissors_convert (iscissors, display);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-09 23:19:42 +08:00
|
|
|
if (iscissors->curve->closed && iscissors->mask)
|
2015-03-18 05:19:29 +08:00
|
|
|
{
|
|
|
|
gimp_channel_select_channel (gimp_image_get_mask (image),
|
2017-07-05 03:58:11 +08:00
|
|
|
gimp_tool_get_undo_desc (tool),
|
2015-03-18 05:19:29 +08:00
|
|
|
iscissors->mask,
|
|
|
|
0, 0,
|
|
|
|
options->operation,
|
|
|
|
options->feather,
|
|
|
|
options->feather_radius,
|
|
|
|
options->feather_radius);
|
|
|
|
|
|
|
|
gimp_image_flush (image);
|
|
|
|
}
|
2007-01-19 17:32:35 +08:00
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
|
|
|
|
/* XXX need some scan-conversion routines from somewhere. maybe. ? */
|
|
|
|
|
2000-03-03 21:01:49 +08:00
|
|
|
static gint
|
2001-03-12 06:19:06 +08:00
|
|
|
mouse_over_vertex (GimpIscissorsTool *iscissors,
|
2006-04-12 20:49:29 +08:00
|
|
|
gdouble x,
|
|
|
|
gdouble y)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2007-05-23 22:56:21 +08:00
|
|
|
GList *list;
|
2015-03-27 17:45:28 +08:00
|
|
|
gint segments_found = 0;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* traverse through the list, returning non-zero if the current cursor
|
2015-03-27 17:45:28 +08:00
|
|
|
* position is on an existing curve vertex. Set the segment1 and segment2
|
|
|
|
* variables to the two segments containing the vertex in question
|
1999-08-16 11:43:48 +08:00
|
|
|
*/
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
iscissors->segment1 = iscissors->segment2 = NULL;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2015-03-28 04:37:57 +08:00
|
|
|
for (list = g_queue_peek_head_link (iscissors->curve->segments);
|
2007-05-23 22:56:21 +08:00
|
|
|
list;
|
|
|
|
list = g_list_next (list))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
ISegment *segment = list->data;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2001-11-16 05:17:36 +08:00
|
|
|
if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (iscissors),
|
2006-03-29 01:55:52 +08:00
|
|
|
GIMP_TOOL (iscissors)->display,
|
2001-11-12 22:45:58 +08:00
|
|
|
x, y,
|
2001-11-16 05:17:36 +08:00
|
|
|
GIMP_HANDLE_CIRCLE,
|
2015-03-27 17:45:28 +08:00
|
|
|
segment->x1, segment->y1,
|
2010-11-09 05:37:00 +08:00
|
|
|
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
|
|
|
|
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
|
2010-10-19 02:13:09 +08:00
|
|
|
GIMP_HANDLE_ANCHOR_CENTER))
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
iscissors->segment1 = segment;
|
2001-11-12 22:45:58 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
if (segments_found++)
|
|
|
|
return segments_found;
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
2001-11-16 05:17:36 +08:00
|
|
|
else if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (iscissors),
|
2006-03-29 01:55:52 +08:00
|
|
|
GIMP_TOOL (iscissors)->display,
|
2001-11-12 22:45:58 +08:00
|
|
|
x, y,
|
2001-11-16 05:17:36 +08:00
|
|
|
GIMP_HANDLE_CIRCLE,
|
2015-03-27 17:45:28 +08:00
|
|
|
segment->x2, segment->y2,
|
2010-11-09 05:37:00 +08:00
|
|
|
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
|
|
|
|
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
|
2010-10-19 02:13:09 +08:00
|
|
|
GIMP_HANDLE_ANCHOR_CENTER))
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
iscissors->segment2 = segment;
|
2001-11-12 22:45:58 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
if (segments_found++)
|
|
|
|
return segments_found;
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
2000-03-03 21:01:49 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
return segments_found;
|
2000-03-03 21:01:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2006-08-15 04:32:42 +08:00
|
|
|
clicked_on_vertex (GimpIscissorsTool *iscissors,
|
|
|
|
gdouble x,
|
|
|
|
gdouble y)
|
2000-03-03 21:01:49 +08:00
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
gint segments_found = mouse_over_vertex (iscissors, x, y);
|
2000-03-03 21:01:49 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
if (segments_found > 1)
|
2015-03-29 04:31:03 +08:00
|
|
|
{
|
|
|
|
gimp_iscissors_tool_push_undo (iscissors);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
2000-03-03 21:01:49 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
/* if only one segment was found, the segments are unconnected, and
|
1999-08-16 11:43:48 +08:00
|
|
|
* the user only wants to move either the first or last point
|
|
|
|
* disallow this for now.
|
|
|
|
*/
|
2015-03-27 17:45:28 +08:00
|
|
|
if (segments_found == 1)
|
2000-03-03 21:01:49 +08:00
|
|
|
return FALSE;
|
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
return clicked_on_segment (iscissors, x, y);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2007-05-23 22:56:21 +08:00
|
|
|
static GList *
|
2015-03-27 17:45:28 +08:00
|
|
|
mouse_over_segment (GimpIscissorsTool *iscissors,
|
|
|
|
gdouble x,
|
|
|
|
gdouble y)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2007-05-23 22:56:21 +08:00
|
|
|
GList *list;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2000-03-03 21:01:49 +08:00
|
|
|
/* traverse through the list, returning the curve segment's list element
|
2003-08-22 09:42:57 +08:00
|
|
|
* if the current cursor position is on a curve...
|
1999-08-16 11:43:48 +08:00
|
|
|
*/
|
2015-03-28 04:37:57 +08:00
|
|
|
for (list = g_queue_peek_head_link (iscissors->curve->segments);
|
2007-05-23 22:56:21 +08:00
|
|
|
list;
|
|
|
|
list = g_list_next (list))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
ISegment *segment = list->data;
|
2006-08-15 04:32:42 +08:00
|
|
|
gpointer *pt;
|
|
|
|
gint len;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
if (! segment->points)
|
|
|
|
continue;
|
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
pt = segment->points->pdata;
|
|
|
|
len = segment->points->len;
|
2006-08-15 04:32:42 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
while (len--)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2006-08-15 04:32:42 +08:00
|
|
|
guint32 coords = GPOINTER_TO_INT (*pt);
|
|
|
|
gint tx, ty;
|
|
|
|
|
2006-04-12 20:49:29 +08:00
|
|
|
pt++;
|
|
|
|
tx = coords & 0x0000ffff;
|
|
|
|
ty = coords >> 16;
|
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
/* Is the specified point close enough to the segment? */
|
2010-09-26 00:24:38 +08:00
|
|
|
if (gimp_draw_tool_calc_distance_square (GIMP_DRAW_TOOL (iscissors),
|
|
|
|
GIMP_TOOL (iscissors)->display,
|
|
|
|
tx, ty,
|
2010-11-09 05:37:00 +08:00
|
|
|
x, y) < SQR (GIMP_TOOL_HANDLE_SIZE_CIRCLE / 2))
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
}
|
2000-03-03 21:01:49 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2000-03-03 21:01:49 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2015-03-27 17:45:28 +08:00
|
|
|
clicked_on_segment (GimpIscissorsTool *iscissors,
|
|
|
|
gdouble x,
|
|
|
|
gdouble y)
|
2000-03-03 21:01:49 +08:00
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
GList *list = mouse_over_segment (iscissors, x, y);
|
2002-03-03 18:38:37 +08:00
|
|
|
|
2000-03-03 21:01:49 +08:00
|
|
|
/* traverse through the list, getting back the curve segment's list
|
2015-03-27 17:45:28 +08:00
|
|
|
* element if the current cursor position is on a segment...
|
|
|
|
* If this occurs, replace the segment with two new segments,
|
2000-03-03 21:01:49 +08:00
|
|
|
* separated by a new vertex.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (list)
|
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
ISegment *segment = list->data;
|
|
|
|
ISegment *new_segment;
|
2000-03-03 21:01:49 +08:00
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
gimp_iscissors_tool_push_undo (iscissors);
|
|
|
|
|
2016-10-09 23:19:42 +08:00
|
|
|
new_segment = icurve_insert_segment (iscissors->curve,
|
|
|
|
list,
|
|
|
|
iscissors->x,
|
|
|
|
iscissors->y,
|
|
|
|
segment->x2,
|
|
|
|
segment->y2);
|
2000-03-03 21:01:49 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
iscissors->segment1 = new_segment;
|
|
|
|
iscissors->segment2 = segment;
|
2000-03-03 21:01:49 +08:00
|
|
|
|
|
|
|
return TRUE;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
2000-03-03 04:09:12 +08:00
|
|
|
return FALSE;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static void
|
2015-03-27 17:45:28 +08:00
|
|
|
calculate_segment (GimpIscissorsTool *iscissors,
|
|
|
|
ISegment *segment)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2016-10-07 07:18:16 +08:00
|
|
|
GimpDisplay *display = GIMP_TOOL (iscissors)->display;
|
|
|
|
GimpPickable *pickable = GIMP_PICKABLE (gimp_display_get_image (display));
|
|
|
|
gint width;
|
|
|
|
gint height;
|
|
|
|
gint xs, ys, xe, ye;
|
|
|
|
gint x1, y1, x2, y2;
|
|
|
|
gint ewidth, eheight;
|
|
|
|
|
|
|
|
/* Initialise the gradient map buffer for this pickable if we don't
|
|
|
|
* already have one.
|
|
|
|
*/
|
|
|
|
if (! iscissors->gradient_map)
|
|
|
|
iscissors->gradient_map = gradient_map_new (pickable);
|
|
|
|
|
|
|
|
width = gegl_buffer_get_width (iscissors->gradient_map);
|
|
|
|
height = gegl_buffer_get_height (iscissors->gradient_map);
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* Calculate the lowest cost path from one vertex to the next as specified
|
2015-03-27 17:45:28 +08:00
|
|
|
* by the parameter "segment".
|
1999-08-16 11:43:48 +08:00
|
|
|
* Here are the steps:
|
|
|
|
* 1) Calculate the appropriate working area for this operation
|
|
|
|
* 2) Allocate a temp buf for the dynamic programming array
|
|
|
|
* 3) Run the dynamic programming algorithm to find the optimal path
|
2015-03-27 17:45:28 +08:00
|
|
|
* 4) Translate the optimal path into pixels in the isegment data
|
1999-08-16 11:43:48 +08:00
|
|
|
* structure.
|
|
|
|
*/
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Get the bounding box */
|
2016-10-07 07:18:16 +08:00
|
|
|
xs = CLAMP (segment->x1, 0, width - 1);
|
|
|
|
ys = CLAMP (segment->y1, 0, height - 1);
|
|
|
|
xe = CLAMP (segment->x2, 0, width - 1);
|
|
|
|
ye = CLAMP (segment->y2, 0, height - 1);
|
2000-01-26 07:06:12 +08:00
|
|
|
x1 = MIN (xs, xe);
|
|
|
|
y1 = MIN (ys, ye);
|
|
|
|
x2 = MAX (xs, xe) + 1; /* +1 because if xe = 199 & xs = 0, x2 - x1, width = 200 */
|
|
|
|
y2 = MAX (ys, ye) + 1;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2003-08-22 09:42:57 +08:00
|
|
|
/* expand the boundaries past the ending points by
|
1999-08-16 11:43:48 +08:00
|
|
|
* some percentage of width and height. This serves the following purpose:
|
|
|
|
* It gives the algorithm more area to search so better solutions
|
|
|
|
* are found. This is particularly helpful in finding "bumps" which
|
|
|
|
* fall outside the bounding box represented by the start and end
|
2015-03-27 17:45:28 +08:00
|
|
|
* coordinates of the "segment".
|
1999-08-16 11:43:48 +08:00
|
|
|
*/
|
2001-03-12 06:19:06 +08:00
|
|
|
ewidth = (x2 - x1) * EXTEND_BY + FIXED;
|
1999-08-16 11:43:48 +08:00
|
|
|
eheight = (y2 - y1) * EXTEND_BY + FIXED;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
if (xe >= xs)
|
2016-10-07 07:18:16 +08:00
|
|
|
x2 += CLAMP (ewidth, 0, width - x2);
|
1999-08-16 11:43:48 +08:00
|
|
|
else
|
2000-01-26 07:06:12 +08:00
|
|
|
x1 -= CLAMP (ewidth, 0, x1);
|
2007-12-26 00:21:40 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
if (ye >= ys)
|
2016-10-07 07:18:16 +08:00
|
|
|
y2 += CLAMP (eheight, 0, height - y2);
|
1999-08-16 11:43:48 +08:00
|
|
|
else
|
2000-01-26 07:06:12 +08:00
|
|
|
y1 -= CLAMP (eheight, 0, y1);
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* blow away any previous points list we might have */
|
2015-03-27 17:45:28 +08:00
|
|
|
if (segment->points)
|
2000-03-03 04:09:12 +08:00
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
g_ptr_array_free (segment->points, TRUE);
|
|
|
|
segment->points = NULL;
|
2000-03-03 04:09:12 +08:00
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
if ((x2 - x1) && (y2 - y1))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2016-10-07 07:18:16 +08:00
|
|
|
/* If the bounding box has width and height... */
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2016-10-07 07:58:37 +08:00
|
|
|
GimpTempBuf *dp_buf; /* dynamic programming buffer */
|
|
|
|
gint dp_width = (x2 - x1);
|
|
|
|
gint dp_height = (y2 - y1);
|
2012-04-08 05:58:38 +08:00
|
|
|
|
2016-10-07 07:58:37 +08:00
|
|
|
dp_buf = gimp_temp_buf_new (dp_width, dp_height,
|
|
|
|
babl_format ("Y u32"));
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* find the optimal path of pixels from (x1, y1) to (x2, y2) */
|
2016-10-07 07:58:37 +08:00
|
|
|
find_optimal_path (iscissors->gradient_map, dp_buf,
|
2006-04-12 20:49:29 +08:00
|
|
|
x1, y1, x2, y2, xs, ys);
|
2003-08-22 09:42:57 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* get a list of the pixels in the optimal path */
|
2016-10-07 07:58:37 +08:00
|
|
|
segment->points = plot_pixels (dp_buf, x1, y1, xs, ys, xe, ye);
|
|
|
|
|
|
|
|
gimp_temp_buf_unref (dp_buf);
|
1999-08-16 11:43:48 +08:00
|
|
|
}
|
|
|
|
else if ((x2 - x1) == 0)
|
|
|
|
{
|
2016-10-07 07:18:16 +08:00
|
|
|
/* If the bounding box has no width */
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* plot a vertical line */
|
2016-10-07 07:18:16 +08:00
|
|
|
gint y = ys;
|
|
|
|
gint dir = (ys > ye) ? -1 : 1;
|
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
segment->points = g_ptr_array_new ();
|
1999-08-16 11:43:48 +08:00
|
|
|
while (y != ye)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
g_ptr_array_add (segment->points, GINT_TO_POINTER ((y << 16) + xs));
|
2006-04-12 20:49:29 +08:00
|
|
|
y += dir;
|
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
}
|
|
|
|
else if ((y2 - y1) == 0)
|
|
|
|
{
|
2016-10-07 07:18:16 +08:00
|
|
|
/* If the bounding box has no height */
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* plot a horizontal line */
|
2016-10-07 07:18:16 +08:00
|
|
|
gint x = xs;
|
|
|
|
gint dir = (xs > xe) ? -1 : 1;
|
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
segment->points = g_ptr_array_new ();
|
1999-08-16 11:43:48 +08:00
|
|
|
while (x != xe)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
g_ptr_array_add (segment->points, GINT_TO_POINTER ((ys << 16) + x));
|
2006-04-12 20:49:29 +08:00
|
|
|
x += dir;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* badly need to get a replacement - this is _way_ too expensive */
|
2000-03-03 04:09:12 +08:00
|
|
|
static gboolean
|
2018-05-14 07:08:13 +08:00
|
|
|
gradient_map_value (GeglSampler *map_sampler,
|
|
|
|
const GeglRectangle *map_extent,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
guint8 *grad,
|
|
|
|
guint8 *dir)
|
1999-08-16 11:43:48 +08:00
|
|
|
{
|
2018-05-14 07:08:13 +08:00
|
|
|
if (x >= map_extent->x &&
|
|
|
|
y >= map_extent->y &&
|
|
|
|
x < map_extent->width &&
|
|
|
|
y < map_extent->height)
|
1999-08-16 11:43:48 +08:00
|
|
|
{
|
2015-03-18 05:19:29 +08:00
|
|
|
guint8 sample[2];
|
2007-09-13 02:29:11 +08:00
|
|
|
|
2018-05-14 07:08:13 +08:00
|
|
|
gegl_sampler_get (map_sampler, x, y, NULL, sample, GEGL_ABYSS_NONE);
|
2007-09-13 02:29:11 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
*grad = sample[0];
|
|
|
|
*dir = sample[1];
|
2007-09-13 02:29:11 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
return TRUE;
|
1999-08-16 11:43:48 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
return FALSE;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
2000-03-03 04:09:12 +08:00
|
|
|
static gint
|
2018-05-14 07:08:13 +08:00
|
|
|
calculate_link (GeglSampler *map_sampler,
|
|
|
|
const GeglRectangle *map_extent,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
guint32 pixel,
|
|
|
|
gint link)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2001-03-12 06:19:06 +08:00
|
|
|
gint value = 0;
|
1999-08-16 11:43:48 +08:00
|
|
|
guint8 grad1, dir1, grad2, dir2;
|
|
|
|
|
2018-05-14 07:08:13 +08:00
|
|
|
if (! gradient_map_value (map_sampler, map_extent, x, y, &grad1, &dir1))
|
2000-03-03 04:09:12 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
grad1 = 0;
|
|
|
|
dir1 = 255;
|
2000-03-03 04:09:12 +08:00
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* Convert the gradient into a cost: large gradients are good, and
|
|
|
|
* so have low cost. */
|
|
|
|
grad1 = 255 - grad1;
|
|
|
|
|
|
|
|
/* calculate the contribution of the gradient magnitude */
|
|
|
|
if (link > 1)
|
2001-11-09 03:14:51 +08:00
|
|
|
value += diagonal_weight[grad1] * OMEGA_G;
|
1997-11-25 06:05:25 +08:00
|
|
|
else
|
1999-08-16 11:43:48 +08:00
|
|
|
value += grad1 * OMEGA_G;
|
|
|
|
|
|
|
|
/* calculate the contribution of the gradient direction */
|
|
|
|
x += (gint8)(pixel & 0xff);
|
|
|
|
y += (gint8)((pixel & 0xff00) >> 8);
|
2006-11-07 19:09:51 +08:00
|
|
|
|
2018-05-14 07:08:13 +08:00
|
|
|
if (! gradient_map_value (map_sampler, map_extent, x, y, &grad2, &dir2))
|
2000-03-03 04:09:12 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
grad2 = 0;
|
|
|
|
dir2 = 255;
|
2003-08-22 09:42:57 +08:00
|
|
|
}
|
2006-11-07 19:09:51 +08:00
|
|
|
|
|
|
|
value +=
|
|
|
|
(direction_value[dir1][link] + direction_value[dir2][link]) * OMEGA_D;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
static GPtrArray *
|
2016-10-07 07:58:37 +08:00
|
|
|
plot_pixels (GimpTempBuf *dp_buf,
|
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint xs,
|
|
|
|
gint ys,
|
|
|
|
gint xe,
|
|
|
|
gint ye)
|
1999-08-16 11:43:48 +08:00
|
|
|
{
|
2000-03-03 04:09:12 +08:00
|
|
|
gint x, y;
|
|
|
|
guint32 coords;
|
|
|
|
gint link;
|
2012-04-23 15:40:56 +08:00
|
|
|
gint width = gimp_temp_buf_get_width (dp_buf);
|
2000-03-03 04:09:12 +08:00
|
|
|
guint *data;
|
1999-08-16 11:43:48 +08:00
|
|
|
GPtrArray *list;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Start the data pointer at the correct location */
|
2012-04-09 05:56:52 +08:00
|
|
|
data = (guint *) gimp_temp_buf_get_data (dp_buf) + (ye - y1) * width + (xe - x1);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
x = xe;
|
|
|
|
y = ye;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
list = g_ptr_array_new ();
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2006-11-07 19:09:51 +08:00
|
|
|
while (TRUE)
|
1999-08-16 11:43:48 +08:00
|
|
|
{
|
|
|
|
coords = (y << 16) + x;
|
|
|
|
g_ptr_array_add (list, GINT_TO_POINTER (coords));
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
link = PIXEL_DIR (*data);
|
|
|
|
if (link == SEED_POINT)
|
2006-04-12 20:49:29 +08:00
|
|
|
return list;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-11-09 03:14:51 +08:00
|
|
|
x += move[link][0];
|
|
|
|
y += move[link][1];
|
|
|
|
data += move[link][1] * width + move[link][0];
|
1999-08-16 11:43:48 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* won't get here */
|
|
|
|
return NULL;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
|
2015-03-21 04:18:23 +08:00
|
|
|
#define PACK(x, y) ((((y) & 0xff) << 8) | ((x) & 0xff))
|
1999-08-16 11:43:48 +08:00
|
|
|
#define OFFSET(pixel) ((gint8)((pixel) & 0xff) + \
|
2015-03-21 04:18:23 +08:00
|
|
|
((gint8)(((pixel) & 0xff00) >> 8)) * \
|
|
|
|
gimp_temp_buf_get_width (dp_buf))
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
static void
|
2015-03-18 05:19:29 +08:00
|
|
|
find_optimal_path (GeglBuffer *gradient_map,
|
2012-04-09 02:25:49 +08:00
|
|
|
GimpTempBuf *dp_buf,
|
2006-04-12 20:49:29 +08:00
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2,
|
|
|
|
gint xs,
|
|
|
|
gint ys)
|
1999-08-16 11:43:48 +08:00
|
|
|
{
|
2018-05-14 07:08:13 +08:00
|
|
|
GeglSampler *map_sampler;
|
|
|
|
const GeglRectangle *map_extent;
|
|
|
|
gint i, j, k;
|
|
|
|
gint x, y;
|
|
|
|
gint link;
|
|
|
|
gint linkdir;
|
|
|
|
gint dirx, diry;
|
|
|
|
gint min_cost;
|
|
|
|
gint new_cost;
|
|
|
|
gint offset;
|
|
|
|
gint cum_cost[8];
|
|
|
|
gint link_cost[8];
|
|
|
|
gint pixel_cost[8];
|
|
|
|
guint32 pixel[8];
|
|
|
|
guint32 *data;
|
|
|
|
guint32 *d;
|
|
|
|
gint dp_buf_width = gimp_temp_buf_get_width (dp_buf);
|
|
|
|
gint dp_buf_height = gimp_temp_buf_get_height (dp_buf);
|
|
|
|
|
|
|
|
/* initialize the gradient map sampler and extent */
|
|
|
|
map_sampler = gegl_buffer_sampler_new (gradient_map,
|
|
|
|
gegl_buffer_get_format (gradient_map),
|
|
|
|
GEGL_SAMPLER_NEAREST);
|
|
|
|
map_extent = gegl_buffer_get_extent (gradient_map);
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* initialize the dynamic programming buffer */
|
2012-04-09 05:56:52 +08:00
|
|
|
data = (guint32 *) gimp_temp_buf_data_clear (dp_buf);
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* what directions are we filling the array in according to? */
|
|
|
|
dirx = (xs - x1 == 0) ? 1 : -1;
|
|
|
|
diry = (ys - y1 == 0) ? 1 : -1;
|
|
|
|
linkdir = (dirx * diry);
|
|
|
|
|
|
|
|
y = ys;
|
|
|
|
|
2012-04-23 15:40:56 +08:00
|
|
|
for (i = 0; i < dp_buf_height; i++)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
x = xs;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2012-04-23 15:40:56 +08:00
|
|
|
d = data + (y-y1) * dp_buf_width + (x-x1);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2012-04-23 15:40:56 +08:00
|
|
|
for (j = 0; j < dp_buf_width; j++)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
|
|
|
min_cost = G_MAXINT;
|
|
|
|
|
2013-01-27 23:52:38 +08:00
|
|
|
/* pixel[] array encodes how to get to a neighbour, if possible.
|
2006-04-12 20:49:29 +08:00
|
|
|
* 0 means no connection (eg edge).
|
|
|
|
* Rest packed as bottom two bytes: y offset then x offset.
|
2015-03-21 04:18:23 +08:00
|
|
|
* Initially, we assume we can't get anywhere.
|
|
|
|
*/
|
2006-04-12 20:49:29 +08:00
|
|
|
for (k = 0; k < 8; k++)
|
|
|
|
pixel[k] = 0;
|
|
|
|
|
|
|
|
/* Find the valid neighboring pixels */
|
|
|
|
/* the previous pixel */
|
|
|
|
if (j)
|
|
|
|
pixel[((dirx == 1) ? 4 : 0)] = PACK (-dirx, 0);
|
|
|
|
|
|
|
|
/* the previous row of pixels */
|
|
|
|
if (i)
|
|
|
|
{
|
|
|
|
pixel[((diry == 1) ? 5 : 1)] = PACK (0, -diry);
|
|
|
|
|
|
|
|
link = (linkdir == 1) ? 3 : 2;
|
|
|
|
if (j)
|
2015-03-21 04:18:23 +08:00
|
|
|
pixel[((diry == 1) ? (link + 4) : link)] = PACK (-dirx, -diry);
|
2006-04-12 20:49:29 +08:00
|
|
|
|
|
|
|
link = (linkdir == 1) ? 2 : 3;
|
2012-04-23 15:40:56 +08:00
|
|
|
if (j != dp_buf_width - 1)
|
2006-04-12 20:49:29 +08:00
|
|
|
pixel[((diry == 1) ? (link + 4) : link)] = PACK (dirx, -diry);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find the minimum cost of going through each neighbor to reach the
|
|
|
|
* seed point...
|
|
|
|
*/
|
|
|
|
link = -1;
|
|
|
|
for (k = 0; k < 8; k ++)
|
|
|
|
if (pixel[k])
|
|
|
|
{
|
2018-05-14 07:08:13 +08:00
|
|
|
link_cost[k] = calculate_link (map_sampler, map_extent,
|
2001-11-09 03:14:51 +08:00
|
|
|
xs + j*dirx, ys + i*diry,
|
|
|
|
pixel [k],
|
|
|
|
((k > 3) ? k - 4 : k));
|
2006-04-12 20:49:29 +08:00
|
|
|
offset = OFFSET (pixel [k]);
|
|
|
|
pixel_cost[k] = PIXEL_COST (d[offset]);
|
|
|
|
cum_cost[k] = pixel_cost[k] + link_cost[k];
|
|
|
|
if (cum_cost[k] < min_cost)
|
|
|
|
{
|
|
|
|
min_cost = cum_cost[k];
|
|
|
|
link = k;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If anything can be done... */
|
|
|
|
if (link >= 0)
|
|
|
|
{
|
2015-03-21 04:18:23 +08:00
|
|
|
/* set the cumulative cost of this pixel and the new direction
|
|
|
|
*/
|
2006-04-12 20:49:29 +08:00
|
|
|
*d = (cum_cost[link] << 8) + link;
|
|
|
|
|
|
|
|
/* possibly change the links from the other pixels to this pixel...
|
|
|
|
* these changes occur if a neighboring pixel will receive a lower
|
|
|
|
* cumulative cost by going through this pixel.
|
|
|
|
*/
|
|
|
|
for (k = 0; k < 8; k ++)
|
|
|
|
if (pixel[k] && k != link)
|
|
|
|
{
|
|
|
|
/* if the cumulative cost at the neighbor is greater than
|
|
|
|
* the cost through the link to the current pixel, change the
|
|
|
|
* neighbor's link to point to the current pixel.
|
|
|
|
*/
|
|
|
|
new_cost = link_cost[k] + cum_cost[link];
|
|
|
|
if (pixel_cost[k] > new_cost)
|
|
|
|
{
|
2015-03-21 04:18:23 +08:00
|
|
|
/* reverse the link direction /--------------------\ */
|
2006-04-12 20:49:29 +08:00
|
|
|
offset = OFFSET (pixel[k]);
|
|
|
|
d[offset] = (new_cost << 8) + ((k > 3) ? k - 4 : k + 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Set the seed point */
|
|
|
|
else if (!i && !j)
|
2015-03-21 04:18:23 +08:00
|
|
|
{
|
|
|
|
*d = SEED_POINT;
|
|
|
|
}
|
2006-04-12 20:49:29 +08:00
|
|
|
|
|
|
|
/* increment the data pointer and the x counter */
|
|
|
|
d += dirx;
|
|
|
|
x += dirx;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* increment the y counter */
|
|
|
|
y += diry;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
2018-05-14 07:08:13 +08:00
|
|
|
|
|
|
|
g_object_unref (map_sampler);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
static GeglBuffer *
|
2016-10-07 07:18:16 +08:00
|
|
|
gradient_map_new (GimpPickable *pickable)
|
2000-03-03 04:09:12 +08:00
|
|
|
{
|
2015-03-18 05:19:29 +08:00
|
|
|
GeglBuffer *buffer;
|
|
|
|
GeglTileHandler *handler;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2016-10-07 07:18:16 +08:00
|
|
|
buffer = gimp_pickable_get_buffer (pickable);
|
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
|
2016-10-07 07:18:16 +08:00
|
|
|
gegl_buffer_get_width (buffer),
|
|
|
|
gegl_buffer_get_height (buffer)),
|
2015-03-18 05:19:29 +08:00
|
|
|
babl_format_n (babl_type ("u8"), 2));
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2016-10-07 07:18:16 +08:00
|
|
|
handler = gimp_tile_handler_iscissors_new (pickable);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
gimp_tile_handler_validate_assign (GIMP_TILE_HANDLER_VALIDATE (handler),
|
|
|
|
buffer);
|
2007-06-25 14:57:31 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
gimp_tile_handler_validate_invalidate (GIMP_TILE_HANDLER_VALIDATE (handler),
|
2017-08-05 21:43:41 +08:00
|
|
|
GEGL_RECTANGLE (0, 0,
|
|
|
|
gegl_buffer_get_width (buffer),
|
|
|
|
gegl_buffer_get_height (buffer)));
|
2007-06-25 14:57:31 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
g_object_unref (handler);
|
2006-04-12 20:49:29 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
return buffer;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2001-03-12 06:19:06 +08:00
|
|
|
find_max_gradient (GimpIscissorsTool *iscissors,
|
2016-10-07 07:18:16 +08:00
|
|
|
GimpPickable *pickable,
|
2006-04-12 20:49:29 +08:00
|
|
|
gint *x,
|
|
|
|
gint *y)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2015-03-18 05:19:29 +08:00
|
|
|
GeglBufferIterator *iter;
|
|
|
|
GeglRectangle *roi;
|
2016-10-07 07:18:16 +08:00
|
|
|
gint width;
|
|
|
|
gint height;
|
2015-03-18 05:19:29 +08:00
|
|
|
gint radius;
|
|
|
|
gint cx, cy;
|
|
|
|
gint x1, y1, x2, y2;
|
|
|
|
gfloat max_gradient;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2016-10-07 07:18:16 +08:00
|
|
|
/* Initialise the gradient map buffer for this pickable if we don't
|
2015-03-29 04:31:03 +08:00
|
|
|
* already have one.
|
|
|
|
*/
|
2007-06-25 15:04:42 +08:00
|
|
|
if (! iscissors->gradient_map)
|
2016-10-07 07:18:16 +08:00
|
|
|
iscissors->gradient_map = gradient_map_new (pickable);
|
|
|
|
|
|
|
|
width = gegl_buffer_get_width (iscissors->gradient_map);
|
|
|
|
height = gegl_buffer_get_height (iscissors->gradient_map);
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
radius = GRADIENT_SEARCH >> 1;
|
|
|
|
|
|
|
|
/* calculate the extent of the search */
|
2016-10-07 07:18:16 +08:00
|
|
|
cx = CLAMP (*x, 0, width);
|
|
|
|
cy = CLAMP (*y, 0, height);
|
|
|
|
x1 = CLAMP (cx - radius, 0, width);
|
|
|
|
y1 = CLAMP (cy - radius, 0, height);
|
|
|
|
x2 = CLAMP (cx + radius, 0, width);
|
|
|
|
y2 = CLAMP (cy + radius, 0, height);
|
1999-08-16 11:43:48 +08:00
|
|
|
/* calculate the factor to multiply the distance from the cursor by */
|
|
|
|
|
|
|
|
max_gradient = 0;
|
|
|
|
*x = cx;
|
|
|
|
*y = cy;
|
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
iter = gegl_buffer_iterator_new (iscissors->gradient_map,
|
|
|
|
GEGL_RECTANGLE (x1, y1, x2 - x1, y2 - y1),
|
|
|
|
0, NULL,
|
2018-09-11 07:33:36 +08:00
|
|
|
GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
|
|
|
|
roi = &iter->items[0].roi;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
while (gegl_buffer_iterator_next (iter))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2018-09-11 07:33:36 +08:00
|
|
|
guint8 *data = iter->items[0].data;
|
2015-03-18 05:19:29 +08:00
|
|
|
gint endx = roi->x + roi->width;
|
|
|
|
gint endy = roi->y + roi->height;
|
|
|
|
gint i, j;
|
2007-06-25 15:04:42 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
for (i = roi->y; i < endy; i++)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2015-03-18 05:19:29 +08:00
|
|
|
const guint8 *gradient = data + 2 * roi->width * (i - roi->y);
|
2007-06-25 15:04:42 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
for (j = roi->x; j < endx; j++)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2007-06-25 15:04:42 +08:00
|
|
|
gfloat g = *gradient;
|
|
|
|
|
2006-04-12 20:49:29 +08:00
|
|
|
gradient += COST_WIDTH;
|
2007-06-25 15:04:42 +08:00
|
|
|
|
2015-03-18 05:19:29 +08:00
|
|
|
g *= distance_weights [(i - y1) * GRADIENT_SEARCH + (j - x1)];
|
2007-06-25 15:04:42 +08:00
|
|
|
|
2006-04-12 20:49:29 +08:00
|
|
|
if (g > max_gradient)
|
|
|
|
{
|
|
|
|
max_gradient = g;
|
2007-06-25 15:04:42 +08:00
|
|
|
|
2006-04-12 20:49:29 +08:00
|
|
|
*x = j;
|
|
|
|
*y = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
2015-03-21 04:18:23 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
static ISegment *
|
|
|
|
isegment_new (gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2)
|
2015-03-21 04:18:23 +08:00
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
ISegment *segment = g_slice_new0 (ISegment);
|
2015-03-21 04:18:23 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
segment->x1 = x1;
|
|
|
|
segment->y1 = y1;
|
|
|
|
segment->x2 = x2;
|
|
|
|
segment->y2 = y2;
|
2015-03-21 04:18:23 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
return segment;
|
2015-03-21 04:18:23 +08:00
|
|
|
}
|
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
static ISegment *
|
|
|
|
isegment_copy (ISegment *segment)
|
|
|
|
{
|
|
|
|
ISegment *copy = isegment_new (segment->x1,
|
|
|
|
segment->y1,
|
|
|
|
segment->x2,
|
|
|
|
segment->y2);
|
|
|
|
|
|
|
|
if (segment->points)
|
|
|
|
{
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
copy->points = g_ptr_array_sized_new (segment->points->len);
|
|
|
|
|
|
|
|
for (i = 0; i < segment->points->len; i++)
|
|
|
|
{
|
|
|
|
gpointer value = g_ptr_array_index (segment->points, i);
|
|
|
|
|
|
|
|
g_ptr_array_add (copy->points, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
2015-03-21 04:18:23 +08:00
|
|
|
static void
|
2015-03-27 17:45:28 +08:00
|
|
|
isegment_free (ISegment *segment)
|
2015-03-21 04:18:23 +08:00
|
|
|
{
|
2015-03-27 17:45:28 +08:00
|
|
|
if (segment->points)
|
|
|
|
g_ptr_array_free (segment->points, TRUE);
|
2015-03-21 04:18:23 +08:00
|
|
|
|
2015-03-27 17:45:28 +08:00
|
|
|
g_slice_free (ISegment, segment);
|
2015-03-21 04:18:23 +08:00
|
|
|
}
|
2015-03-28 04:37:57 +08:00
|
|
|
|
|
|
|
static ICurve *
|
|
|
|
icurve_new (void)
|
|
|
|
{
|
|
|
|
ICurve *curve = g_slice_new0 (ICurve);
|
|
|
|
|
|
|
|
curve->segments = g_queue_new ();
|
|
|
|
curve->first_point = TRUE;
|
|
|
|
|
|
|
|
return curve;
|
|
|
|
}
|
|
|
|
|
2015-03-29 04:31:03 +08:00
|
|
|
static ICurve *
|
|
|
|
icurve_copy (ICurve *curve)
|
|
|
|
{
|
|
|
|
ICurve *copy = icurve_new ();
|
|
|
|
GList *link;
|
|
|
|
|
|
|
|
for (link = g_queue_peek_head_link (curve->segments);
|
|
|
|
link;
|
|
|
|
link = g_list_next (link))
|
|
|
|
{
|
|
|
|
g_queue_push_tail (copy->segments, isegment_copy (link->data));
|
|
|
|
}
|
|
|
|
|
|
|
|
copy->first_point = curve->first_point;
|
2016-10-09 23:19:42 +08:00
|
|
|
copy->closed = curve->closed;
|
2015-03-29 04:31:03 +08:00
|
|
|
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
2015-03-28 04:37:57 +08:00
|
|
|
static void
|
|
|
|
icurve_clear (ICurve *curve)
|
|
|
|
{
|
|
|
|
while (! g_queue_is_empty (curve->segments))
|
|
|
|
isegment_free (g_queue_pop_head (curve->segments));
|
|
|
|
|
|
|
|
curve->first_point = TRUE;
|
2016-10-09 23:19:42 +08:00
|
|
|
curve->closed = FALSE;
|
2015-03-28 04:37:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
icurve_free (ICurve *curve)
|
|
|
|
{
|
|
|
|
g_queue_free_full (curve->segments, (GDestroyNotify) isegment_free);
|
|
|
|
|
|
|
|
g_slice_free (ICurve, curve);
|
|
|
|
}
|
2016-10-09 23:19:42 +08:00
|
|
|
|
|
|
|
static ISegment *
|
|
|
|
icurve_append_segment (ICurve *curve,
|
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2)
|
|
|
|
{
|
|
|
|
ISegment *segment = isegment_new (x1, y1, x2, y2);
|
|
|
|
|
|
|
|
g_queue_push_tail (curve->segments, segment);
|
|
|
|
|
|
|
|
return segment;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ISegment *
|
|
|
|
icurve_insert_segment (ICurve *curve,
|
|
|
|
GList *sibling,
|
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2)
|
|
|
|
{
|
|
|
|
ISegment *segment = sibling->data;
|
|
|
|
ISegment *new_segment;
|
|
|
|
|
|
|
|
new_segment = isegment_new (x1, y1, x2, y2);
|
|
|
|
|
|
|
|
segment->x2 = x1;
|
|
|
|
segment->y2 = y1;
|
|
|
|
|
|
|
|
g_queue_insert_after (curve->segments, sibling, new_segment);
|
|
|
|
|
|
|
|
return new_segment;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
icurve_delete_segment (ICurve *curve,
|
|
|
|
ISegment *segment)
|
|
|
|
{
|
|
|
|
GList *link = g_queue_find (curve->segments, segment);
|
|
|
|
ISegment *next_segment = NULL;
|
|
|
|
|
|
|
|
if (link->next)
|
|
|
|
next_segment = link->next->data;
|
|
|
|
else if (curve->closed)
|
|
|
|
next_segment = g_queue_peek_head (curve->segments);
|
|
|
|
|
|
|
|
if (next_segment)
|
|
|
|
{
|
|
|
|
next_segment->x1 = segment->x1;
|
|
|
|
next_segment->y1 = segment->y1;
|
|
|
|
}
|
2016-11-08 20:45:12 +08:00
|
|
|
|
|
|
|
g_queue_remove (curve->segments, segment);
|
|
|
|
isegment_free (segment);
|
2016-10-09 23:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
icurve_close (ICurve *curve)
|
|
|
|
{
|
|
|
|
ISegment *first = g_queue_peek_head (curve->segments);
|
|
|
|
ISegment *last = g_queue_peek_tail (curve->segments);
|
|
|
|
|
|
|
|
last->x2 = first->x1;
|
|
|
|
last->y2 = first->y1;
|
|
|
|
|
|
|
|
curve->closed = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GimpScanConvert *
|
|
|
|
icurve_create_scan_convert (ICurve *curve)
|
|
|
|
{
|
|
|
|
GimpScanConvert *sc;
|
|
|
|
GList *list;
|
|
|
|
GimpVector2 *points;
|
|
|
|
guint n_total_points = 0;
|
|
|
|
|
|
|
|
sc = gimp_scan_convert_new ();
|
|
|
|
|
|
|
|
for (list = g_queue_peek_tail_link (curve->segments);
|
|
|
|
list;
|
|
|
|
list = g_list_previous (list))
|
|
|
|
{
|
|
|
|
ISegment *segment = list->data;
|
|
|
|
|
|
|
|
n_total_points += segment->points->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
points = g_new (GimpVector2, n_total_points);
|
|
|
|
n_total_points = 0;
|
|
|
|
|
|
|
|
/* go over the segments in reverse order, adding the points we have */
|
|
|
|
for (list = g_queue_peek_tail_link (curve->segments);
|
|
|
|
list;
|
|
|
|
list = g_list_previous (list))
|
|
|
|
{
|
|
|
|
ISegment *segment = list->data;
|
|
|
|
guint n_points;
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
n_points = segment->points->len;
|
|
|
|
|
|
|
|
for (i = 0; i < n_points; i++)
|
|
|
|
{
|
|
|
|
guint32 packed = GPOINTER_TO_INT (g_ptr_array_index (segment->points,
|
|
|
|
i));
|
|
|
|
|
|
|
|
points[n_total_points + i].x = packed & 0x0000ffff;
|
|
|
|
points[n_total_points + i].y = packed >> 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
n_total_points += n_points;
|
|
|
|
}
|
|
|
|
|
|
|
|
gimp_scan_convert_add_polyline (sc, n_total_points, points, TRUE);
|
|
|
|
g_free (points);
|
|
|
|
|
|
|
|
return sc;
|
|
|
|
}
|