1997-11-25 06:05:25 +08:00
|
|
|
/* The GIMP -- an image manipulation program
|
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
1998-04-13 13:44:11 +08:00
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
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. */
|
|
|
|
|
|
|
|
/* The history of this implementation is lonog and varied. It was
|
|
|
|
* orignally done by Spencer and Peter, and worked fine in the 0.54
|
|
|
|
* (motif only) release of the gimp. Later revisions (0.99.something
|
|
|
|
* 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
|
|
|
|
* by Austin Donnelly. */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
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"
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
#include <stdio.h>
|
1997-11-25 06:05:25 +08:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
1999-08-05 07:22:29 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
#include "appenv.h"
|
|
|
|
#include "draw_core.h"
|
1998-01-29 16:03:27 +08:00
|
|
|
#include "channel_pvt.h"
|
|
|
|
#include "drawable.h"
|
1997-11-25 06:05:25 +08:00
|
|
|
#include "errors.h"
|
|
|
|
#include "gdisplay.h"
|
|
|
|
#include "gimage_mask.h"
|
|
|
|
#include "interface.h"
|
|
|
|
#include "iscissors.h"
|
|
|
|
#include "edit_selection.h"
|
|
|
|
#include "paint_funcs.h"
|
|
|
|
#include "rect_select.h"
|
1999-04-13 01:55:06 +08:00
|
|
|
#include "selection_options.h"
|
1997-11-25 06:05:25 +08:00
|
|
|
#include "temp_buf.h"
|
1998-03-12 13:43:35 +08:00
|
|
|
#include "tools.h"
|
1999-03-06 07:50:24 +08:00
|
|
|
#include "bezier_selectP.h"
|
1999-10-06 02:05:34 +08:00
|
|
|
#include "scan_convert.h"
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-11-23 22:47:09 +08:00
|
|
|
#include "libgimp/gimpintl.h"
|
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 "libgimp/gimpmath.h"
|
1998-11-23 22:47:09 +08:00
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
#ifdef DEBUG
|
|
|
|
#define TRC(x) printf x
|
1999-10-18 04:28:56 +08:00
|
|
|
#define D(x) x
|
1999-10-06 02:05:34 +08:00
|
|
|
#else
|
|
|
|
#define TRC(x)
|
1999-10-18 04:28:56 +08:00
|
|
|
#define D(x)
|
1999-10-06 02:05:34 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* local structures */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
typedef struct _ICurve ICurve;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
struct _ICurve
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
int x1, y1;
|
|
|
|
int x2, y2;
|
|
|
|
GPtrArray *points;
|
1997-11-25 06:05:25 +08:00
|
|
|
};
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* The possible states... */
|
|
|
|
typedef enum {
|
|
|
|
NO_ACTION,
|
|
|
|
SEED_PLACEMENT,
|
|
|
|
SEED_ADJUSTMENT,
|
|
|
|
WAITING
|
|
|
|
} Iscissors_state;
|
|
|
|
|
|
|
|
/* The possible drawing states... */
|
|
|
|
typedef enum {
|
|
|
|
DRAW_NOTHING = 0x0,
|
|
|
|
DRAW_CURRENT_SEED = 0x1,
|
|
|
|
DRAW_CURVE = 0x2,
|
|
|
|
DRAW_ACTIVE_CURVE = 0x4
|
|
|
|
} Iscissors_draw;
|
|
|
|
#define DRAW_ALL (DRAW_CURRENT_SEED | DRAW_CURVE)
|
|
|
|
|
|
|
|
typedef struct _iscissors Iscissors;
|
|
|
|
|
|
|
|
struct _iscissors
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
DrawCore * core; /* Core select object */
|
1999-08-16 11:43:48 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
int x, y; /* upper left hand coordinate */
|
|
|
|
int ix, iy; /* initial coordinates */
|
|
|
|
int nx, ny; /* new coordinates */
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
TempBuf * dp_buf; /* dynamic programming buffer */
|
|
|
|
|
|
|
|
ICurve * curve1; /* 1st curve connected to current point */
|
|
|
|
ICurve * curve2; /* 2nd curve connected to current point */
|
|
|
|
|
|
|
|
GSList * curves; /* the list of curves */
|
|
|
|
|
|
|
|
gboolean first_point; /* is this the first point? */
|
|
|
|
gboolean connected; /* is the region closed? */
|
|
|
|
|
|
|
|
Iscissors_state state; /* state of iscissors */
|
|
|
|
Iscissors_draw draw; /* items to draw on a draw request */
|
|
|
|
|
|
|
|
/* XXX might be useful */
|
1997-11-25 06:05:25 +08:00
|
|
|
Channel * mask; /* selection mask */
|
1999-08-16 11:43:48 +08:00
|
|
|
TileManager * gradient_map; /* lazily filled gradient map */
|
1997-11-25 06:05:25 +08:00
|
|
|
};
|
|
|
|
|
1999-04-09 06:25:54 +08:00
|
|
|
typedef struct _IScissorsOptions IScissorsOptions;
|
|
|
|
struct _IScissorsOptions
|
|
|
|
{
|
1999-04-13 01:55:06 +08:00
|
|
|
SelectionOptions selection_options;
|
1999-04-09 06:25:54 +08:00
|
|
|
};
|
|
|
|
|
1999-04-13 01:55:06 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/**********************************************/
|
|
|
|
/* Intelligent scissors selection apparatus */
|
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Other defines... */
|
|
|
|
#define MAX_GRADIENT 179.606 /* == sqrt(127^2 + 127^2) */
|
|
|
|
#define GRADIENT_SEARCH 32 /* how far to look when snapping to an edge */
|
|
|
|
#define TARGET_HEIGHT 25
|
|
|
|
#define TARGET_WIDTH 25
|
|
|
|
#define POINT_WIDTH 8 /* size (in pixels) of seed handles */
|
|
|
|
#define POINT_HALFWIDTH (POINT_WIDTH / 2)
|
|
|
|
#define EXTEND_BY 0.2 /* proportion to expand cost map by */
|
|
|
|
#define FIXED 5 /* additional fixed size to expand cost map */
|
|
|
|
#define MIN_GRADIENT 63 /* gradients < this are directionless */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
#define MAX_POINTS 2048
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
#ifdef USE_LAPLACIAN
|
|
|
|
# define COST_WIDTH 3 /* number of bytes for each pixel in cost map */
|
|
|
|
#else
|
|
|
|
# define COST_WIDTH 2 /* number of bytes for each pixel in cost map */
|
|
|
|
#endif
|
1997-11-25 06:05:25 +08:00
|
|
|
#define BLOCK_WIDTH 64
|
|
|
|
#define BLOCK_HEIGHT 64
|
1999-08-16 11:43:48 +08:00
|
|
|
#define CONV_WIDTH (BLOCK_WIDTH + 2)
|
|
|
|
#define CONV_HEIGHT (BLOCK_HEIGHT + 2)
|
|
|
|
|
|
|
|
/* weight to give between laplacian (_Z), gradient (_G) and direction (_D) */
|
|
|
|
#ifdef USE_LAPLACIAN
|
|
|
|
# define OMEGA_Z 0.16
|
|
|
|
# define OMEGA_D 0.42
|
|
|
|
# define OMEGA_G 0.42
|
|
|
|
#else
|
|
|
|
# define OMEGA_D 0.2
|
|
|
|
# define OMEGA_G 0.8
|
|
|
|
#endif
|
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 */
|
|
|
|
#define PIXEL_COST(x) (x >> 8)
|
|
|
|
#define PIXEL_DIR(x) (x & 0x000000ff)
|
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 */
|
|
|
|
static int move [8][2] =
|
|
|
|
{
|
|
|
|
{ 1, 0 },
|
|
|
|
{ 0, 1 },
|
|
|
|
{ -1, 1 },
|
|
|
|
{ 1, 1 },
|
|
|
|
{ -1, 0 },
|
|
|
|
{ 0, -1 },
|
|
|
|
{ 1, -1 },
|
|
|
|
{ -1, -1 },
|
|
|
|
};
|
|
|
|
|
|
|
|
/* IE:
|
|
|
|
* '---+---+---`
|
|
|
|
* | 7 | 5 | 6 |
|
|
|
|
* +---+---+---+
|
|
|
|
* | 4 | | 0 |
|
|
|
|
* +---+---+---+
|
|
|
|
* | 2 | 1 | 3 |
|
|
|
|
* `---+---+---'
|
|
|
|
*/
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* points for drawing curves */
|
|
|
|
static GdkPoint curve_points [MAX_POINTS];
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* temporary convolution buffers -- */
|
1999-10-18 04:28:56 +08:00
|
|
|
D(static unsigned int sent0 = 0xd0d0d0d0);
|
1999-08-16 11:43:48 +08:00
|
|
|
static unsigned char maxgrad_conv0 [TILE_WIDTH * TILE_HEIGHT * 4] = "";
|
1999-10-18 04:28:56 +08:00
|
|
|
D(static unsigned int sent1 = 0xd1d1d1d1);
|
1999-08-16 11:43:48 +08:00
|
|
|
static unsigned char maxgrad_conv1 [TILE_WIDTH * TILE_HEIGHT * 4] = "";
|
1999-10-18 04:28:56 +08:00
|
|
|
D(static unsigned int sent2 = 0xd2d2d2d2);
|
1999-08-16 11:43:48 +08:00
|
|
|
static unsigned char maxgrad_conv2 [TILE_WIDTH * TILE_HEIGHT * 4] = "";
|
1999-10-18 04:28:56 +08:00
|
|
|
D(static unsigned int sent3 = 0xd3d3d3d3);
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
|
|
|
|
static int horz_deriv [9] =
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
1, 0, -1,
|
|
|
|
2, 0, -2,
|
|
|
|
1, 0, -1,
|
1997-11-25 06:05:25 +08:00
|
|
|
};
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
static int vert_deriv [9] =
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
1, 2, 1,
|
|
|
|
0, 0, 0,
|
|
|
|
-1, -2, -1,
|
1997-11-25 06:05:25 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
#ifdef USE_LAPLACIAN
|
|
|
|
static int laplacian [9] =
|
|
|
|
{
|
|
|
|
-1, -1, -1,
|
|
|
|
-1, 8, -1,
|
|
|
|
-1, -1, -1,
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int blur_32 [9] =
|
|
|
|
{
|
|
|
|
1, 1, 1,
|
|
|
|
1, 24, 1,
|
|
|
|
1, 1, 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
static float distance_weights [GRADIENT_SEARCH * GRADIENT_SEARCH];
|
|
|
|
|
|
|
|
static int diagonal_weight [256];
|
|
|
|
static int direction_value [256][4];
|
|
|
|
static gboolean initialized = FALSE;
|
1999-10-06 02:05:34 +08:00
|
|
|
static Tile *cur_tile = NULL;
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* static variables */
|
|
|
|
|
|
|
|
static IScissorsOptions *iscissors_options = NULL;
|
1998-02-08 08:43:39 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
/* Local function prototypes */
|
1998-02-08 08:43:39 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static void iscissors_button_press (Tool *, GdkEventButton *, gpointer);
|
|
|
|
static void iscissors_button_release (Tool *, GdkEventButton *, gpointer);
|
|
|
|
static void iscissors_motion (Tool *, GdkEventMotion *, gpointer);
|
1999-08-16 11:43:48 +08:00
|
|
|
static void iscissors_control (Tool *, ToolAction, gpointer);
|
1997-11-25 06:05:25 +08:00
|
|
|
static void iscissors_reset (Iscissors *);
|
|
|
|
static void iscissors_draw (Tool *);
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
static TileManager *gradient_map_new (GImage *gimage);
|
|
|
|
|
|
|
|
static void find_optimal_path (TileManager *, TempBuf *, int, int,
|
|
|
|
int, int, int, int);
|
|
|
|
static void find_max_gradient (Iscissors *, GImage *, int *, int *);
|
|
|
|
static void calculate_curve (Tool *, ICurve *);
|
|
|
|
static void iscissors_draw_curve (GDisplay *, Iscissors *, ICurve *);
|
|
|
|
static void iscissors_free_icurves (GSList *);
|
|
|
|
static void iscissors_free_buffers (Iscissors *);
|
|
|
|
static int clicked_on_vertex (Tool *);
|
|
|
|
static int clicked_on_curve (Tool *);
|
|
|
|
static void precalculate_arrays (void);
|
|
|
|
static GPtrArray *plot_pixels (Iscissors *, TempBuf *,
|
|
|
|
int, int, int, int, int, int);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
|
1998-02-08 08:43:39 +08:00
|
|
|
|
1999-04-09 06:25:54 +08:00
|
|
|
static void
|
1999-04-13 01:55:06 +08:00
|
|
|
iscissors_options_reset (void)
|
1999-04-09 06:25:54 +08:00
|
|
|
{
|
|
|
|
IScissorsOptions *options = iscissors_options;
|
|
|
|
|
1999-04-13 01:55:06 +08:00
|
|
|
selection_options_reset ((SelectionOptions *) options);
|
1999-04-09 06:25:54 +08:00
|
|
|
}
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static IScissorsOptions *
|
1999-04-13 01:55:06 +08:00
|
|
|
iscissors_options_new (void)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
IScissorsOptions *options;
|
1999-04-13 01:55:06 +08:00
|
|
|
|
|
|
|
/* the new intelligent scissors tool options structure */
|
1999-08-16 11:43:48 +08:00
|
|
|
options = g_new (IScissorsOptions, 1);
|
1999-04-13 01:55:06 +08:00
|
|
|
selection_options_init ((SelectionOptions *) options,
|
|
|
|
ISCISSORS,
|
|
|
|
iscissors_options_reset);
|
1998-02-08 08:43:39 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
return options;
|
|
|
|
}
|
|
|
|
|
|
|
|
Tool *
|
1999-08-16 11:43:48 +08:00
|
|
|
tools_new_iscissors (void)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
Tool * tool;
|
|
|
|
Iscissors * private;
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
if (!iscissors_options)
|
1999-04-13 01:55:06 +08:00
|
|
|
{
|
|
|
|
iscissors_options = iscissors_options_new ();
|
|
|
|
tools_register (ISCISSORS, (ToolOptions *) iscissors_options);
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-07-03 01:40:10 +08:00
|
|
|
tool = tools_new_tool (ISCISSORS);
|
|
|
|
private = g_new (Iscissors, 1);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
private->core = draw_core_new (iscissors_draw);
|
1999-08-16 11:43:48 +08:00
|
|
|
private->curves = NULL;
|
|
|
|
private->dp_buf = NULL;
|
|
|
|
private->state = NO_ACTION;
|
1997-11-25 06:05:25 +08:00
|
|
|
private->mask = NULL;
|
1999-08-16 11:43:48 +08:00
|
|
|
private->gradient_map = NULL;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
tool->auto_snap_to = FALSE; /* Don't snap to guides */
|
1999-04-09 06:25:54 +08:00
|
|
|
|
1999-07-03 01:40:10 +08:00
|
|
|
tool->private = (void *) private;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
1999-07-03 01:40:10 +08:00
|
|
|
tool->button_press_func = iscissors_button_press;
|
1997-11-25 06:05:25 +08:00
|
|
|
tool->button_release_func = iscissors_button_release;
|
1999-07-03 01:40:10 +08:00
|
|
|
tool->motion_func = iscissors_motion;
|
|
|
|
tool->cursor_update_func = rect_select_cursor_update;
|
|
|
|
tool->control_func = iscissors_control;
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
iscissors_reset (private);
|
1999-07-03 01:40:10 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
return tool;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
tools_free_iscissors (Tool *tool)
|
|
|
|
{
|
|
|
|
Iscissors * iscissors;
|
|
|
|
|
|
|
|
iscissors = (Iscissors *) tool->private;
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("tools_free_iscissors\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* XXX? Undraw curve */
|
|
|
|
/*iscissors->draw = DRAW_CURVE;*/
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
if (tool->state == ACTIVE)
|
|
|
|
draw_core_stop (iscissors->core, tool);
|
|
|
|
draw_core_free (iscissors->core);
|
|
|
|
|
|
|
|
iscissors_reset (iscissors);
|
|
|
|
|
|
|
|
g_free (iscissors);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Local functions */
|
|
|
|
|
|
|
|
static void
|
|
|
|
iscissors_button_press (Tool *tool,
|
|
|
|
GdkEventButton *bevent,
|
|
|
|
gpointer gdisp_ptr)
|
|
|
|
{
|
|
|
|
GDisplay *gdisp;
|
1998-03-12 13:43:35 +08:00
|
|
|
GimpDrawable *drawable;
|
1997-11-25 06:05:25 +08:00
|
|
|
Iscissors *iscissors;
|
1999-08-16 11:43:48 +08:00
|
|
|
int grab_pointer = 0;
|
1997-11-25 06:05:25 +08:00
|
|
|
int replace, op;
|
|
|
|
|
|
|
|
gdisp = (GDisplay *) gdisp_ptr;
|
|
|
|
iscissors = (Iscissors *) tool->private;
|
1998-03-12 13:43:35 +08:00
|
|
|
drawable = gimage_active_drawable (gdisp->gimage);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
gdisplay_untransform_coords (gdisp, bevent->x, bevent->y,
|
1999-12-11 08:10:09 +08:00
|
|
|
&iscissors->x, &iscissors->y, FALSE, FALSE);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* If the tool was being used in another image...reset it */
|
1998-03-15 10:46:15 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
if (tool->state == ACTIVE && gdisp_ptr != tool->gdisp_ptr)
|
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
/*iscissors->draw = DRAW_CURVE; XXX? */
|
1997-11-25 06:05:25 +08:00
|
|
|
draw_core_stop (iscissors->core, tool);
|
|
|
|
iscissors_reset (iscissors);
|
|
|
|
}
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
tool->state = ACTIVE;
|
|
|
|
tool->gdisp_ptr = gdisp_ptr;
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
switch (iscissors->state)
|
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
case NO_ACTION :
|
|
|
|
#if 0
|
|
|
|
/* XXX what's this supposed to do? */
|
|
|
|
if (!(bevent->state & GDK_SHIFT_MASK) &&
|
|
|
|
!(bevent->state & GDK_CONTROL_MASK))
|
|
|
|
if (selection_point_inside (gdisp->select, gdisp_ptr, bevent->x, bevent->y))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
init_edit_selection (tool, gdisp->select, gdisp_ptr, bevent->x, bevent->y);
|
1997-11-25 06:05:25 +08:00
|
|
|
return;
|
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
iscissors->state = SEED_PLACEMENT;
|
|
|
|
iscissors->draw = DRAW_CURRENT_SEED;
|
|
|
|
grab_pointer = 1;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
if (! (bevent->state & GDK_SHIFT_MASK))
|
|
|
|
find_max_gradient (iscissors, gdisp->gimage,
|
|
|
|
&iscissors->x, &iscissors->y);
|
|
|
|
iscissors->x = BOUNDS (iscissors->x, 0, gdisp->gimage->width - 1);
|
|
|
|
iscissors->y = BOUNDS (iscissors->y, 0, gdisp->gimage->height - 1);
|
|
|
|
|
|
|
|
iscissors->ix = iscissors->x;
|
|
|
|
iscissors->iy = iscissors->y;
|
|
|
|
|
|
|
|
/* Initialize the selection core only on starting the tool */
|
|
|
|
draw_core_start (iscissors->core, gdisp->canvas->window, tool);
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
default :
|
|
|
|
/* Check if the mouse click occured on a vertex or the curve itself */
|
|
|
|
if (clicked_on_vertex (tool))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
iscissors->nx = iscissors->x;
|
|
|
|
iscissors->ny = iscissors->y;
|
|
|
|
iscissors->state = SEED_ADJUSTMENT;
|
|
|
|
iscissors->draw = DRAW_ACTIVE_CURVE;
|
|
|
|
draw_core_resume (iscissors->core, tool);
|
|
|
|
grab_pointer = 1;
|
|
|
|
}
|
1999-10-06 02:05:34 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* If the iscissors is connected, check if the click was inside */
|
1999-10-06 02:05:34 +08:00
|
|
|
else if (iscissors->connected && iscissors->mask &&
|
|
|
|
channel_value (iscissors->mask, iscissors->x, iscissors->y))
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
{
|
|
|
|
/* Undraw the curve */
|
|
|
|
tool->state = INACTIVE;
|
|
|
|
iscissors->draw = DRAW_CURVE;
|
|
|
|
draw_core_stop (iscissors->core, tool);
|
1998-02-08 08:43:39 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
replace = 0;
|
1999-10-06 02:05:34 +08:00
|
|
|
if (bevent->state & GDK_SHIFT_MASK)
|
1997-11-25 06:05:25 +08:00
|
|
|
op = ADD;
|
1999-10-06 02:05:34 +08:00
|
|
|
else if (bevent->state & GDK_CONTROL_MASK)
|
1997-11-25 06:05:25 +08:00
|
|
|
op = SUB;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
op = ADD;
|
|
|
|
replace = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (replace)
|
1999-10-06 02:05:34 +08:00
|
|
|
gimage_mask_clear (gdisp->gimage);
|
1997-11-25 06:05:25 +08:00
|
|
|
else
|
|
|
|
gimage_mask_undo (gdisp->gimage);
|
|
|
|
|
1999-04-13 01:55:06 +08:00
|
|
|
if (((SelectionOptions *) iscissors_options)->feather)
|
1997-11-25 06:05:25 +08:00
|
|
|
channel_feather (iscissors->mask,
|
|
|
|
gimage_get_mask (gdisp->gimage),
|
1999-04-13 01:55:06 +08:00
|
|
|
((SelectionOptions *) iscissors_options)->feather_radius,
|
1999-05-07 07:10:29 +08:00
|
|
|
((SelectionOptions *) iscissors_options)->feather_radius,
|
1999-04-13 01:55:06 +08:00
|
|
|
op, 0, 0);
|
1997-11-25 06:05:25 +08:00
|
|
|
else
|
|
|
|
channel_combine_mask (gimage_get_mask (gdisp->gimage),
|
|
|
|
iscissors->mask, op, 0, 0);
|
|
|
|
|
|
|
|
iscissors_reset (iscissors);
|
1999-08-16 11:43:48 +08:00
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
selection_start (gdisp->select, TRUE);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
}
|
1999-10-06 02:05:34 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* if we're not connected, we're adding a new point */
|
|
|
|
else if (!iscissors->connected)
|
|
|
|
{
|
|
|
|
iscissors->state = SEED_PLACEMENT;
|
|
|
|
iscissors->draw = DRAW_CURRENT_SEED;
|
|
|
|
grab_pointer = 1;
|
|
|
|
|
|
|
|
draw_core_resume (iscissors->core, tool);
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
if (grab_pointer)
|
|
|
|
gdk_pointer_grab (gdisp->canvas->window, FALSE,
|
|
|
|
GDK_POINTER_MOTION_HINT_MASK |
|
|
|
|
GDK_BUTTON1_MOTION_MASK |
|
|
|
|
GDK_BUTTON_RELEASE_MASK,
|
|
|
|
NULL, NULL, bevent->time);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
static void
|
|
|
|
iscissors_convert (Iscissors *iscissors, gpointer gdisp_ptr)
|
|
|
|
{
|
|
|
|
GDisplay *gdisp = (GDisplay *) gdisp_ptr;
|
|
|
|
ScanConverter *sc;
|
|
|
|
ScanConvertPoint *pts;
|
|
|
|
guint npts;
|
|
|
|
GSList *list;
|
|
|
|
ICurve *icurve;
|
|
|
|
guint packed;
|
|
|
|
int i;
|
|
|
|
int index;
|
|
|
|
|
|
|
|
sc = scan_converter_new (gdisp->gimage->width, gdisp->gimage->height, 1);
|
|
|
|
|
|
|
|
/* go over the curves in reverse order, adding the points we have */
|
|
|
|
list = iscissors->curves;
|
|
|
|
index = g_slist_length (list);
|
|
|
|
while (index)
|
|
|
|
{
|
|
|
|
index--;
|
|
|
|
|
|
|
|
icurve = (ICurve *) g_slist_nth_data (list, index);
|
|
|
|
|
|
|
|
npts = icurve->points->len;
|
|
|
|
pts = g_new (ScanConvertPoint, npts);
|
|
|
|
|
|
|
|
for (i=0; i < npts; i ++)
|
|
|
|
{
|
|
|
|
packed = GPOINTER_TO_INT (g_ptr_array_index (icurve->points, i));
|
|
|
|
pts[i].x = packed & 0x0000ffff;
|
|
|
|
pts[i].y = packed >> 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
scan_converter_add_points (sc, npts, pts);
|
|
|
|
g_free (pts);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iscissors->mask)
|
|
|
|
channel_delete (iscissors->mask);
|
|
|
|
iscissors->mask = scan_converter_to_channel (sc, gdisp->gimage);
|
|
|
|
scan_converter_free (sc);
|
|
|
|
|
|
|
|
channel_invalidate_bounds (iscissors->mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static void
|
|
|
|
iscissors_button_release (Tool *tool,
|
|
|
|
GdkEventButton *bevent,
|
|
|
|
gpointer gdisp_ptr)
|
|
|
|
{
|
|
|
|
Iscissors *iscissors;
|
|
|
|
GDisplay *gdisp;
|
1999-08-16 11:43:48 +08:00
|
|
|
ICurve *curve;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
gdisp = (GDisplay *) gdisp_ptr;
|
|
|
|
iscissors = (Iscissors *) tool->private;
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("iscissors_button_release\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* Make sure X didn't skip the button release event -- as it's known
|
|
|
|
* to do */
|
|
|
|
if (iscissors->state == WAITING)
|
|
|
|
return;
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
gdk_pointer_ungrab (bevent->time);
|
1999-08-16 11:43:48 +08:00
|
|
|
gdk_flush (); /* XXX why the flush? */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Undraw everything */
|
|
|
|
switch (iscissors->state)
|
|
|
|
{
|
|
|
|
case SEED_PLACEMENT :
|
|
|
|
iscissors->draw = DRAW_CURVE | DRAW_CURRENT_SEED;
|
|
|
|
break;
|
|
|
|
case SEED_ADJUSTMENT :
|
|
|
|
iscissors->draw = DRAW_CURVE | DRAW_ACTIVE_CURVE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
draw_core_stop (iscissors->core, tool);
|
|
|
|
|
|
|
|
/* First take care of the case where the user "cancels" the action */
|
|
|
|
if (! (bevent->state & GDK_BUTTON3_MASK))
|
|
|
|
{
|
|
|
|
/* Progress to the next stage of intelligent selection */
|
|
|
|
switch (iscissors->state)
|
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
case SEED_PLACEMENT :
|
|
|
|
/* Add a new icurve */
|
|
|
|
if (!iscissors->first_point)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Determine if we're connecting to the first point */
|
|
|
|
if (iscissors->curves)
|
|
|
|
{
|
|
|
|
curve = (ICurve *) iscissors->curves->data;
|
|
|
|
if (abs (iscissors->x - curve->x1) < POINT_HALFWIDTH &&
|
|
|
|
abs (iscissors->y - curve->y1) < POINT_HALFWIDTH)
|
|
|
|
{
|
|
|
|
iscissors->x = curve->x1;
|
|
|
|
iscissors->y = curve->y1;
|
|
|
|
iscissors->connected = TRUE;
|
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Create the new curve segment */
|
|
|
|
if (iscissors->ix != iscissors->x ||
|
|
|
|
iscissors->iy != iscissors->y)
|
|
|
|
{
|
|
|
|
curve = g_malloc (sizeof (ICurve));
|
|
|
|
|
|
|
|
curve->x1 = iscissors->ix;
|
|
|
|
curve->y1 = iscissors->iy;
|
|
|
|
iscissors->ix = curve->x2 = iscissors->x;
|
|
|
|
iscissors->iy = curve->y2 = iscissors->y;
|
|
|
|
curve->points = NULL;
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("create new curve segment\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
iscissors->curves = g_slist_append (iscissors->curves,
|
|
|
|
(void *) curve);
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("calculate curve\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
calculate_curve (tool, curve);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else /* this was our first point */
|
|
|
|
{
|
|
|
|
iscissors->first_point = FALSE;
|
|
|
|
}
|
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
case SEED_ADJUSTMENT :
|
|
|
|
/* recalculate both curves */
|
|
|
|
if (iscissors->curve1)
|
|
|
|
{
|
|
|
|
iscissors->curve1->x1 = iscissors->nx;
|
|
|
|
iscissors->curve1->y1 = iscissors->ny;
|
|
|
|
calculate_curve (tool, iscissors->curve1);
|
|
|
|
}
|
|
|
|
if (iscissors->curve2)
|
|
|
|
{
|
|
|
|
iscissors->curve2->x2 = iscissors->nx;
|
|
|
|
iscissors->curve2->y2 = iscissors->ny;
|
|
|
|
calculate_curve (tool, iscissors->curve2);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
break;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
default:
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("button_release: draw core resume\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* Draw only the boundary */
|
|
|
|
iscissors->state = WAITING;
|
|
|
|
iscissors->draw = DRAW_CURVE;
|
|
|
|
draw_core_resume (iscissors->core, tool);
|
|
|
|
|
|
|
|
/* convert the curves into a region */
|
|
|
|
if (iscissors->connected)
|
|
|
|
iscissors_convert (iscissors, gdisp_ptr);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iscissors_motion (Tool *tool,
|
|
|
|
GdkEventMotion *mevent,
|
|
|
|
gpointer gdisp_ptr)
|
|
|
|
{
|
|
|
|
Iscissors *iscissors;
|
|
|
|
GDisplay *gdisp;
|
1998-02-08 08:43:39 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
gdisp = (GDisplay *) gdisp_ptr;
|
|
|
|
iscissors = (Iscissors *) tool->private;
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
if (tool->state != ACTIVE || iscissors->state == NO_ACTION)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (iscissors->state == SEED_PLACEMENT)
|
|
|
|
iscissors->draw = DRAW_CURRENT_SEED;
|
|
|
|
else if (iscissors->state == SEED_ADJUSTMENT)
|
|
|
|
iscissors->draw = DRAW_ACTIVE_CURVE;
|
|
|
|
|
|
|
|
draw_core_pause (iscissors->core, tool);
|
|
|
|
|
|
|
|
gdisplay_untransform_coords (gdisp, mevent->x, mevent->y,
|
1999-12-11 08:10:09 +08:00
|
|
|
&iscissors->x, &iscissors->y, FALSE, FALSE);
|
1998-03-15 10:46:15 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
switch (iscissors->state)
|
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
case SEED_PLACEMENT :
|
|
|
|
/* Hold the shift key down to disable the auto-edge snap feature */
|
|
|
|
if (! (mevent->state & GDK_SHIFT_MASK))
|
|
|
|
find_max_gradient (iscissors, gdisp->gimage,
|
|
|
|
&iscissors->x, &iscissors->y);
|
|
|
|
iscissors->x = BOUNDS (iscissors->x, 0, gdisp->gimage->width - 1);
|
|
|
|
iscissors->y = BOUNDS (iscissors->y, 0, gdisp->gimage->height - 1);
|
|
|
|
|
|
|
|
if (iscissors->first_point)
|
|
|
|
{
|
|
|
|
iscissors->ix = iscissors->x;
|
|
|
|
iscissors->iy = iscissors->y;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SEED_ADJUSTMENT :
|
|
|
|
/* Move the current seed to the location of the cursor */
|
|
|
|
if (! (mevent->state & GDK_SHIFT_MASK))
|
|
|
|
find_max_gradient (iscissors, gdisp->gimage,
|
|
|
|
&iscissors->x, &iscissors->y);
|
|
|
|
iscissors->x = BOUNDS (iscissors->x, 0, gdisp->gimage->width - 1);
|
|
|
|
iscissors->y = BOUNDS (iscissors->y, 0, gdisp->gimage->height - 1);
|
|
|
|
|
|
|
|
iscissors->nx = iscissors->x;
|
|
|
|
iscissors->ny = iscissors->y;
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
default :
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
draw_core_resume (iscissors->core, tool);
|
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
|
|
|
|
iscissors_draw (Tool *tool)
|
|
|
|
{
|
|
|
|
GDisplay *gdisp;
|
|
|
|
Iscissors *iscissors;
|
1999-08-16 11:43:48 +08:00
|
|
|
ICurve *curve;
|
|
|
|
GSList *list;
|
|
|
|
int tx1, ty1, tx2, ty2;
|
|
|
|
int txn, tyn;
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("iscissors_draw\n"));
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
gdisp = (GDisplay *) tool->gdisp_ptr;
|
|
|
|
iscissors = (Iscissors *) tool->private;
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
gdisplay_transform_coords (gdisp, iscissors->ix, iscissors->iy, &tx1, &ty1,
|
|
|
|
FALSE);
|
|
|
|
|
|
|
|
/* Draw the crosshairs target if we're placing a seed */
|
|
|
|
if (iscissors->draw & DRAW_CURRENT_SEED)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
gdisplay_transform_coords(gdisp, iscissors->x, iscissors->y, &tx2, &ty2,
|
|
|
|
FALSE);
|
|
|
|
|
|
|
|
gdk_draw_line (iscissors->core->win, iscissors->core->gc,
|
|
|
|
tx2 - (TARGET_WIDTH >> 1), ty2,
|
|
|
|
tx2 + (TARGET_WIDTH >> 1), ty2);
|
|
|
|
gdk_draw_line (iscissors->core->win, iscissors->core->gc,
|
|
|
|
tx2, ty2 - (TARGET_HEIGHT >> 1),
|
|
|
|
tx2, ty2 + (TARGET_HEIGHT >> 1));
|
|
|
|
|
|
|
|
if (!iscissors->first_point)
|
|
|
|
gdk_draw_line (iscissors->core->win, iscissors->core->gc,
|
|
|
|
tx1, ty1, tx2, ty2);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
1998-03-27 03:17:42 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
if ((iscissors->draw & DRAW_CURVE) && !iscissors->first_point)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Draw a point at the init point coordinates */
|
|
|
|
if (!iscissors->connected)
|
|
|
|
gdk_draw_arc (iscissors->core->win, iscissors->core->gc, 1,
|
|
|
|
tx1 - POINT_HALFWIDTH, ty1 - POINT_HALFWIDTH,
|
|
|
|
POINT_WIDTH, POINT_WIDTH, 0, 23040);
|
|
|
|
|
|
|
|
/* Go through the list of icurves, and render each one... */
|
|
|
|
list = iscissors->curves;
|
|
|
|
while (list)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
curve = (ICurve *) list->data;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* plot the curve */
|
|
|
|
iscissors_draw_curve (gdisp, iscissors, curve);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
gdisplay_transform_coords (gdisp, curve->x1, curve->y1, &tx1, &ty1,
|
|
|
|
FALSE);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
gdk_draw_arc (iscissors->core->win, iscissors->core->gc, 1,
|
|
|
|
tx1 - POINT_HALFWIDTH, ty1 - POINT_HALFWIDTH,
|
|
|
|
POINT_WIDTH, POINT_WIDTH, 0, 23040);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
list = g_slist_next (list);
|
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
if (iscissors->draw & DRAW_ACTIVE_CURVE)
|
|
|
|
{
|
|
|
|
gdisplay_transform_coords (gdisp, iscissors->nx, iscissors->ny,
|
|
|
|
&txn, &tyn, FALSE);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* plot both curves, and the control point between them */
|
|
|
|
if (iscissors->curve1)
|
|
|
|
{
|
|
|
|
gdisplay_transform_coords (gdisp, iscissors->curve1->x2,
|
|
|
|
iscissors->curve1->y2, &tx1, &ty1, FALSE);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
gdk_draw_line (iscissors->core->win, iscissors->core->gc,
|
|
|
|
tx1, ty1, txn, tyn);
|
|
|
|
}
|
|
|
|
if (iscissors->curve2)
|
|
|
|
{
|
|
|
|
gdisplay_transform_coords (gdisp, iscissors->curve2->x1,
|
|
|
|
iscissors->curve2->y1, &tx2, &ty2, FALSE);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
gdk_draw_line (iscissors->core->win, iscissors->core->gc,
|
|
|
|
tx2, ty2, txn, tyn);
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
gdk_draw_arc (iscissors->core->win, iscissors->core->gc, 1,
|
|
|
|
txn - POINT_HALFWIDTH, tyn - POINT_HALFWIDTH,
|
|
|
|
POINT_WIDTH, POINT_WIDTH, 0, 23040);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
static void
|
|
|
|
iscissors_draw_curve (GDisplay *gdisp,
|
|
|
|
Iscissors *iscissors,
|
|
|
|
ICurve *curve)
|
|
|
|
{
|
|
|
|
gpointer *point;
|
|
|
|
guint len;
|
|
|
|
int tx, ty;
|
|
|
|
int npts;
|
|
|
|
guint32 coords;
|
|
|
|
|
1999-10-18 04:28:56 +08:00
|
|
|
/* Uh, this shouldn't happen, but it does. So we ignore it.
|
|
|
|
* Quality code, baby. */
|
|
|
|
if (!curve->points)
|
|
|
|
return;
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
npts = 0;
|
|
|
|
point = curve->points->pdata;
|
|
|
|
len = curve->points->len;
|
|
|
|
while (len--)
|
|
|
|
{
|
|
|
|
coords = GPOINTER_TO_INT (*point);
|
|
|
|
point++;
|
|
|
|
gdisplay_transform_coords (gdisp, (coords & 0x0000ffff),
|
|
|
|
(coords >> 16), &tx, &ty, FALSE);
|
|
|
|
if (npts < MAX_POINTS)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
curve_points [npts].x = tx;
|
|
|
|
curve_points [npts].y = ty;
|
|
|
|
npts ++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_warning ("too many points in ICurve segment!");
|
|
|
|
return;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* draw the curve */
|
|
|
|
gdk_draw_lines (iscissors->core->win, iscissors->core->gc,
|
|
|
|
curve_points, npts);
|
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
|
1999-06-22 06:12:07 +08:00
|
|
|
iscissors_control (Tool *tool,
|
|
|
|
ToolAction action,
|
|
|
|
gpointer gdisp_ptr)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
Iscissors * iscissors;
|
1999-08-16 11:43:48 +08:00
|
|
|
Iscissors_draw draw;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
iscissors = (Iscissors *) tool->private;
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
switch (iscissors->state)
|
|
|
|
{
|
|
|
|
case SEED_PLACEMENT:
|
|
|
|
draw = DRAW_CURVE | DRAW_CURRENT_SEED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SEED_ADJUSTMENT:
|
|
|
|
draw = DRAW_CURVE | DRAW_ACTIVE_CURVE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
draw = DRAW_CURVE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
iscissors->draw = draw;
|
1997-11-25 06:05:25 +08:00
|
|
|
switch (action)
|
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
case PAUSE:
|
1997-11-25 06:05:25 +08:00
|
|
|
draw_core_pause (iscissors->core, tool);
|
|
|
|
break;
|
1999-06-22 06:12:07 +08:00
|
|
|
|
|
|
|
case RESUME:
|
1997-11-25 06:05:25 +08:00
|
|
|
draw_core_resume (iscissors->core, tool);
|
|
|
|
break;
|
1999-06-22 06:12:07 +08:00
|
|
|
|
|
|
|
case HALT:
|
1997-11-25 06:05:25 +08:00
|
|
|
draw_core_stop (iscissors->core, tool);
|
|
|
|
iscissors_reset (iscissors);
|
|
|
|
break;
|
1999-06-22 06:12:07 +08:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
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
|
|
|
|
iscissors_reset (Iscissors *iscissors)
|
|
|
|
{
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC( ("iscissors_reset\n"));
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Free and reset the curve list */
|
|
|
|
if (iscissors->curves)
|
|
|
|
{
|
|
|
|
iscissors_free_icurves (iscissors->curves);
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("g_slist_free (iscissors->curves);\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
g_slist_free (iscissors->curves);
|
|
|
|
iscissors->curves = NULL;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* free mask */
|
|
|
|
if (iscissors->mask)
|
|
|
|
channel_delete (iscissors->mask);
|
|
|
|
iscissors->mask = NULL;
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* free the gradient map */
|
|
|
|
if (iscissors->gradient_map)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-10-06 02:05:34 +08:00
|
|
|
/* release any tile we were using */
|
|
|
|
if (cur_tile)
|
|
|
|
{
|
|
|
|
TRC (("tile_release\n"));
|
|
|
|
tile_release (cur_tile, FALSE);
|
|
|
|
}
|
|
|
|
cur_tile = NULL;
|
|
|
|
|
|
|
|
TRC (("tile_manager_destroy (iscissors->gradient_map);\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
tile_manager_destroy (iscissors->gradient_map);
|
|
|
|
iscissors->gradient_map = NULL;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
iscissors->curve1 = NULL;
|
|
|
|
iscissors->curve2 = NULL;
|
|
|
|
iscissors->first_point = TRUE;
|
|
|
|
iscissors->connected = FALSE;
|
|
|
|
iscissors->state = NO_ACTION;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Reset the dp buffers */
|
|
|
|
iscissors_free_buffers (iscissors);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* If they haven't already been initialized, precalculate the diagonal
|
|
|
|
* weight and direction value arrays
|
|
|
|
*/
|
|
|
|
if (!initialized)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
precalculate_arrays ();
|
|
|
|
initialized = TRUE;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-03-17 07:59:27 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
static void
|
|
|
|
iscissors_free_icurves (GSList *list)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
ICurve * curve;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("iscissors_free_icurves\n"));
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
while (list)
|
|
|
|
{
|
|
|
|
curve = (ICurve *) list->data;
|
|
|
|
if (curve->points)
|
|
|
|
{
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("g_ptr_array_free (curve->points);\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
g_ptr_array_free (curve->points, TRUE);
|
|
|
|
}
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("g_free (curve);\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
g_free (curve);
|
|
|
|
list = g_slist_next (list);
|
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
|
1999-08-16 11:43:48 +08:00
|
|
|
iscissors_free_buffers (Iscissors * iscissors)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
if (iscissors->dp_buf)
|
|
|
|
temp_buf_free (iscissors->dp_buf);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
iscissors->dp_buf = NULL;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* XXX need some scan-conversion routines from somewhere. maybe. ? */
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
static int
|
|
|
|
clicked_on_vertex (Tool *tool)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
Iscissors * iscissors;
|
|
|
|
GSList *list;
|
|
|
|
ICurve * curve;
|
|
|
|
int curves_found = 0;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
iscissors = (Iscissors *) tool->private;
|
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
|
|
|
|
* position is on an existing curve vertex. Set the curve1 and curve2
|
|
|
|
* variables to the two curves containing the vertex in question
|
|
|
|
*/
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
iscissors->curve1 = iscissors->curve2 = NULL;
|
|
|
|
|
|
|
|
list = iscissors->curves;
|
|
|
|
|
|
|
|
while (list && curves_found < 2)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
curve = (ICurve *) list->data;
|
|
|
|
|
|
|
|
if (abs (curve->x1 - iscissors->x) < POINT_HALFWIDTH &&
|
|
|
|
abs (curve->y1 - iscissors->y) < POINT_HALFWIDTH)
|
|
|
|
{
|
|
|
|
iscissors->curve1 = curve;
|
|
|
|
if (curves_found++)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else if (abs (curve->x2 - iscissors->x) < POINT_HALFWIDTH &&
|
|
|
|
abs (curve->y2 - iscissors->y) < POINT_HALFWIDTH)
|
|
|
|
{
|
|
|
|
iscissors->curve2 = curve;
|
|
|
|
if (curves_found++)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
list = g_slist_next (list);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* if only one curve was found, the curves are unconnected, and
|
|
|
|
* the user only wants to move either the first or last point
|
|
|
|
* disallow this for now.
|
|
|
|
*/
|
|
|
|
if (curves_found == 1)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
return 0;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* no vertices were found at the cursor click point. Now check whether
|
|
|
|
* the click occured on a curve. If so, create a new vertex there and
|
|
|
|
* two curve segments to replace what used to be just one...
|
|
|
|
*/
|
|
|
|
return clicked_on_curve (tool);
|
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 int
|
1999-08-16 11:43:48 +08:00
|
|
|
clicked_on_curve (Tool *tool)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
Iscissors * iscissors;
|
|
|
|
GSList *list, *new_link;
|
|
|
|
gpointer *pt;
|
|
|
|
int len;
|
|
|
|
ICurve * curve, * new_curve;
|
|
|
|
guint32 coords;
|
|
|
|
int tx, ty;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
iscissors = (Iscissors *) tool->private;
|
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
|
|
|
|
* position is on a curve... If this occurs, replace the curve with two
|
|
|
|
* new curves, separated by the new vertex.
|
|
|
|
*/
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
list = iscissors->curves;
|
|
|
|
while (list)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
curve = (ICurve *) list->data;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
pt = curve->points->pdata;
|
|
|
|
len = curve->points->len;
|
|
|
|
while (len--)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
coords = GPOINTER_TO_INT (*pt);
|
|
|
|
pt++;
|
|
|
|
tx = coords & 0x0000ffff;
|
|
|
|
ty = coords >> 16;
|
|
|
|
|
|
|
|
/* Is the specified point close enough to the curve? */
|
|
|
|
if (abs (tx - iscissors->x) < POINT_HALFWIDTH &&
|
|
|
|
abs (ty - iscissors->y) < POINT_HALFWIDTH)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Since we're modifying the curve, undraw the existing one */
|
|
|
|
iscissors->draw = DRAW_CURVE;
|
|
|
|
draw_core_pause (iscissors->core, tool);
|
|
|
|
|
|
|
|
/* Create the new curve */
|
|
|
|
new_curve = g_malloc (sizeof (ICurve));
|
|
|
|
|
|
|
|
new_curve->x2 = curve->x2;
|
|
|
|
new_curve->y2 = curve->y2;
|
|
|
|
new_curve->x1 = curve->x2 = iscissors->x;
|
|
|
|
new_curve->y1 = curve->y2 = iscissors->y;
|
|
|
|
new_curve->points = NULL;
|
|
|
|
|
|
|
|
/* Create the new link and supply the new curve as data */
|
|
|
|
new_link = g_slist_alloc ();
|
|
|
|
new_link->data = (void *) new_curve;
|
|
|
|
|
|
|
|
/* Insert the new link in the list */
|
|
|
|
new_link->next = list->next;
|
|
|
|
list->next = new_link;
|
|
|
|
|
|
|
|
iscissors->curve1 = new_curve;
|
|
|
|
iscissors->curve2 = curve;
|
|
|
|
|
|
|
|
/* Redraw the curve */
|
|
|
|
draw_core_resume (iscissors->core, tool);
|
|
|
|
|
|
|
|
return 1;
|
1998-01-29 16:03:27 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
list = g_slist_next (list);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
return 0;
|
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
|
1999-08-16 11:43:48 +08:00
|
|
|
precalculate_arrays (void)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
int i;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
{
|
|
|
|
/* The diagonal weight array */
|
1999-08-16 12:59:48 +08:00
|
|
|
diagonal_weight [i] = (int) (i * G_SQRT2);
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("i: %d, v0: %d, v1: %d, v2: %d, v3: %d\n", i,
|
1999-08-16 11:43:48 +08:00
|
|
|
direction_value [i][0],
|
|
|
|
direction_value [i][1],
|
|
|
|
direction_value [i][2],
|
1999-10-06 02:05:34 +08:00
|
|
|
direction_value [i][3]));
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
1998-01-29 16:03:27 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* set the 256th index of the direction_values to the hightest cost */
|
|
|
|
direction_value [255][0] = 255;
|
|
|
|
direction_value [255][1] = 255;
|
|
|
|
direction_value [255][2] = 255;
|
|
|
|
direction_value [255][3] = 255;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
1999-08-16 11:43:48 +08:00
|
|
|
calculate_curve (Tool *tool, ICurve *curve)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
GDisplay * gdisp;
|
|
|
|
Iscissors * iscissors;
|
|
|
|
int x, y, dir;
|
|
|
|
int xs, ys, xe, ye;
|
|
|
|
int x1, y1, x2, y2;
|
1997-11-25 06:05:25 +08:00
|
|
|
int width, height;
|
1999-08-16 11:43:48 +08:00
|
|
|
int ewidth, eheight;
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("calculate_curve(%p, %p)\n", tool, curve));
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* Calculate the lowest cost path from one vertex to the next as specified
|
|
|
|
* by the parameter "curve".
|
|
|
|
* 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
|
|
|
|
* 4) Translate the optimal path into pixels in the icurve data
|
|
|
|
* structure.
|
|
|
|
*/
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
gdisp = (GDisplay *) tool->gdisp_ptr;
|
|
|
|
iscissors = (Iscissors *) tool->private;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Get the bounding box */
|
|
|
|
xs = BOUNDS (curve->x1, 0, gdisp->gimage->width - 1);
|
|
|
|
ys = BOUNDS (curve->y1, 0, gdisp->gimage->height - 1);
|
|
|
|
xe = BOUNDS (curve->x2, 0, gdisp->gimage->width - 1);
|
|
|
|
ye = BOUNDS (curve->y2, 0, gdisp->gimage->height - 1);
|
|
|
|
x1 = MINIMUM (xs, xe);
|
|
|
|
y1 = MINIMUM (ys, ye);
|
|
|
|
x2 = MAXIMUM (xs, xe) + 1; /* +1 because if xe = 199 & xs = 0, x2 - x1, width = 200 */
|
|
|
|
y2 = MAXIMUM (ys, ye) + 1;
|
|
|
|
|
|
|
|
/* expand the boundaries past the ending points by
|
|
|
|
* 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
|
|
|
|
* coordinates of the "curve".
|
|
|
|
*/
|
|
|
|
ewidth = (x2 - x1) * EXTEND_BY + FIXED;
|
|
|
|
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)
|
|
|
|
x2 += BOUNDS (ewidth, 0, gdisp->gimage->width - x2);
|
|
|
|
else
|
|
|
|
x1 -= BOUNDS (ewidth, 0, x1);
|
|
|
|
if (ye >= ys)
|
|
|
|
y2 += BOUNDS (eheight, 0, gdisp->gimage->height - y2);
|
|
|
|
else
|
|
|
|
y1 -= BOUNDS (eheight, 0, y1);
|
|
|
|
|
|
|
|
/* blow away any previous points list we might have */
|
|
|
|
if (curve->points)
|
|
|
|
{
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("1229: g_ptr_array_free (curve->points);\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
g_ptr_array_free (curve->points, TRUE);
|
|
|
|
curve->points = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the bounding box has width and height... */
|
|
|
|
if ((x2 - x1) && (y2 - y1))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
width = (x2 - x1);
|
|
|
|
height = (y2 - y1);
|
|
|
|
|
|
|
|
/* Initialise the gradient map tile manager for this image if we
|
|
|
|
* don't already have one. */
|
|
|
|
if (!iscissors->gradient_map)
|
|
|
|
iscissors->gradient_map = gradient_map_new (gdisp->gimage);
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("dp buf resize\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
/* allocate the dynamic programming array */
|
|
|
|
iscissors->dp_buf =
|
|
|
|
temp_buf_resize (iscissors->dp_buf, 4, x1, y1, width, height);
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("find_optimal_path\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
/* find the optimal path of pixels from (x1, y1) to (x2, y2) */
|
|
|
|
find_optimal_path (iscissors->gradient_map, iscissors->dp_buf,
|
|
|
|
x1, y1, x2, y2, xs, ys);
|
|
|
|
|
|
|
|
/* get a list of the pixels in the optimal path */
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("plot_pixels\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
curve->points = plot_pixels (iscissors, iscissors->dp_buf,
|
|
|
|
x1, y1, xs, ys, xe, ye);
|
|
|
|
}
|
|
|
|
/* If the bounding box has no width */
|
|
|
|
else if ((x2 - x1) == 0)
|
|
|
|
{
|
|
|
|
/* plot a vertical line */
|
|
|
|
y = ys;
|
|
|
|
dir = (ys > ye) ? -1 : 1;
|
|
|
|
curve->points = g_ptr_array_new ();
|
|
|
|
while (y != ye)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
g_ptr_array_add (curve->points, GINT_TO_POINTER ((y << 16) + xs));
|
|
|
|
y += dir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* If the bounding box has no height */
|
|
|
|
else if ((y2 - y1) == 0)
|
|
|
|
{
|
|
|
|
/* plot a horizontal line */
|
|
|
|
x = xs;
|
|
|
|
dir = (xs > xe) ? -1 : 1;
|
|
|
|
curve->points = g_ptr_array_new ();
|
|
|
|
while (x != xe)
|
|
|
|
{
|
|
|
|
g_ptr_array_add (curve->points, GINT_TO_POINTER ((ys << 16) + x));
|
|
|
|
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 */
|
|
|
|
static int
|
|
|
|
gradient_map_value (TileManager *map, int x, int y,
|
|
|
|
guint8 *grad, guint8 *dir)
|
|
|
|
{
|
|
|
|
static int cur_tilex;
|
|
|
|
static int cur_tiley;
|
|
|
|
guint8 *p;
|
|
|
|
|
|
|
|
if (!cur_tile ||
|
|
|
|
x / TILE_WIDTH != cur_tilex ||
|
|
|
|
y / TILE_HEIGHT != cur_tiley)
|
|
|
|
{
|
|
|
|
if (cur_tile)
|
|
|
|
tile_release (cur_tile, FALSE);
|
|
|
|
cur_tile = tile_manager_get_tile (map, x, y, TRUE, FALSE);
|
|
|
|
if (!cur_tile)
|
|
|
|
return 0;
|
|
|
|
cur_tilex = x / TILE_WIDTH;
|
|
|
|
cur_tiley = y / TILE_HEIGHT;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
p = tile_data_pointer (cur_tile, x % TILE_WIDTH, y % TILE_HEIGHT);
|
|
|
|
*grad = p[0];
|
|
|
|
*dir = p[1];
|
|
|
|
return 1;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
static int
|
|
|
|
calculate_link (TileManager *gradient_map,
|
|
|
|
int x, int y, guint32 pixel, int link)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
int value = 0;
|
|
|
|
guint8 grad1, dir1, grad2, dir2;
|
|
|
|
|
|
|
|
if (!gradient_map_value (gradient_map, x, y, &grad1, &dir1))
|
|
|
|
{
|
|
|
|
grad1 = 0;
|
|
|
|
dir1 = 255;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
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);
|
|
|
|
if (!gradient_map_value (gradient_map, x, y, &grad2, &dir2))
|
|
|
|
{
|
|
|
|
grad2 = 0;
|
|
|
|
dir2 = 255;
|
|
|
|
}
|
|
|
|
value += (direction_value [dir1][link] + direction_value [dir2][link]) *
|
|
|
|
OMEGA_D;
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
static GPtrArray *
|
|
|
|
plot_pixels (Iscissors *iscissors, TempBuf *dp_buf,
|
|
|
|
int x1, int y1,
|
|
|
|
int xs, int ys,
|
|
|
|
int xe, int ye)
|
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
guint32 coords;
|
|
|
|
int link;
|
|
|
|
int width;
|
|
|
|
unsigned int * data;
|
|
|
|
GPtrArray *list;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
width = dp_buf->width;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Start the data pointer at the correct location */
|
|
|
|
data = (unsigned int *)temp_buf_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
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
return list;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
x += move [link][0];
|
|
|
|
y += move [link][1];
|
|
|
|
data += move [link][1] * width + move [link][0];
|
|
|
|
}
|
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
|
|
|
|
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
#define PACK(x, y) ((((y) & 0xff) << 8) | ((x) & 0xff))
|
|
|
|
#define OFFSET(pixel) ((gint8)((pixel) & 0xff) + \
|
|
|
|
((gint8)(((pixel) & 0xff00) >> 8)) * dp_buf->width)
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
static void
|
|
|
|
find_optimal_path (TileManager *gradient_map, TempBuf *dp_buf,
|
|
|
|
int x1, int y1, int x2, int y2,
|
|
|
|
int xs, int ys)
|
|
|
|
{
|
|
|
|
int i, j, k;
|
|
|
|
int x, y;
|
|
|
|
int link;
|
|
|
|
int linkdir;
|
|
|
|
int dirx, diry;
|
|
|
|
int min_cost;
|
|
|
|
int new_cost;
|
|
|
|
int offset;
|
|
|
|
int cum_cost [8];
|
|
|
|
int link_cost [8];
|
|
|
|
int pixel_cost [8];
|
|
|
|
guint32 pixel [8];
|
|
|
|
guint32 * data, *d;
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("find_optimal_path (%p, %p, [%d,%d-%d,%d] %d, %d)\n",
|
|
|
|
gradient_map, dp_buf, x1, y1, x2, y2, xs, ys));
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* initialize the dynamic programming buffer */
|
|
|
|
data = (guint32 *) temp_buf_data (dp_buf);
|
|
|
|
for (i = 0; i < dp_buf->height; i++)
|
|
|
|
for (j = 0; j < dp_buf->width; j++)
|
|
|
|
*data++ = 0; /* 0 cumulative cost, 0 direction */
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
/* Start the data pointer at the correct location */
|
|
|
|
data = (guint32 *) temp_buf_data (dp_buf);
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("find_optimal_path: mainloop\n"));
|
1999-08-16 11:43:48 +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
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
d = data + (y-y1) * dp_buf->width + (x-x1);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
for (j = 0; j < dp_buf->width; j++)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
min_cost = G_MAXINT;
|
|
|
|
|
|
|
|
/* pixel[] array encodes how to get to a neigbour, if possible.
|
|
|
|
* 0 means no connection (eg edge).
|
|
|
|
* Rest packed as bottom two bytes: y offset then x offset.
|
|
|
|
* Initially, we assume we can't get anywhere. */
|
|
|
|
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)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
pixel [((diry == 1) ? 5 : 1)] = PACK (0, -diry);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
link = (linkdir == 1) ? 3 : 2;
|
|
|
|
if (j)
|
|
|
|
pixel [((diry == 1) ? (link + 4) : link)] = PACK(-dirx, -diry);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
link = (linkdir == 1) ? 2 : 3;
|
|
|
|
if (j != dp_buf->width - 1)
|
|
|
|
pixel [((diry == 1) ? (link + 4) : link)] = PACK (dirx, -diry);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* 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])
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
link_cost [k] = calculate_link (gradient_map,
|
|
|
|
xs + j*dirx, ys + i*diry,
|
|
|
|
pixel [k],
|
|
|
|
((k > 3) ? k - 4 : k));
|
|
|
|
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;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* If anything can be done... */
|
|
|
|
if (link >= 0)
|
|
|
|
{
|
|
|
|
/* set the cumulative cost of this pixel and the new direction */
|
|
|
|
*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)
|
|
|
|
{
|
|
|
|
/* reverse the link direction /-----------------------\ */
|
|
|
|
offset = OFFSET (pixel [k]);
|
|
|
|
d [offset] = (new_cost << 8) + ((k > 3) ? k - 4 : k + 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Set the seed point */
|
|
|
|
else if (!i && !j)
|
|
|
|
*d = SEED_POINT;
|
|
|
|
|
|
|
|
/* 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
|
|
|
}
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("done: find_optimal_path\n"));
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Called to fill in a newly referenced tile in the gradient map */
|
|
|
|
static void
|
|
|
|
gradmap_tile_validate (TileManager *tm, Tile *tile)
|
|
|
|
{
|
|
|
|
static gboolean first_gradient = TRUE;
|
|
|
|
int x, y;
|
|
|
|
int dw, dh;
|
|
|
|
int sw, sh;
|
|
|
|
int i, j;
|
|
|
|
int b;
|
|
|
|
float gradient;
|
|
|
|
guint8 *gradmap;
|
|
|
|
guint8 *tiledata;
|
|
|
|
guint8 *datah, *datav;
|
|
|
|
gint8 hmax, vmax;
|
|
|
|
Tile *srctile;
|
|
|
|
PixelRegion srcPR, destPR;
|
|
|
|
GImage *gimage;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
gimage = (GImage *) tile_manager_get_user_data (tm);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
if (first_gradient)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
int radius = GRADIENT_SEARCH >> 1;
|
|
|
|
/* compute the distance weights */
|
|
|
|
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)));
|
|
|
|
first_gradient = FALSE;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
tile_manager_get_tile_coordinates (tm, tile, &x, &y);
|
|
|
|
dw = tile_ewidth (tile);
|
|
|
|
dh = tile_eheight (tile);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("fill req for tile %p @ (%d, %d)\n", tile, x, y));
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* get corresponding tile in the gimage */
|
|
|
|
srctile = tile_manager_get_tile (gimp_image_composite (gimage),
|
|
|
|
x, y, TRUE, FALSE);
|
|
|
|
if (!srctile)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
g_warning ("bad tile coords?");
|
|
|
|
return;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
sw = tile_ewidth (srctile);
|
|
|
|
sh = tile_eheight (srctile);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
if (dw != sw || dh != sh)
|
|
|
|
g_warning ("dw:%d sw:%d dh:%d sh:%d\n", dw, sw, dh, sh);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
srcPR.w = MIN (dw, sw);
|
|
|
|
srcPR.h = MIN (dh, sh);
|
|
|
|
srcPR.bytes = gimp_image_composite_bytes (gimage);
|
|
|
|
srcPR.data = tile_data_pointer (srctile, 0, 0);
|
1999-10-18 04:28:56 +08:00
|
|
|
srcPR.rowstride = srcPR.w * srcPR.bytes;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* XXX tile edges? */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Blur the source to get rid of noise */
|
|
|
|
destPR.rowstride = TILE_WIDTH * 4;
|
|
|
|
destPR.data = maxgrad_conv0;
|
Actually use the enum types GimpImageType, GimpImageBaseType,
* app/*.[ch]: Actually use the enum types GimpImageType,
GimpImageBaseType, LayerModeEffects, PaintApplicationMode,
BrushApplicationMode, GimpFillType and ConvertPaletteType, instead
of just int or gint. Hopefully I catched most of the places
where these should be used.
Add an enum ConvolutionType, suffix the too general constants
NORMAL, ABSOLUTE and NEGATIVE with _CONVOL. Use NORMAL_MODE
instead of NORMAL in some places (this was what was intended). Fix
some minor gccisms.
* app/apptypes.h: New file. This file contains the above
enumeration types, and some opaque struct typedefs. It was
necessary to collect these in one header that doesn't include
other headers, because when we started using the above mentioned
types in the headers, all hell broke loose because of the
spaghetti-like cross-inclusion mess between headers.
(An example: Header A includes header B, which includes header C
which includes A. B uses a type defined in A. This is not defined,
because A hasn't defined it yet at the point where it includes B,
and A included from B of course is skipped as we already are
reading A.)
1999-08-19 07:41:39 +08:00
|
|
|
convolve_region (&srcPR, &destPR, blur_32, 3, 32, NORMAL_CONVOL);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Set the "src" temp buf up as the new source Pixel Region */
|
|
|
|
srcPR.rowstride = destPR.rowstride;
|
|
|
|
srcPR.data = destPR.data;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Get the horizontal derivative */
|
|
|
|
destPR.data = maxgrad_conv1;
|
Actually use the enum types GimpImageType, GimpImageBaseType,
* app/*.[ch]: Actually use the enum types GimpImageType,
GimpImageBaseType, LayerModeEffects, PaintApplicationMode,
BrushApplicationMode, GimpFillType and ConvertPaletteType, instead
of just int or gint. Hopefully I catched most of the places
where these should be used.
Add an enum ConvolutionType, suffix the too general constants
NORMAL, ABSOLUTE and NEGATIVE with _CONVOL. Use NORMAL_MODE
instead of NORMAL in some places (this was what was intended). Fix
some minor gccisms.
* app/apptypes.h: New file. This file contains the above
enumeration types, and some opaque struct typedefs. It was
necessary to collect these in one header that doesn't include
other headers, because when we started using the above mentioned
types in the headers, all hell broke loose because of the
spaghetti-like cross-inclusion mess between headers.
(An example: Header A includes header B, which includes header C
which includes A. B uses a type defined in A. This is not defined,
because A hasn't defined it yet at the point where it includes B,
and A included from B of course is skipped as we already are
reading A.)
1999-08-19 07:41:39 +08:00
|
|
|
convolve_region (&srcPR, &destPR, horz_deriv, 3, 1, NEGATIVE_CONVOL);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* Get the vertical derivative */
|
|
|
|
destPR.data = maxgrad_conv2;
|
Actually use the enum types GimpImageType, GimpImageBaseType,
* app/*.[ch]: Actually use the enum types GimpImageType,
GimpImageBaseType, LayerModeEffects, PaintApplicationMode,
BrushApplicationMode, GimpFillType and ConvertPaletteType, instead
of just int or gint. Hopefully I catched most of the places
where these should be used.
Add an enum ConvolutionType, suffix the too general constants
NORMAL, ABSOLUTE and NEGATIVE with _CONVOL. Use NORMAL_MODE
instead of NORMAL in some places (this was what was intended). Fix
some minor gccisms.
* app/apptypes.h: New file. This file contains the above
enumeration types, and some opaque struct typedefs. It was
necessary to collect these in one header that doesn't include
other headers, because when we started using the above mentioned
types in the headers, all hell broke loose because of the
spaghetti-like cross-inclusion mess between headers.
(An example: Header A includes header B, which includes header C
which includes A. B uses a type defined in A. This is not defined,
because A hasn't defined it yet at the point where it includes B,
and A included from B of course is skipped as we already are
reading A.)
1999-08-19 07:41:39 +08:00
|
|
|
convolve_region (&srcPR, &destPR, vert_deriv, 3, 1, NEGATIVE_CONVOL);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* calculate overall gradient */
|
|
|
|
tiledata = tile_data_pointer (tile, 0, 0);
|
|
|
|
for (i = 0; i < srcPR.h; i++)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
datah = maxgrad_conv1 + srcPR.rowstride*i;
|
|
|
|
datav = maxgrad_conv2 + srcPR.rowstride*i;
|
1999-10-18 04:28:56 +08:00
|
|
|
gradmap = tiledata + tile_ewidth (tile) * COST_WIDTH * i;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
for (j = 0; j < srcPR.w; j++)
|
|
|
|
{
|
|
|
|
hmax = datah[0] - 128;
|
|
|
|
vmax = datav[0] - 128;
|
|
|
|
for (b = 1; b < srcPR.bytes; b++)
|
|
|
|
{
|
|
|
|
if (abs (datah[b] - 128) > abs (hmax)) hmax = datah[b] - 128;
|
|
|
|
if (abs (datav[b] - 128) > abs (vmax)) vmax = datav[b] - 128;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
if (i == 0 || j == 0 || i == srcPR.h-1 || j == srcPR.w-1)
|
|
|
|
{
|
|
|
|
gradmap[j*COST_WIDTH] = 0;
|
|
|
|
gradmap[j*COST_WIDTH + 1] = 255;
|
|
|
|
goto contin;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* 1 byte absolute magitude first */
|
|
|
|
gradient = sqrt(SQR(hmax) + SQR(vmax));
|
|
|
|
gradmap[j*COST_WIDTH] = gradient * 255 / MAX_GRADIENT;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* then 1 byte direction */
|
|
|
|
if (gradient > MIN_GRADIENT)
|
1998-01-29 16:03:27 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
float direction;
|
|
|
|
if (!hmax)
|
|
|
|
direction = (vmax > 0) ? G_PI_2 : -G_PI_2;
|
|
|
|
else
|
|
|
|
direction = atan ((double) vmax / (double) hmax);
|
|
|
|
/* Scale the direction from between 0 and 254,
|
|
|
|
* corresponding to -PI/2, PI/2 255 is reserved for
|
|
|
|
* directionless pixels */
|
|
|
|
gradmap[j*COST_WIDTH + 1] =
|
|
|
|
(guint8) (254 * (direction + G_PI_2) / G_PI);
|
1998-01-29 16:03:27 +08:00
|
|
|
}
|
1999-08-16 11:43:48 +08:00
|
|
|
else
|
|
|
|
gradmap[j*COST_WIDTH + 1] = 255; /* reserved for weak gradient */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
contin:
|
|
|
|
{
|
1999-10-06 02:05:34 +08:00
|
|
|
#ifdef DEBUG
|
1999-08-16 11:43:48 +08:00
|
|
|
int g = gradmap[j*COST_WIDTH];
|
|
|
|
int d = gradmap[j*COST_WIDTH + 1];
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("%c%c", 'a' + (g * 25 / 255), '0' + (d / 25)));
|
|
|
|
#endif /* DEBUG */
|
1999-08-16 11:43:48 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
datah += srcPR.bytes;
|
|
|
|
datav += srcPR.bytes;
|
|
|
|
}
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("\n"));
|
1999-08-16 11:43:48 +08:00
|
|
|
}
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("\n"));
|
|
|
|
|
|
|
|
tile_release (srctile, FALSE);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
static TileManager *
|
|
|
|
gradient_map_new (GImage *gimage)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
TileManager *tm;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
tm = tile_manager_new (gimage->width, gimage->height,
|
|
|
|
sizeof(guint8) * COST_WIDTH);
|
|
|
|
tile_manager_set_user_data (tm, gimage);
|
|
|
|
tile_manager_set_validate_proc (tm, gradmap_tile_validate);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
return tm;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
1999-08-16 11:43:48 +08:00
|
|
|
find_max_gradient (Iscissors *iscissors, GImage *gimage, int *x, int *y)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
PixelRegion srcPR;
|
|
|
|
int radius;
|
|
|
|
int i, j;
|
|
|
|
int endx, endy;
|
|
|
|
int sx, sy, cx, cy;
|
|
|
|
int x1, y1, x2, y2;
|
|
|
|
void *pr;
|
|
|
|
guint8 *gradient;
|
|
|
|
float g, max_gradient;
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("find_max_gradient(%d, %d)\n", *x, *y));
|
1999-08-16 11:43:48 +08:00
|
|
|
|
|
|
|
/* Initialise the gradient map tile manager for this image if we
|
|
|
|
* don't already have one. */
|
|
|
|
if (!iscissors->gradient_map)
|
|
|
|
iscissors->gradient_map = gradient_map_new (gimage);
|
|
|
|
|
|
|
|
radius = GRADIENT_SEARCH >> 1;
|
|
|
|
|
|
|
|
/* calculate the extent of the search */
|
|
|
|
cx = BOUNDS (*x, 0, gimage->width);
|
|
|
|
cy = BOUNDS (*y, 0, gimage->height);
|
|
|
|
sx = cx - radius;
|
|
|
|
sy = cy - radius;
|
|
|
|
x1 = BOUNDS (cx - radius, 0, gimage->width);
|
|
|
|
y1 = BOUNDS (cy - radius, 0, gimage->height);
|
|
|
|
x2 = BOUNDS (cx + radius, 0, gimage->width);
|
|
|
|
y2 = BOUNDS (cy + radius, 0, gimage->height);
|
|
|
|
/* calculate the factor to multiply the distance from the cursor by */
|
|
|
|
|
|
|
|
max_gradient = 0;
|
|
|
|
*x = cx;
|
|
|
|
*y = cy;
|
|
|
|
|
|
|
|
/* Find the point of max gradient */
|
|
|
|
pixel_region_init (&srcPR, iscissors->gradient_map,
|
|
|
|
x1, y1, x2 - x1, y2 - y1, FALSE);
|
|
|
|
|
|
|
|
/* this iterates over 1, 2 or 4 tiles only */
|
|
|
|
for (pr = pixel_regions_register (1, &srcPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-16 11:43:48 +08:00
|
|
|
endx = srcPR.x + srcPR.w;
|
|
|
|
endy = srcPR.y + srcPR.h;
|
|
|
|
for (i = srcPR.y; i < endy; i++)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-10-18 04:28:56 +08:00
|
|
|
gradient = srcPR.data + srcPR.rowstride * (i - srcPR.y);
|
1999-08-16 11:43:48 +08:00
|
|
|
for (j = srcPR.x; j < endx; j++)
|
|
|
|
{
|
|
|
|
g = *gradient;
|
|
|
|
gradient += COST_WIDTH;
|
|
|
|
g *= distance_weights [(i-y1) * GRADIENT_SEARCH + (j-x1)];
|
|
|
|
if (g > max_gradient)
|
|
|
|
{
|
|
|
|
max_gradient = g;
|
|
|
|
*x = j;
|
|
|
|
*y = i;
|
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-10-06 02:05:34 +08:00
|
|
|
TRC (("done: find_max_gradient(%d, %d)\n", *x, *y));
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-08-16 11:43:48 +08:00
|
|
|
/* End of iscissors.c */
|