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
|
|
|
|
*
|
|
|
|
* 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
|
|
|
*/
|
2000-12-17 05:37:03 +08:00
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
#include <stdlib.h>
|
1997-11-25 06:05:25 +08:00
|
|
|
#include <string.h>
|
2000-12-17 05:37:03 +08:00
|
|
|
|
2001-12-08 00:10:53 +08:00
|
|
|
#include <glib-object.h>
|
2000-12-17 05:37:03 +08:00
|
|
|
|
2003-10-01 08:02:48 +08:00
|
|
|
#include "libgimpmath/gimpmath.h"
|
|
|
|
|
2001-05-15 19:25:25 +08:00
|
|
|
#include "base-types.h"
|
2000-12-17 05:37:03 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
#include "boundary.h"
|
2001-05-15 19:25:25 +08:00
|
|
|
#include "pixel-region.h"
|
new ui for the "Layer Offset" dialog.
1999-07-22 Michael Natterer <mitschel@cs.tu-berlin.de>
* app/channel_ops.[ch]: new ui for the "Layer Offset" dialog.
* app/channels_dialog.c
* app/layers_dialog.c: major code cleanup: Folded some callbacks
into common ones, "widget" instead of "w", indentation, ...
* app/commands.c
* app/interface.[ch]
* app/global_edit.c: the query boxes must be shown by the caller
now. There's no need to split up the string for the message box
manually as the Gtk 1.2 label widget handles newlines corectly.
Added the "edge_lock" toggle to the "Shrink Selection" dialog.
Nicer spacings for the query and message boxes.
* app/ink.c: tried to grab the pointer in the blob preview but
failed. Left the code there as a reminder (commented out).
* app/menus.c: reordered <Image>/Select.
I was bored and grep-ed the sources for ancient or deprecated stuff:
* app/about_dialog.[ch]
* app/actionarea.[ch]
* app/app_procs.c
* app/brush_edit.c
* app/brush_select.c
* app/color_select.c
* app/convert.c
* app/devices.c
* app/gdisplay.c
* app/gdisplay_ops.c
* app/histogram_tool.[ch]
* app/info_window.c
* app/install.c
* app/ops_buttons.c
* app/palette.c
* app/palette_select.c
* app/paths_dialog.c
* app/pattern_select.c
* app/resize.c
* app/scale_toolc.c
* app/text_tool.c:
s/container_border_width/container_set_border_width/g,
s/sprintf/g_snprintf/g, replaced some constant string lengths with
strlen(x).
* app/bezier_select.c
* app/blend.c
* app/boundary.c
* app/errors.[ch]
* app/free_select.c
* app/gimpbrushlist.c
* app/gimprc.c
* app/iscissors.c
* app/main.c
* app/patterns.[ch]
* app/text_tool.c: namespace fanaticism: prefixed all gimp error
functions with "gimp_" and formated the messages more uniformly.
* app/gradient.c
* app/gradient_select.c: same stuff as above for the ui
code. There are still some sub-dialogs which need cleanup.
Did some cleanup in most of these files: prototypes, removed tons
of #include's, i18n fixes, s/w/widget/ as above, indentation, ...
1999-07-23 00:21:10 +08:00
|
|
|
#include "tile.h"
|
2001-05-15 19:25:25 +08:00
|
|
|
#include "tile-manager.h"
|
2000-12-29 23:22:01 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* BoundSeg array growth parameter */
|
|
|
|
#define MAX_SEGS_INC 2048
|
|
|
|
|
2000-12-28 10:01:16 +08:00
|
|
|
|
2005-08-20 20:35:54 +08:00
|
|
|
typedef struct _Boundary Boundary;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2005-08-20 20:35:54 +08:00
|
|
|
struct _Boundary
|
|
|
|
{
|
|
|
|
/* The array of segments */
|
2005-08-20 23:46:37 +08:00
|
|
|
BoundSeg *segs;
|
|
|
|
gint num_segs;
|
|
|
|
gint max_segs;
|
|
|
|
|
|
|
|
/* The array of vertical segments */
|
|
|
|
gint *vert_segs;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
/* The empty segment arrays */
|
2005-08-20 23:46:37 +08:00
|
|
|
gint *empty_segs_n;
|
|
|
|
gint *empty_segs_c;
|
|
|
|
gint *empty_segs_l;
|
|
|
|
gint max_empty_segs;
|
2005-08-20 20:35:54 +08:00
|
|
|
};
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
|
|
|
|
/* local function prototypes */
|
|
|
|
|
2008-01-17 04:05:29 +08:00
|
|
|
static Boundary * boundary_new (PixelRegion *PR);
|
|
|
|
static BoundSeg * boundary_free (Boundary *boundary,
|
|
|
|
gboolean free_segs);
|
|
|
|
|
|
|
|
static void boundary_add_seg (Boundary *bounrady,
|
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2,
|
|
|
|
gboolean open);
|
|
|
|
|
|
|
|
static void find_empty_segs (PixelRegion *maskPR,
|
|
|
|
gint scanline,
|
|
|
|
gint empty_segs[],
|
|
|
|
gint max_empty,
|
|
|
|
gint *num_empty,
|
|
|
|
BoundaryType type,
|
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2,
|
|
|
|
guchar threshold);
|
|
|
|
static void process_horiz_seg (Boundary *boundary,
|
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2,
|
|
|
|
gboolean open);
|
|
|
|
static void make_horiz_segs (Boundary *boundary,
|
|
|
|
gint start,
|
|
|
|
gint end,
|
|
|
|
gint scanline,
|
|
|
|
gint empty[],
|
|
|
|
gint num_empty,
|
|
|
|
gint top);
|
|
|
|
static Boundary * generate_boundary (PixelRegion *PR,
|
|
|
|
BoundaryType type,
|
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2,
|
|
|
|
guchar threshold);
|
2007-06-11 15:04:20 +08:00
|
|
|
|
|
|
|
static gint cmp_segptr_xy1_addr (const BoundSeg **seg_ptr_a,
|
|
|
|
const BoundSeg **seg_ptr_b);
|
|
|
|
static gint cmp_segptr_xy2_addr (const BoundSeg **seg_ptr_a,
|
|
|
|
const BoundSeg **seg_ptr_b);
|
|
|
|
|
|
|
|
static gint cmp_segptr_xy1 (const BoundSeg **seg_ptr_a,
|
|
|
|
const BoundSeg **seg_ptr_b);
|
|
|
|
static gint cmp_segptr_xy2 (const BoundSeg **seg_ptr_a,
|
|
|
|
const BoundSeg **seg_ptr_b);
|
|
|
|
|
|
|
|
static const BoundSeg * find_segment (const BoundSeg **segs_by_xy1,
|
|
|
|
const BoundSeg **segs_by_xy2,
|
|
|
|
gint num_segs,
|
|
|
|
gint x,
|
|
|
|
gint y);
|
|
|
|
|
|
|
|
static const BoundSeg * find_segment_with_func (const BoundSeg **segs,
|
|
|
|
gint num_segs,
|
|
|
|
const BoundSeg *search_seg,
|
|
|
|
GCompareFunc cmp_func);
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2008-01-17 04:05:29 +08:00
|
|
|
static void simplify_subdivide (const BoundSeg *segs,
|
|
|
|
gint start_idx,
|
|
|
|
gint end_idx,
|
|
|
|
GArray **ret_points);
|
2005-08-20 23:46:37 +08:00
|
|
|
|
|
|
|
|
|
|
|
/* public functions */
|
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
/**
|
|
|
|
* boundary_find:
|
|
|
|
* @maskPR: any PixelRegion
|
|
|
|
* @type: type of bounds
|
|
|
|
* @x1: left side of bounds
|
|
|
|
* @y1: top side of bounds
|
|
|
|
* @x2: right side of bounds
|
|
|
|
* @y2: botton side of bounds
|
|
|
|
* @threshold: pixel value of boundary line
|
|
|
|
* @num_segs: number of returned #BoundSeg's
|
|
|
|
*
|
|
|
|
* This function returns an array of #BoundSeg's which describe all
|
|
|
|
* outlines along pixel value @threahold, optionally within specified
|
|
|
|
* bounds instead of the whole region.
|
|
|
|
*
|
|
|
|
* The @maskPR paramater can be any PixelRegion. If the region has
|
|
|
|
* more than 1 bytes/pixel, the last byte of each pixel is used to
|
|
|
|
* determine the boundary outline.
|
|
|
|
*
|
|
|
|
* Return value: the boundary array.
|
|
|
|
**/
|
2005-08-20 23:46:37 +08:00
|
|
|
BoundSeg *
|
|
|
|
boundary_find (PixelRegion *maskPR,
|
|
|
|
BoundaryType type,
|
|
|
|
int x1,
|
|
|
|
int y1,
|
|
|
|
int x2,
|
|
|
|
int y2,
|
|
|
|
guchar threshold,
|
|
|
|
int *num_segs)
|
|
|
|
{
|
|
|
|
Boundary *boundary;
|
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
g_return_val_if_fail (maskPR != NULL, NULL);
|
|
|
|
g_return_val_if_fail (num_segs != NULL, NULL);
|
2005-08-20 23:46:37 +08:00
|
|
|
|
|
|
|
boundary = generate_boundary (maskPR, type, x1, y1, x2, y2, threshold);
|
|
|
|
|
|
|
|
*num_segs = boundary->num_segs;
|
|
|
|
|
|
|
|
return boundary_free (boundary, FALSE);
|
|
|
|
}
|
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
/**
|
|
|
|
* boundary_sort:
|
|
|
|
* @segs: unsorted input segs.
|
|
|
|
* @num_segs: number of input segs
|
|
|
|
* @num_groups: number of groups in the sorted segs
|
|
|
|
*
|
|
|
|
* This function takes an array of #BoundSeg's as returned by
|
|
|
|
* boundary_find() and sorts it by contiguous groups. The returned
|
|
|
|
* array contains markers consisting of -1 coordinates and is
|
|
|
|
* @num_groups elements longer than @segs.
|
|
|
|
*
|
|
|
|
* Return value: the sorted segs
|
|
|
|
**/
|
2005-08-20 23:46:37 +08:00
|
|
|
BoundSeg *
|
|
|
|
boundary_sort (const BoundSeg *segs,
|
2006-04-12 20:49:29 +08:00
|
|
|
gint num_segs,
|
|
|
|
gint *num_groups)
|
2005-08-20 23:46:37 +08:00
|
|
|
{
|
2007-06-11 15:12:19 +08:00
|
|
|
Boundary *boundary;
|
|
|
|
const BoundSeg **segs_ptrs_by_xy1;
|
|
|
|
const BoundSeg **segs_ptrs_by_xy2;
|
|
|
|
gint index;
|
|
|
|
gint x, y;
|
|
|
|
gint startx, starty;
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
g_return_val_if_fail ((segs == NULL && num_segs == 0) ||
|
|
|
|
(segs != NULL && num_segs > 0), NULL);
|
|
|
|
g_return_val_if_fail (num_groups != NULL, NULL);
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
*num_groups = 0;
|
|
|
|
|
|
|
|
if (num_segs == 0)
|
|
|
|
return NULL;
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
/* prepare arrays with BoundSeg pointers sorted by xy1 and xy2 accordingly */
|
|
|
|
segs_ptrs_by_xy1 = g_new (const BoundSeg *, num_segs);
|
|
|
|
segs_ptrs_by_xy2 = g_new (const BoundSeg *, num_segs);
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
for (index = 0; index < num_segs; index++)
|
|
|
|
{
|
|
|
|
segs_ptrs_by_xy1[index] = segs + index;
|
|
|
|
segs_ptrs_by_xy2[index] = segs + index;
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort (segs_ptrs_by_xy1, num_segs, sizeof (BoundSeg *),
|
|
|
|
(GCompareFunc) cmp_segptr_xy1_addr);
|
|
|
|
qsort (segs_ptrs_by_xy2, num_segs, sizeof (BoundSeg *),
|
|
|
|
(GCompareFunc) cmp_segptr_xy2_addr);
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
for (index = 0; index < num_segs; index++)
|
|
|
|
((BoundSeg *) segs)[index].visited = FALSE;
|
|
|
|
|
|
|
|
boundary = boundary_new (NULL);
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
for (index = 0; index < num_segs; index++)
|
2005-08-20 23:46:37 +08:00
|
|
|
{
|
2007-06-11 15:04:20 +08:00
|
|
|
const BoundSeg *cur_seg;
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
if (segs[index].visited)
|
|
|
|
continue;
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
boundary_add_seg (boundary,
|
|
|
|
segs[index].x1, segs[index].y1,
|
|
|
|
segs[index].x2, segs[index].y2,
|
|
|
|
segs[index].open);
|
2005-08-21 01:25:19 +08:00
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
((BoundSeg *) segs)[index].visited = TRUE;
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
startx = segs[index].x1;
|
|
|
|
starty = segs[index].y1;
|
|
|
|
x = segs[index].x2;
|
|
|
|
y = segs[index].y2;
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
while ((cur_seg = find_segment (segs_ptrs_by_xy1, segs_ptrs_by_xy2,
|
|
|
|
num_segs, x, y)) != NULL)
|
|
|
|
{
|
|
|
|
/* make sure ordering is correct */
|
|
|
|
if (x == cur_seg->x1 && y == cur_seg->y1)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2007-06-11 15:04:20 +08:00
|
|
|
boundary_add_seg (boundary,
|
|
|
|
cur_seg->x1, cur_seg->y1,
|
|
|
|
cur_seg->x2, cur_seg->y2,
|
|
|
|
cur_seg->open);
|
|
|
|
x = cur_seg->x2;
|
|
|
|
y = cur_seg->y2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
boundary_add_seg (boundary,
|
|
|
|
cur_seg->x2, cur_seg->y2,
|
|
|
|
cur_seg->x1, cur_seg->y1,
|
|
|
|
cur_seg->open);
|
|
|
|
x = cur_seg->x1;
|
|
|
|
y = cur_seg->y1;
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
((BoundSeg *) cur_seg)->visited = TRUE;
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
2007-06-11 15:04:20 +08:00
|
|
|
|
|
|
|
if (G_UNLIKELY (x != startx || y != starty))
|
|
|
|
g_warning ("sort_boundary(): Unconnected boundary group!");
|
|
|
|
|
|
|
|
/* Mark the end of a group */
|
|
|
|
*num_groups = *num_groups + 1;
|
|
|
|
boundary_add_seg (boundary, -1, -1, -1, -1, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (segs_ptrs_by_xy1);
|
|
|
|
g_free (segs_ptrs_by_xy2);
|
2005-08-20 23:46:37 +08:00
|
|
|
|
|
|
|
return boundary_free (boundary, FALSE);
|
|
|
|
}
|
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
/**
|
|
|
|
* boundary_simplify:
|
|
|
|
* @sorted_segs: sorted input segs
|
|
|
|
* @num_groups: number of groups in the sorted segs
|
|
|
|
* @num_segs: number of returned segs.
|
|
|
|
*
|
|
|
|
* This function takes an array of #BoundSeg's which has been sorted
|
|
|
|
* with boundary_sort() and reduces the number of segments while
|
|
|
|
* preserving the general shape as close as possible.
|
|
|
|
*
|
|
|
|
* Return value: the simplified segs.
|
|
|
|
**/
|
2005-08-20 23:46:37 +08:00
|
|
|
BoundSeg *
|
2005-08-21 01:25:19 +08:00
|
|
|
boundary_simplify (BoundSeg *sorted_segs,
|
2005-08-20 23:46:37 +08:00
|
|
|
gint num_groups,
|
|
|
|
gint *num_segs)
|
|
|
|
{
|
2005-08-21 01:25:19 +08:00
|
|
|
GArray *new_bounds;
|
|
|
|
gint i, seg;
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
g_return_val_if_fail ((sorted_segs == NULL && num_groups == 0) ||
|
|
|
|
(sorted_segs != NULL && num_groups > 0), NULL);
|
2005-08-20 23:46:37 +08:00
|
|
|
g_return_val_if_fail (num_segs != NULL, NULL);
|
|
|
|
|
|
|
|
new_bounds = g_array_new (FALSE, FALSE, sizeof (BoundSeg));
|
|
|
|
|
|
|
|
seg = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < num_groups; i++)
|
|
|
|
{
|
2005-08-21 01:25:19 +08:00
|
|
|
gint start = seg;
|
|
|
|
gint n_points = 0;
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
while (sorted_segs[seg].x1 != -1 ||
|
|
|
|
sorted_segs[seg].x2 != -1 ||
|
|
|
|
sorted_segs[seg].y1 != -1 ||
|
|
|
|
sorted_segs[seg].y2 != -1)
|
2005-08-20 23:46:37 +08:00
|
|
|
{
|
|
|
|
n_points++;
|
|
|
|
seg++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n_points > 0)
|
|
|
|
{
|
2005-08-21 01:25:19 +08:00
|
|
|
GArray *tmp_points;
|
|
|
|
BoundSeg tmp_seg;
|
|
|
|
gint j;
|
|
|
|
|
|
|
|
tmp_points = g_array_new (FALSE, FALSE, sizeof (gint));
|
2005-08-20 23:46:37 +08:00
|
|
|
|
|
|
|
/* temporarily use the delimiter to close the polygon */
|
2005-08-21 01:25:19 +08:00
|
|
|
tmp_seg = sorted_segs[seg];
|
|
|
|
sorted_segs[seg] = sorted_segs[start];
|
2008-01-17 04:05:29 +08:00
|
|
|
simplify_subdivide (sorted_segs,
|
|
|
|
start, start + n_points, &tmp_points);
|
2005-08-21 01:25:19 +08:00
|
|
|
sorted_segs[seg] = tmp_seg;
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
for (j = 0; j < tmp_points->len; j++)
|
2005-08-20 23:46:37 +08:00
|
|
|
g_array_append_val (new_bounds,
|
2005-08-21 01:25:19 +08:00
|
|
|
sorted_segs[g_array_index (tmp_points,
|
|
|
|
gint, j)]);
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
g_array_append_val (new_bounds, sorted_segs[seg]);
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
g_array_free (tmp_points, TRUE);
|
2005-08-20 23:46:37 +08:00
|
|
|
}
|
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
seg++;
|
2005-08-20 23:46:37 +08:00
|
|
|
}
|
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
*num_segs = new_bounds->len;
|
2005-08-20 23:46:37 +08:00
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
return (BoundSeg *) g_array_free (new_bounds, FALSE);
|
2005-08-20 23:46:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* private functions */
|
|
|
|
|
|
|
|
static Boundary *
|
|
|
|
boundary_new (PixelRegion *PR)
|
2005-08-20 20:35:54 +08:00
|
|
|
{
|
2007-05-22 18:43:48 +08:00
|
|
|
Boundary *boundary = g_slice_new0 (Boundary);
|
2005-08-20 20:35:54 +08:00
|
|
|
|
2005-08-20 23:46:37 +08:00
|
|
|
if (PR)
|
|
|
|
{
|
|
|
|
gint i;
|
2005-08-20 20:35:54 +08:00
|
|
|
|
2005-08-20 23:46:37 +08:00
|
|
|
/* array for determining the vertical line segments
|
|
|
|
* which must be drawn
|
|
|
|
*/
|
|
|
|
boundary->vert_segs = g_new (gint, PR->w + PR->x + 1);
|
2005-08-20 20:35:54 +08:00
|
|
|
|
2005-08-20 23:46:37 +08:00
|
|
|
for (i = 0; i <= (PR->w + PR->x); i++)
|
|
|
|
boundary->vert_segs[i] = -1;
|
2005-08-20 20:35:54 +08:00
|
|
|
|
2005-08-20 23:46:37 +08:00
|
|
|
/* find the maximum possible number of empty segments
|
|
|
|
* given the current mask
|
|
|
|
*/
|
|
|
|
boundary->max_empty_segs = PR->w + 3;
|
|
|
|
|
|
|
|
boundary->empty_segs_n = g_new (gint, boundary->max_empty_segs);
|
|
|
|
boundary->empty_segs_c = g_new (gint, boundary->max_empty_segs);
|
|
|
|
boundary->empty_segs_l = g_new (gint, boundary->max_empty_segs);
|
|
|
|
}
|
|
|
|
|
|
|
|
return boundary;
|
|
|
|
}
|
|
|
|
|
|
|
|
static BoundSeg *
|
|
|
|
boundary_free (Boundary *boundary,
|
|
|
|
gboolean free_segs)
|
|
|
|
{
|
|
|
|
BoundSeg *segs = NULL;
|
|
|
|
|
|
|
|
if (free_segs)
|
|
|
|
g_free (boundary->segs);
|
|
|
|
else
|
|
|
|
segs = boundary->segs;
|
|
|
|
|
|
|
|
g_free (boundary->vert_segs);
|
|
|
|
g_free (boundary->empty_segs_n);
|
|
|
|
g_free (boundary->empty_segs_c);
|
|
|
|
g_free (boundary->empty_segs_l);
|
2005-08-20 20:35:54 +08:00
|
|
|
|
2007-05-22 18:43:48 +08:00
|
|
|
g_slice_free (Boundary, boundary);
|
2005-08-20 23:46:37 +08:00
|
|
|
|
|
|
|
return segs;
|
2005-08-20 20:35:54 +08:00
|
|
|
}
|
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
static void
|
|
|
|
boundary_add_seg (Boundary *boundary,
|
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2,
|
|
|
|
gboolean open)
|
|
|
|
{
|
|
|
|
if (boundary->num_segs >= boundary->max_segs)
|
|
|
|
{
|
|
|
|
boundary->max_segs += MAX_SEGS_INC;
|
|
|
|
|
|
|
|
boundary->segs = g_renew (BoundSeg, boundary->segs, boundary->max_segs);
|
|
|
|
}
|
|
|
|
|
|
|
|
boundary->segs[boundary->num_segs].x1 = x1;
|
|
|
|
boundary->segs[boundary->num_segs].y1 = y1;
|
|
|
|
boundary->segs[boundary->num_segs].x2 = x2;
|
|
|
|
boundary->segs[boundary->num_segs].y2 = y2;
|
|
|
|
boundary->segs[boundary->num_segs].open = open;
|
|
|
|
boundary->num_segs ++;
|
|
|
|
}
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static void
|
|
|
|
find_empty_segs (PixelRegion *maskPR,
|
2006-04-12 20:49:29 +08:00
|
|
|
gint scanline,
|
|
|
|
gint empty_segs[],
|
|
|
|
gint max_empty,
|
|
|
|
gint *num_empty,
|
|
|
|
BoundaryType type,
|
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2,
|
2003-07-10 19:59:38 +08:00
|
|
|
guchar threshold)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2006-07-06 19:13:26 +08:00
|
|
|
const guchar *data = NULL;
|
|
|
|
Tile *tile = NULL;
|
|
|
|
gint start = 0;
|
|
|
|
gint end = 0;
|
|
|
|
gint endx = 0;
|
|
|
|
gint dstep = 0;
|
|
|
|
gint val, last;
|
|
|
|
gint x, tilex;
|
|
|
|
gint l_num_empty;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
*num_empty = 0;
|
|
|
|
|
|
|
|
if (scanline < maskPR->y || scanline >= (maskPR->y + maskPR->h))
|
|
|
|
{
|
|
|
|
empty_segs[(*num_empty)++] = 0;
|
|
|
|
empty_segs[(*num_empty)++] = G_MAXINT;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-08-20 23:46:37 +08:00
|
|
|
if (type == BOUNDARY_WITHIN_BOUNDS)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
if (scanline < y1 || scanline >= y2)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
|
|
|
empty_segs[(*num_empty)++] = 0;
|
|
|
|
empty_segs[(*num_empty)++] = G_MAXINT;
|
|
|
|
return;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
start = x1;
|
2003-07-10 19:59:38 +08:00
|
|
|
end = x2;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
2005-08-20 23:46:37 +08:00
|
|
|
else if (type == BOUNDARY_IGNORE_BOUNDS)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
start = maskPR->x;
|
2003-07-10 19:59:38 +08:00
|
|
|
end = maskPR->x + maskPR->w;
|
1997-11-25 06:05:25 +08:00
|
|
|
if (scanline < y1 || scanline >= y2)
|
2006-04-12 20:49:29 +08:00
|
|
|
x2 = -1;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
tilex = -1;
|
|
|
|
empty_segs[(*num_empty)++] = 0;
|
|
|
|
last = -1;
|
|
|
|
|
1999-04-09 14:00:11 +08:00
|
|
|
l_num_empty = *num_empty;
|
|
|
|
|
2003-07-10 19:59:38 +08:00
|
|
|
if (! maskPR->tiles)
|
|
|
|
{
|
|
|
|
data = maskPR->data + scanline * maskPR->rowstride;
|
|
|
|
dstep = maskPR->bytes;
|
|
|
|
|
|
|
|
endx = end;
|
|
|
|
}
|
|
|
|
|
1999-04-09 14:00:11 +08:00
|
|
|
for (x = start; x < end;)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
/* Check to see if we must advance to next tile */
|
2003-07-10 19:59:38 +08:00
|
|
|
if (maskPR->tiles)
|
|
|
|
{
|
|
|
|
if ((x / TILE_WIDTH) != tilex)
|
|
|
|
{
|
|
|
|
if (tile)
|
|
|
|
tile_release (tile, FALSE);
|
2006-07-06 19:13:26 +08:00
|
|
|
|
2003-07-11 18:00:24 +08:00
|
|
|
tile = tile_manager_get_tile (maskPR->tiles,
|
2006-04-12 20:49:29 +08:00
|
|
|
x, scanline, TRUE, FALSE);
|
2007-09-13 02:29:11 +08:00
|
|
|
data = ((const guchar *) tile_data_pointer (tile, x, scanline) +
|
|
|
|
tile_bpp (tile) - 1);
|
2003-07-10 19:59:38 +08:00
|
|
|
|
|
|
|
tilex = x / TILE_WIDTH;
|
|
|
|
dstep = tile_bpp (tile);
|
|
|
|
}
|
|
|
|
|
|
|
|
endx = x + (TILE_WIDTH - (x%TILE_WIDTH));
|
|
|
|
endx = MIN (end, endx);
|
|
|
|
}
|
|
|
|
|
2005-08-20 23:46:37 +08:00
|
|
|
if (type == BOUNDARY_IGNORE_BOUNDS && (endx > x1 || x < x2))
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
|
|
|
for (; x < endx; x++)
|
|
|
|
{
|
|
|
|
if (*data > threshold)
|
|
|
|
if (x >= x1 && x < x2)
|
|
|
|
val = -1;
|
|
|
|
else
|
|
|
|
val = 1;
|
|
|
|
else
|
|
|
|
val = -1;
|
|
|
|
|
|
|
|
data += dstep;
|
|
|
|
|
|
|
|
if (last != val)
|
|
|
|
empty_segs[l_num_empty++] = x;
|
|
|
|
|
|
|
|
last = val;
|
|
|
|
}
|
|
|
|
}
|
1999-04-09 14:00:11 +08:00
|
|
|
else
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
|
|
|
for (; x < endx; x++)
|
|
|
|
{
|
|
|
|
if (*data > threshold)
|
|
|
|
val = 1;
|
|
|
|
else
|
|
|
|
val = -1;
|
|
|
|
|
|
|
|
data += dstep;
|
|
|
|
|
|
|
|
if (last != val)
|
|
|
|
empty_segs[l_num_empty++] = x;
|
|
|
|
|
|
|
|
last = val;
|
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
2005-08-21 01:25:19 +08:00
|
|
|
|
1999-04-09 14:00:11 +08:00
|
|
|
*num_empty = l_num_empty;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
if (last > 0)
|
|
|
|
empty_segs[(*num_empty)++] = x;
|
|
|
|
|
|
|
|
empty_segs[(*num_empty)++] = G_MAXINT;
|
|
|
|
|
|
|
|
if (tile)
|
1998-07-10 10:43:12 +08:00
|
|
|
tile_release (tile, FALSE);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-08-20 20:35:54 +08:00
|
|
|
process_horiz_seg (Boundary *boundary,
|
|
|
|
gint x1,
|
2006-04-12 20:49:29 +08:00
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2,
|
|
|
|
gboolean open)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
/* This procedure accounts for any vertical segments that must be
|
|
|
|
drawn to close in the horizontal segments. */
|
|
|
|
|
2005-08-20 20:35:54 +08:00
|
|
|
if (boundary->vert_segs[x1] >= 0)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2005-08-21 01:25:19 +08:00
|
|
|
boundary_add_seg (boundary, x1, boundary->vert_segs[x1], x1, y1, !open);
|
2005-08-20 20:35:54 +08:00
|
|
|
boundary->vert_segs[x1] = -1;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
else
|
2005-08-20 20:35:54 +08:00
|
|
|
boundary->vert_segs[x1] = y1;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2005-08-20 20:35:54 +08:00
|
|
|
if (boundary->vert_segs[x2] >= 0)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2005-08-21 01:25:19 +08:00
|
|
|
boundary_add_seg (boundary, x2, boundary->vert_segs[x2], x2, y2, open);
|
2005-08-20 20:35:54 +08:00
|
|
|
boundary->vert_segs[x2] = -1;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
else
|
2005-08-20 20:35:54 +08:00
|
|
|
boundary->vert_segs[x2] = y2;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2005-08-21 01:25:19 +08:00
|
|
|
boundary_add_seg (boundary, x1, y1, x2, y2, open);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-08-20 20:35:54 +08:00
|
|
|
make_horiz_segs (Boundary *boundary,
|
|
|
|
gint start,
|
2006-04-12 20:49:29 +08:00
|
|
|
gint end,
|
|
|
|
gint scanline,
|
|
|
|
gint empty[],
|
|
|
|
gint num_empty,
|
|
|
|
gint top)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2000-12-28 10:01:16 +08:00
|
|
|
gint empty_index;
|
|
|
|
gint e_s, e_e; /* empty segment start and end values */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
for (empty_index = 0; empty_index < num_empty; empty_index += 2)
|
|
|
|
{
|
|
|
|
e_s = *empty++;
|
|
|
|
e_e = *empty++;
|
2005-08-20 20:35:54 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
if (e_s <= start && e_e >= end)
|
2006-04-12 20:49:29 +08:00
|
|
|
process_horiz_seg (boundary,
|
2005-08-20 20:35:54 +08:00
|
|
|
start, scanline, end, scanline, top);
|
1997-11-25 06:05:25 +08:00
|
|
|
else if ((e_s > start && e_s < end) ||
|
2006-04-12 20:49:29 +08:00
|
|
|
(e_e < end && e_e > start))
|
|
|
|
process_horiz_seg (boundary,
|
2005-08-20 20:35:54 +08:00
|
|
|
MAX (e_s, start), scanline,
|
2006-04-12 20:49:29 +08:00
|
|
|
MIN (e_e, end), scanline, top);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-08-20 23:46:37 +08:00
|
|
|
static Boundary *
|
|
|
|
generate_boundary (PixelRegion *PR,
|
2006-04-12 20:49:29 +08:00
|
|
|
BoundaryType type,
|
|
|
|
gint x1,
|
|
|
|
gint y1,
|
|
|
|
gint x2,
|
|
|
|
gint y2,
|
2003-07-10 19:59:38 +08:00
|
|
|
guchar threshold)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2005-08-20 23:46:37 +08:00
|
|
|
Boundary *boundary;
|
|
|
|
gint scanline;
|
|
|
|
gint i;
|
|
|
|
gint start, end;
|
|
|
|
gint *tmp_segs;
|
|
|
|
|
|
|
|
gint num_empty_n = 0;
|
|
|
|
gint num_empty_c = 0;
|
|
|
|
gint num_empty_l = 0;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2005-08-20 23:46:37 +08:00
|
|
|
boundary = boundary_new (PR);
|
2001-07-09 05:44:52 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
start = 0;
|
|
|
|
end = 0;
|
|
|
|
|
2005-08-20 23:46:37 +08:00
|
|
|
if (type == BOUNDARY_WITHIN_BOUNDS)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
start = y1;
|
2005-08-20 20:35:54 +08:00
|
|
|
end = y2;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
2005-08-20 23:46:37 +08:00
|
|
|
else if (type == BOUNDARY_IGNORE_BOUNDS)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2001-07-09 05:44:52 +08:00
|
|
|
start = PR->y;
|
|
|
|
end = PR->y + PR->h;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the empty segments for the previous and current scanlines */
|
2005-08-20 20:35:54 +08:00
|
|
|
find_empty_segs (PR, start - 1, boundary->empty_segs_l,
|
2006-04-12 20:49:29 +08:00
|
|
|
boundary->max_empty_segs, &num_empty_l,
|
|
|
|
type, x1, y1, x2, y2,
|
2003-07-10 19:59:38 +08:00
|
|
|
threshold);
|
2005-08-20 20:35:54 +08:00
|
|
|
find_empty_segs (PR, start, boundary->empty_segs_c,
|
2006-04-12 20:49:29 +08:00
|
|
|
boundary->max_empty_segs, &num_empty_c,
|
|
|
|
type, x1, y1, x2, y2,
|
2003-07-10 19:59:38 +08:00
|
|
|
threshold);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
for (scanline = start; scanline < end; scanline++)
|
|
|
|
{
|
|
|
|
/* find the empty segment list for the next scanline */
|
2005-08-20 20:35:54 +08:00
|
|
|
find_empty_segs (PR, scanline + 1, boundary->empty_segs_n,
|
2006-04-12 20:49:29 +08:00
|
|
|
boundary->max_empty_segs, &num_empty_n,
|
|
|
|
type, x1, y1, x2, y2,
|
2003-07-10 19:59:38 +08:00
|
|
|
threshold);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* process the segments on the current scanline */
|
|
|
|
for (i = 1; i < num_empty_c - 1; i += 2)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
|
|
|
make_horiz_segs (boundary,
|
2005-08-20 20:35:54 +08:00
|
|
|
boundary->empty_segs_c [i],
|
|
|
|
boundary->empty_segs_c [i+1],
|
2007-06-11 15:04:20 +08:00
|
|
|
scanline,
|
|
|
|
boundary->empty_segs_l, num_empty_l, 1);
|
2006-04-12 20:49:29 +08:00
|
|
|
make_horiz_segs (boundary,
|
2005-08-20 20:35:54 +08:00
|
|
|
boundary->empty_segs_c [i],
|
|
|
|
boundary->empty_segs_c [i+1],
|
2007-06-11 15:04:20 +08:00
|
|
|
scanline + 1,
|
|
|
|
boundary->empty_segs_n, num_empty_n, 0);
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* get the next scanline of empty segments, swap others */
|
2005-08-20 20:35:54 +08:00
|
|
|
tmp_segs = boundary->empty_segs_l;
|
|
|
|
boundary->empty_segs_l = boundary->empty_segs_c;
|
|
|
|
num_empty_l = num_empty_c;
|
|
|
|
boundary->empty_segs_c = boundary->empty_segs_n;
|
|
|
|
num_empty_c = num_empty_n;
|
|
|
|
boundary->empty_segs_n = tmp_segs;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
2005-08-20 20:35:54 +08:00
|
|
|
|
2005-08-20 23:46:37 +08:00
|
|
|
return boundary;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
2005-08-20 23:46:37 +08:00
|
|
|
/* sorting utility functions */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2008-01-17 04:05:29 +08:00
|
|
|
static inline gint
|
|
|
|
cmp_xy (const gint ax,
|
|
|
|
const gint ay,
|
|
|
|
const gint bx,
|
|
|
|
const gint by)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2008-01-17 04:05:29 +08:00
|
|
|
if (ay < by)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (ay > by)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else if (ax < bx)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (ax > bx)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2007-06-11 15:04:20 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
/*
|
|
|
|
* Compares (x1, y1) pairs in specified segments, using their addresses if
|
|
|
|
* (x1, y1) pairs are equal.
|
|
|
|
*/
|
|
|
|
static gint
|
|
|
|
cmp_segptr_xy1_addr (const BoundSeg **seg_ptr_a,
|
|
|
|
const BoundSeg **seg_ptr_b)
|
|
|
|
{
|
|
|
|
const BoundSeg *seg_a = *seg_ptr_a;
|
|
|
|
const BoundSeg *seg_b = *seg_ptr_b;
|
|
|
|
|
|
|
|
gint result = cmp_xy (seg_a->x1, seg_a->y1, seg_b->x1, seg_b->y1);
|
|
|
|
|
|
|
|
if (result == 0)
|
|
|
|
{
|
|
|
|
if (seg_a < seg_b)
|
|
|
|
result = -1;
|
|
|
|
else if (seg_a > seg_b)
|
|
|
|
result = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compares (x2, y2) pairs in specified segments, using their addresses if
|
|
|
|
* (x2, y2) pairs are equal.
|
|
|
|
*/
|
|
|
|
static gint
|
|
|
|
cmp_segptr_xy2_addr (const BoundSeg **seg_ptr_a,
|
|
|
|
const BoundSeg **seg_ptr_b)
|
|
|
|
{
|
|
|
|
const BoundSeg *seg_a = *seg_ptr_a;
|
|
|
|
const BoundSeg *seg_b = *seg_ptr_b;
|
|
|
|
|
|
|
|
gint result = cmp_xy (seg_a->x2, seg_a->y2, seg_b->x2, seg_b->y2);
|
|
|
|
|
|
|
|
if (result == 0)
|
|
|
|
{
|
|
|
|
if (seg_a < seg_b)
|
|
|
|
result = -1;
|
|
|
|
else if (seg_a > seg_b)
|
|
|
|
result = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compares (x1, y1) pairs in specified segments.
|
|
|
|
*/
|
|
|
|
static gint
|
|
|
|
cmp_segptr_xy1 (const BoundSeg **seg_ptr_a, const BoundSeg **seg_ptr_b)
|
|
|
|
{
|
|
|
|
const BoundSeg *seg_a = *seg_ptr_a, *seg_b = *seg_ptr_b;
|
|
|
|
|
2008-01-17 04:05:29 +08:00
|
|
|
return cmp_xy (seg_a->x1, seg_a->y1, seg_b->x1, seg_b->y1);
|
2007-06-11 15:04:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compares (x2, y2) pairs in specified segments.
|
|
|
|
*/
|
|
|
|
static gint
|
|
|
|
cmp_segptr_xy2 (const BoundSeg **seg_ptr_a,
|
|
|
|
const BoundSeg **seg_ptr_b)
|
|
|
|
{
|
|
|
|
const BoundSeg *seg_a = *seg_ptr_a;
|
|
|
|
const BoundSeg *seg_b = *seg_ptr_b;
|
|
|
|
|
|
|
|
return cmp_xy (seg_a->x2, seg_a->y2, seg_b->x2, seg_b->y2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const BoundSeg *
|
|
|
|
find_segment (const BoundSeg **segs_by_xy1,
|
|
|
|
const BoundSeg **segs_by_xy2,
|
|
|
|
gint num_segs,
|
|
|
|
gint x,
|
|
|
|
gint y)
|
|
|
|
{
|
|
|
|
const BoundSeg *segptr_xy1;
|
|
|
|
const BoundSeg *segptr_xy2;
|
|
|
|
BoundSeg search_seg;
|
|
|
|
|
|
|
|
search_seg.x1 = search_seg.x2 = x;
|
|
|
|
search_seg.y1 = search_seg.y2 = y;
|
|
|
|
|
|
|
|
segptr_xy1 = find_segment_with_func (segs_by_xy1, num_segs, &search_seg,
|
|
|
|
(GCompareFunc) cmp_segptr_xy1);
|
|
|
|
segptr_xy2 = find_segment_with_func (segs_by_xy2, num_segs, &search_seg,
|
|
|
|
(GCompareFunc) cmp_segptr_xy2);
|
|
|
|
|
|
|
|
/* return segment with smaller address */
|
|
|
|
if (segptr_xy1 != NULL && segptr_xy2 != NULL)
|
|
|
|
return MIN(segptr_xy1, segptr_xy2);
|
|
|
|
else if (segptr_xy1 != NULL)
|
|
|
|
return segptr_xy1;
|
|
|
|
else if (segptr_xy2 != NULL)
|
|
|
|
return segptr_xy2;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const BoundSeg *
|
|
|
|
find_segment_with_func (const BoundSeg **segs,
|
|
|
|
gint num_segs,
|
|
|
|
const BoundSeg *search_seg,
|
|
|
|
GCompareFunc cmp_func)
|
|
|
|
{
|
|
|
|
const BoundSeg **seg;
|
|
|
|
const BoundSeg *found_seg = NULL;
|
|
|
|
|
|
|
|
seg = bsearch (&search_seg, segs, num_segs, sizeof (BoundSeg *), cmp_func);
|
|
|
|
|
|
|
|
if (seg != NULL)
|
|
|
|
{
|
|
|
|
/* find first matching segment */
|
|
|
|
while (seg > segs && cmp_func (seg - 1, &search_seg) == 0)
|
|
|
|
seg--;
|
|
|
|
|
|
|
|
/* find first non-visited segment */
|
|
|
|
while (seg != segs + num_segs && cmp_func (seg, &search_seg) == 0)
|
|
|
|
if (!(*seg)->visited)
|
|
|
|
{
|
|
|
|
found_seg = *seg;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
seg++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return found_seg;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
2007-06-11 15:04:20 +08:00
|
|
|
|
2005-08-20 23:46:37 +08:00
|
|
|
/* simplifying utility functions */
|
2003-10-01 08:02:48 +08:00
|
|
|
|
|
|
|
static void
|
2005-08-21 01:25:19 +08:00
|
|
|
simplify_subdivide (const BoundSeg *segs,
|
|
|
|
gint start_idx,
|
|
|
|
gint end_idx,
|
|
|
|
GArray **ret_points)
|
2003-10-01 08:02:48 +08:00
|
|
|
{
|
2007-06-12 18:20:36 +08:00
|
|
|
gint maxdist_idx;
|
|
|
|
gint maxdist;
|
|
|
|
gint threshold;
|
|
|
|
gint i, dx, dy;
|
2003-10-01 08:02:48 +08:00
|
|
|
|
|
|
|
if (end_idx - start_idx < 2)
|
|
|
|
{
|
|
|
|
*ret_points = g_array_append_val (*ret_points, start_idx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
maxdist = 0;
|
|
|
|
maxdist_idx = -1;
|
|
|
|
|
|
|
|
if (segs[start_idx].x1 == segs[end_idx].x1 &&
|
|
|
|
segs[start_idx].y1 == segs[end_idx].y1)
|
|
|
|
{
|
|
|
|
/* start and endpoint are at the same coordinates */
|
|
|
|
for (i = start_idx + 1; i < end_idx; i++)
|
|
|
|
{
|
|
|
|
/* compare the sqared distances */
|
2007-06-12 18:20:36 +08:00
|
|
|
gint dist = (SQR (segs[i].x1 - segs[start_idx].x1) +
|
|
|
|
SQR (segs[i].y1 - segs[start_idx].y1));
|
2006-07-06 19:13:26 +08:00
|
|
|
|
2003-10-01 08:02:48 +08:00
|
|
|
if (dist > maxdist)
|
|
|
|
{
|
|
|
|
maxdist = dist;
|
|
|
|
maxdist_idx = i;
|
|
|
|
}
|
|
|
|
}
|
2006-07-06 19:13:26 +08:00
|
|
|
|
2007-06-12 18:20:36 +08:00
|
|
|
threshold = 1;
|
2003-10-01 08:02:48 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dx = segs[end_idx].x1 - segs[start_idx].x1;
|
|
|
|
dy = segs[end_idx].y1 - segs[start_idx].y1;
|
2005-08-20 20:35:54 +08:00
|
|
|
|
2003-10-01 08:02:48 +08:00
|
|
|
for (i = start_idx + 1; i < end_idx; i++)
|
|
|
|
{
|
|
|
|
/* this is not really the euclidic distance, but is
|
|
|
|
* proportional for this part of the line
|
|
|
|
* (for the real distance we'd have to divide by
|
|
|
|
* (SQR(dx)+SQR(dy)))
|
|
|
|
*/
|
2007-06-12 18:20:36 +08:00
|
|
|
gint dist = abs (dx * (segs[start_idx].y1 - segs[i].y1) -
|
|
|
|
dy * (segs[start_idx].x1 - segs[i].x1));
|
2003-10-01 08:02:48 +08:00
|
|
|
|
|
|
|
if (dist > maxdist)
|
|
|
|
{
|
|
|
|
maxdist = dist;
|
|
|
|
maxdist_idx = i;
|
|
|
|
}
|
|
|
|
}
|
2006-07-06 19:13:26 +08:00
|
|
|
|
2007-06-12 18:20:36 +08:00
|
|
|
/* threshold is chosen to catch 45 degree stairs */
|
|
|
|
threshold = SQR (dx) + SQR (dy);
|
2003-10-01 08:02:48 +08:00
|
|
|
}
|
2005-08-20 20:35:54 +08:00
|
|
|
|
2007-06-12 18:20:36 +08:00
|
|
|
if (maxdist <= threshold)
|
2003-10-01 08:02:48 +08:00
|
|
|
{
|
|
|
|
*ret_points = g_array_append_val (*ret_points, start_idx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Simons hack */
|
|
|
|
maxdist_idx = (start_idx + end_idx) / 2;
|
|
|
|
|
|
|
|
simplify_subdivide (segs, start_idx, maxdist_idx, ret_points);
|
|
|
|
simplify_subdivide (segs, maxdist_idx, end_idx, ret_points);
|
|
|
|
}
|