mirror of
https://gitlab.gnome.org/GNOME/gimp
synced 2024-10-21 20:12:30 +00:00
even more cleanup, replaced informal comments by API docs.
2005-08-20 Michael Natterer <mitch@gimp.org> * app/base/boundary.[ch]: even more cleanup, replaced informal comments by API docs.
This commit is contained in:
parent
4fb1743f55
commit
eb69eb49dc
|
@ -1,3 +1,8 @@
|
||||||
|
2005-08-20 Michael Natterer <mitch@gimp.org>
|
||||||
|
|
||||||
|
* app/base/boundary.[ch]: even more cleanup, replaced informal
|
||||||
|
comments by API docs.
|
||||||
|
|
||||||
2005-08-20 Michael Natterer <mitch@gimp.org>
|
2005-08-20 Michael Natterer <mitch@gimp.org>
|
||||||
|
|
||||||
* app/base/boundary.[ch]: renamed puclic functions, defines and
|
* app/base/boundary.[ch]: renamed puclic functions, defines and
|
||||||
|
|
|
@ -48,7 +48,7 @@ struct _Boundary
|
||||||
/* The array of vertical segments */
|
/* The array of vertical segments */
|
||||||
gint *vert_segs;
|
gint *vert_segs;
|
||||||
|
|
||||||
/* static empty segment arrays */
|
/* The empty segment arrays */
|
||||||
gint *empty_segs_n;
|
gint *empty_segs_n;
|
||||||
gint *empty_segs_c;
|
gint *empty_segs_c;
|
||||||
gint *empty_segs_l;
|
gint *empty_segs_l;
|
||||||
|
@ -62,6 +62,13 @@ static Boundary * boundary_new (PixelRegion *PR);
|
||||||
static BoundSeg * boundary_free (Boundary *boundary,
|
static BoundSeg * boundary_free (Boundary *boundary,
|
||||||
gboolean free_segs);
|
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,
|
static void find_empty_segs (PixelRegion *maskPR,
|
||||||
gint scanline,
|
gint scanline,
|
||||||
gint empty_segs[],
|
gint empty_segs[],
|
||||||
|
@ -73,12 +80,6 @@ static void find_empty_segs (PixelRegion *maskPR,
|
||||||
gint x2,
|
gint x2,
|
||||||
gint y2,
|
gint y2,
|
||||||
guchar threshold);
|
guchar threshold);
|
||||||
static void make_seg (Boundary *bounrady,
|
|
||||||
gint x1,
|
|
||||||
gint y1,
|
|
||||||
gint x2,
|
|
||||||
gint y2,
|
|
||||||
gboolean open);
|
|
||||||
static void process_horiz_seg (Boundary *boundary,
|
static void process_horiz_seg (Boundary *boundary,
|
||||||
gint x1,
|
gint x1,
|
||||||
gint y1,
|
gint y1,
|
||||||
|
@ -113,6 +114,27 @@ static void simplify_subdivide (const BoundSeg *segs,
|
||||||
|
|
||||||
/* public functions */
|
/* public functions */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
**/
|
||||||
BoundSeg *
|
BoundSeg *
|
||||||
boundary_find (PixelRegion *maskPR,
|
boundary_find (PixelRegion *maskPR,
|
||||||
BoundaryType type,
|
BoundaryType type,
|
||||||
|
@ -125,10 +147,8 @@ boundary_find (PixelRegion *maskPR,
|
||||||
{
|
{
|
||||||
Boundary *boundary;
|
Boundary *boundary;
|
||||||
|
|
||||||
/* The mask paramater can be any PixelRegion. If the region
|
g_return_val_if_fail (maskPR != NULL, NULL);
|
||||||
* has more than 1 bytes/pixel, the last byte of each pixel is
|
g_return_val_if_fail (num_segs != NULL, NULL);
|
||||||
* used to determine the boundary outline.
|
|
||||||
*/
|
|
||||||
|
|
||||||
boundary = generate_boundary (maskPR, type, x1, y1, x2, y2, threshold);
|
boundary = generate_boundary (maskPR, type, x1, y1, x2, y2, threshold);
|
||||||
|
|
||||||
|
@ -137,6 +157,19 @@ boundary_find (PixelRegion *maskPR,
|
||||||
return boundary_free (boundary, FALSE);
|
return boundary_free (boundary, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
**/
|
||||||
BoundSeg *
|
BoundSeg *
|
||||||
boundary_sort (const BoundSeg *segs,
|
boundary_sort (const BoundSeg *segs,
|
||||||
gint num_segs,
|
gint num_segs,
|
||||||
|
@ -147,20 +180,26 @@ boundary_sort (const BoundSeg *segs,
|
||||||
gint index;
|
gint index;
|
||||||
gint x, y;
|
gint x, y;
|
||||||
gint startx, starty;
|
gint startx, starty;
|
||||||
gboolean empty = (num_segs == 0);
|
gboolean empty;
|
||||||
BoundSeg *new_segs;
|
BoundSeg *new_segs;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
*num_groups = 0;
|
||||||
|
|
||||||
|
if (num_segs == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < num_segs; i++)
|
||||||
|
((BoundSeg *) segs)[i].visited = FALSE;
|
||||||
|
|
||||||
boundary = boundary_new (NULL);
|
boundary = boundary_new (NULL);
|
||||||
|
|
||||||
index = 0;
|
index = 0;
|
||||||
new_segs = NULL;
|
new_segs = NULL;
|
||||||
|
empty = FALSE;
|
||||||
for (i = 0; i < num_segs; i++)
|
|
||||||
((BoundSeg *) segs)[i].visited = FALSE;
|
|
||||||
|
|
||||||
boundary->num_segs = 0;
|
|
||||||
|
|
||||||
*num_groups = 0;
|
|
||||||
|
|
||||||
while (! empty)
|
while (! empty)
|
||||||
{
|
{
|
||||||
|
@ -177,10 +216,11 @@ boundary_sort (const BoundSeg *segs,
|
||||||
|
|
||||||
if (! empty)
|
if (! empty)
|
||||||
{
|
{
|
||||||
make_seg (boundary,
|
boundary_add_seg (boundary,
|
||||||
segs[index].x1, segs[index].y1,
|
segs[index].x1, segs[index].y1,
|
||||||
segs[index].x2, segs[index].y2,
|
segs[index].x2, segs[index].y2,
|
||||||
segs[index].open);
|
segs[index].open);
|
||||||
|
|
||||||
((BoundSeg *) segs)[index].visited = TRUE;
|
((BoundSeg *) segs)[index].visited = TRUE;
|
||||||
|
|
||||||
startx = segs[index].x1;
|
startx = segs[index].x1;
|
||||||
|
@ -193,19 +233,19 @@ boundary_sort (const BoundSeg *segs,
|
||||||
/* make sure ordering is correct */
|
/* make sure ordering is correct */
|
||||||
if (x == segs[index].x1 && y == segs[index].y1)
|
if (x == segs[index].x1 && y == segs[index].y1)
|
||||||
{
|
{
|
||||||
make_seg (boundary,
|
boundary_add_seg (boundary,
|
||||||
segs[index].x1, segs[index].y1,
|
segs[index].x1, segs[index].y1,
|
||||||
segs[index].x2, segs[index].y2,
|
segs[index].x2, segs[index].y2,
|
||||||
segs[index].open);
|
segs[index].open);
|
||||||
x = segs[index].x2;
|
x = segs[index].x2;
|
||||||
y = segs[index].y2;
|
y = segs[index].y2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
make_seg (boundary,
|
boundary_add_seg (boundary,
|
||||||
segs[index].x2, segs[index].y2,
|
segs[index].x2, segs[index].y2,
|
||||||
segs[index].x1, segs[index].y1,
|
segs[index].x1, segs[index].y1,
|
||||||
segs[index].open);
|
segs[index].open);
|
||||||
x = segs[index].x1;
|
x = segs[index].x1;
|
||||||
y = segs[index].y1;
|
y = segs[index].y1;
|
||||||
}
|
}
|
||||||
|
@ -214,33 +254,39 @@ boundary_sort (const BoundSeg *segs,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x != startx || y != starty)
|
if (x != startx || y != starty)
|
||||||
g_message ("sort_boundary(): Unconnected boundary group!");
|
g_warning ("sort_boundary(): Unconnected boundary group!");
|
||||||
|
|
||||||
/* Mark the end of a group */
|
/* Mark the end of a group */
|
||||||
*num_groups = *num_groups + 1;
|
*num_groups = *num_groups + 1;
|
||||||
make_seg (boundary, -1, -1, -1, -1, 0);
|
boundary_add_seg (boundary, -1, -1, -1, -1, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return boundary_free (boundary, FALSE);
|
return boundary_free (boundary, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************/
|
/**
|
||||||
/* Reducing the number of points */
|
* boundary_simplify:
|
||||||
/* We expect the Boundary to be */
|
* @sorted_segs: sorted input segs
|
||||||
/* sorted. */
|
* @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.
|
||||||
|
**/
|
||||||
BoundSeg *
|
BoundSeg *
|
||||||
boundary_simplify (BoundSeg *stroke_segs,
|
boundary_simplify (BoundSeg *sorted_segs,
|
||||||
gint num_groups,
|
gint num_groups,
|
||||||
gint *num_segs)
|
gint *num_segs)
|
||||||
{
|
{
|
||||||
GArray *new_bounds;
|
GArray *new_bounds;
|
||||||
GArray *points;
|
gint i, seg;
|
||||||
BoundSeg *ret_bounds;
|
|
||||||
BoundSeg tmp_seg;
|
|
||||||
gint i, j, seg, start, n_points;
|
|
||||||
|
|
||||||
|
g_return_val_if_fail ((sorted_segs == NULL && num_groups == 0) ||
|
||||||
|
(sorted_segs != NULL && num_groups > 0), NULL);
|
||||||
g_return_val_if_fail (num_segs != NULL, NULL);
|
g_return_val_if_fail (num_segs != NULL, NULL);
|
||||||
|
|
||||||
new_bounds = g_array_new (FALSE, FALSE, sizeof (BoundSeg));
|
new_bounds = g_array_new (FALSE, FALSE, sizeof (BoundSeg));
|
||||||
|
@ -249,13 +295,13 @@ boundary_simplify (BoundSeg *stroke_segs,
|
||||||
|
|
||||||
for (i = 0; i < num_groups; i++)
|
for (i = 0; i < num_groups; i++)
|
||||||
{
|
{
|
||||||
start = seg;
|
gint start = seg;
|
||||||
n_points = 0;
|
gint n_points = 0;
|
||||||
|
|
||||||
while (stroke_segs[seg].x1 != -1 ||
|
while (sorted_segs[seg].x1 != -1 ||
|
||||||
stroke_segs[seg].x2 != -1 ||
|
sorted_segs[seg].x2 != -1 ||
|
||||||
stroke_segs[seg].y1 != -1 ||
|
sorted_segs[seg].y1 != -1 ||
|
||||||
stroke_segs[seg].y2 != -1)
|
sorted_segs[seg].y2 != -1)
|
||||||
{
|
{
|
||||||
n_points++;
|
n_points++;
|
||||||
seg++;
|
seg++;
|
||||||
|
@ -263,40 +309,35 @@ boundary_simplify (BoundSeg *stroke_segs,
|
||||||
|
|
||||||
if (n_points > 0)
|
if (n_points > 0)
|
||||||
{
|
{
|
||||||
points = g_array_new (FALSE, FALSE, sizeof (gint));
|
GArray *tmp_points;
|
||||||
|
BoundSeg tmp_seg;
|
||||||
|
gint j;
|
||||||
|
|
||||||
|
tmp_points = g_array_new (FALSE, FALSE, sizeof (gint));
|
||||||
|
|
||||||
/* temporarily use the delimiter to close the polygon */
|
/* temporarily use the delimiter to close the polygon */
|
||||||
tmp_seg = stroke_segs[seg];
|
tmp_seg = sorted_segs[seg];
|
||||||
stroke_segs[seg] = stroke_segs[start];
|
sorted_segs[seg] = sorted_segs[start];
|
||||||
simplify_subdivide (stroke_segs, start, start + n_points,
|
simplify_subdivide (sorted_segs, start, start + n_points,
|
||||||
&points);
|
&tmp_points);
|
||||||
stroke_segs[seg] = tmp_seg;
|
sorted_segs[seg] = tmp_seg;
|
||||||
|
|
||||||
for (j = 0; j < points->len; j++)
|
for (j = 0; j < tmp_points->len; j++)
|
||||||
g_array_append_val (new_bounds,
|
g_array_append_val (new_bounds,
|
||||||
stroke_segs [g_array_index (points, gint, j)]);
|
sorted_segs[g_array_index (tmp_points,
|
||||||
|
gint, j)]);
|
||||||
|
|
||||||
g_array_append_val (new_bounds, stroke_segs[seg]);
|
g_array_append_val (new_bounds, sorted_segs[seg]);
|
||||||
|
|
||||||
g_array_free (points, TRUE);
|
g_array_free (tmp_points, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
seg++;
|
seg++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_bounds->len > 0)
|
*num_segs = new_bounds->len;
|
||||||
{
|
|
||||||
ret_bounds = (BoundSeg *) new_bounds->data;
|
|
||||||
*num_segs = new_bounds->len;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ret_bounds = NULL;
|
|
||||||
*num_segs = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_array_free (new_bounds, FALSE);
|
return (BoundSeg *) g_array_free (new_bounds, FALSE);
|
||||||
|
|
||||||
return ret_bounds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -353,6 +394,29 @@ boundary_free (Boundary *boundary,
|
||||||
return segs;
|
return segs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 ++;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
find_empty_segs (PixelRegion *maskPR,
|
find_empty_segs (PixelRegion *maskPR,
|
||||||
gint scanline,
|
gint scanline,
|
||||||
|
@ -487,6 +551,7 @@ find_empty_segs (PixelRegion *maskPR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*num_empty = l_num_empty;
|
*num_empty = l_num_empty;
|
||||||
|
|
||||||
if (last > 0)
|
if (last > 0)
|
||||||
|
@ -498,29 +563,6 @@ find_empty_segs (PixelRegion *maskPR,
|
||||||
tile_release (tile, FALSE);
|
tile_release (tile, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
make_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 ++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
process_horiz_seg (Boundary *boundary,
|
process_horiz_seg (Boundary *boundary,
|
||||||
gint x1,
|
gint x1,
|
||||||
|
@ -534,7 +576,7 @@ process_horiz_seg (Boundary *boundary,
|
||||||
|
|
||||||
if (boundary->vert_segs[x1] >= 0)
|
if (boundary->vert_segs[x1] >= 0)
|
||||||
{
|
{
|
||||||
make_seg (boundary, x1, boundary->vert_segs[x1], x1, y1, !open);
|
boundary_add_seg (boundary, x1, boundary->vert_segs[x1], x1, y1, !open);
|
||||||
boundary->vert_segs[x1] = -1;
|
boundary->vert_segs[x1] = -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -542,13 +584,13 @@ process_horiz_seg (Boundary *boundary,
|
||||||
|
|
||||||
if (boundary->vert_segs[x2] >= 0)
|
if (boundary->vert_segs[x2] >= 0)
|
||||||
{
|
{
|
||||||
make_seg (boundary, x2, boundary->vert_segs[x2], x2, y2, open);
|
boundary_add_seg (boundary, x2, boundary->vert_segs[x2], x2, y2, open);
|
||||||
boundary->vert_segs[x2] = -1;
|
boundary->vert_segs[x2] = -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
boundary->vert_segs[x2] = y2;
|
boundary->vert_segs[x2] = y2;
|
||||||
|
|
||||||
make_seg (boundary, x1, y1, x2, y2, open);
|
boundary_add_seg (boundary, x1, y1, x2, y2, open);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -657,7 +699,6 @@ generate_boundary (PixelRegion *PR,
|
||||||
return boundary;
|
return boundary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* sorting utility functions */
|
/* sorting utility functions */
|
||||||
|
|
||||||
static gint
|
static gint
|
||||||
|
@ -680,10 +721,10 @@ find_segment (const BoundSeg *segs,
|
||||||
/* simplifying utility functions */
|
/* simplifying utility functions */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
simplify_subdivide (const BoundSeg *segs,
|
simplify_subdivide (const BoundSeg *segs,
|
||||||
gint start_idx,
|
gint start_idx,
|
||||||
gint end_idx,
|
gint end_idx,
|
||||||
GArray **ret_points)
|
GArray **ret_points)
|
||||||
{
|
{
|
||||||
gint maxdist_idx;
|
gint maxdist_idx;
|
||||||
gint dist, maxdist;
|
gint dist, maxdist;
|
||||||
|
|
|
@ -53,7 +53,7 @@ BoundSeg * boundary_find (PixelRegion *maskPR,
|
||||||
BoundSeg * boundary_sort (const BoundSeg *segs,
|
BoundSeg * boundary_sort (const BoundSeg *segs,
|
||||||
gint num_segs,
|
gint num_segs,
|
||||||
gint *num_groups);
|
gint *num_groups);
|
||||||
BoundSeg * boundary_simplify (BoundSeg *stroke_segs,
|
BoundSeg * boundary_simplify (BoundSeg *sorted_segs,
|
||||||
gint num_groups,
|
gint num_groups,
|
||||||
gint *num_segs);
|
gint *num_segs);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue