gimp/app/base/boundary.c

808 lines
23 KiB
C
Raw Normal View History

/* 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
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1997-11-25 06:05:25 +08:00
*/
#include "config.h"
1997-11-25 06:05:25 +08:00
#include <string.h>
removed from CVS, they are generated. 2001-12-07 Sven Neumann <sven@gimp.org> * app/core/gimpmarshal.[ch]: removed from CVS, they are generated. * app/base/Makefile.am * app/base/base-enums.h: new file defining enums that are to be registered. Used to build app/base/base-enums.c. * app/base/base-types.h: include base-enums.h. * tools/pdbgen/Makefile.am * tools/pdbgen/enumcode.pl * tools/pdbgen/enums.pl: parse the new base-enums.h file and modified the perl voodoo so it doesn't prefix enums with GIMP_ that are already properly namespaced. * app/core/core-types.h: don't need to chop GIMP from enum. * app/pdb/color_cmds.c * app/pdb/tools_cmds.c * libgimp/gimpenums.h * plug-ins/script-fu/script-fu-constants.c: regenerated. * app/config/gimpconfig-deserialize.[ch] * app/config/gimpconfig-serialize.[ch] * app/config/gimpconfig.[ch]: made GimpConfig an interface including a reasonable default implementation that works on object properties. * app/config/Makefile.am * app/config/gimpbaseconfig.[ch]: new GimpBaseConfig using the GimpConfig interface. Yet only used for testing from app/main.c. * app/main.c: test the new GimpBaseConfig object. * app/gimprc.c * app/base/base-config.h * app/base/*.c * app/core/gimpdatafiles.c * app/core/gimpdrawable-transform.c * app/core/gimppreviewcache.c * app/gui/preferences-dialog.c * app/paint-funcs/paint-funcs.c * app/xcf/xcf-seek.c: need to include glib-object.h since base-config contains registered enums now. Follow name change of InterpolationType to GimpInterpolationType.
2001-12-08 00:10:53 +08:00
#include <glib-object.h>
#include "libgimpmath/gimpmath.h"
new directory app/base/ 2001-05-15 Michael Natterer <mitch@gimp.org> * configure.in: new directory app/base/ * app/Makefile.am * app/boundary.[ch] * app/brush_scale.[ch] * app/gimpchecks.h * app/gimplut.[ch] * app/pixel_processor.[ch] * app/pixel_region.[ch] * app/pixel_surround.[ch] * app/temp_buf.[ch] * app/tile.[ch] * app/tile_cache.[ch] * app/tile_manager.[ch] * app/tile_manager_pvt.h * app/tile_pvt.h * app/tile_swap.[ch]: moved to base/ * app/base/Makefile.am * app/base/base-types.h * app/base/*: new directory for the sub-object pixel maniplation and storage stuff. Does not include Gtk+ or anything outside base/. Did some cleanup in all files. * app/appenums.h * app/apptypes.h * app/core/gimpimage.h: removed types which are now in base/base-types.h. * app/base/base-config.[ch] * app/gimprc.[ch]: put the config variables for base/ to their own file so base/ doesn not have to include gimprc.h (does not yet work, i.e. the variables are un-configurable right now) * app/main.c: set a log handler for "Gimp-Base". * app/paint-funcs/Makefile.am * app/paint-funcs/paint-funcs.[ch]: removed the color hash which maps RGB to color indices because it's a totally standalone system which has nothing to do with the paint-funcs and introduced a GimpImage dependency. paint-funcs/ should be considered on the same sub-object (glib-only) level as base/, only in a different directory. * app/core/Makefile.am * app/core/gimpimage-colorhash.[ch]: put the color hash here. * app/gimage.c: don't invalidate the color hash here... * app/core/gimpimage.c: ... but in the colormap_changed() default inplementation. Initialize the hash in class_init(). * tools/pdbgen/Makefile.am: scan app/base/base-types.h for enums. * tools/pdbgen/enums.pl: regenerated. * app/[lots] * app/core/[of] * app/gui/[files] * app/pdb/[all] * app/tools/[over] * app/widgets/[the] * tools/pdbgen/pdb/[place]: changed #includes accordingly. And use base_config->value instead of the stuff from gimprc.h.
2001-05-15 19:25:25 +08:00
#include "base-types.h"
1997-11-25 06:05:25 +08:00
#include "boundary.h"
new directory app/base/ 2001-05-15 Michael Natterer <mitch@gimp.org> * configure.in: new directory app/base/ * app/Makefile.am * app/boundary.[ch] * app/brush_scale.[ch] * app/gimpchecks.h * app/gimplut.[ch] * app/pixel_processor.[ch] * app/pixel_region.[ch] * app/pixel_surround.[ch] * app/temp_buf.[ch] * app/tile.[ch] * app/tile_cache.[ch] * app/tile_manager.[ch] * app/tile_manager_pvt.h * app/tile_pvt.h * app/tile_swap.[ch]: moved to base/ * app/base/Makefile.am * app/base/base-types.h * app/base/*: new directory for the sub-object pixel maniplation and storage stuff. Does not include Gtk+ or anything outside base/. Did some cleanup in all files. * app/appenums.h * app/apptypes.h * app/core/gimpimage.h: removed types which are now in base/base-types.h. * app/base/base-config.[ch] * app/gimprc.[ch]: put the config variables for base/ to their own file so base/ doesn not have to include gimprc.h (does not yet work, i.e. the variables are un-configurable right now) * app/main.c: set a log handler for "Gimp-Base". * app/paint-funcs/Makefile.am * app/paint-funcs/paint-funcs.[ch]: removed the color hash which maps RGB to color indices because it's a totally standalone system which has nothing to do with the paint-funcs and introduced a GimpImage dependency. paint-funcs/ should be considered on the same sub-object (glib-only) level as base/, only in a different directory. * app/core/Makefile.am * app/core/gimpimage-colorhash.[ch]: put the color hash here. * app/gimage.c: don't invalidate the color hash here... * app/core/gimpimage.c: ... but in the colormap_changed() default inplementation. Initialize the hash in class_init(). * tools/pdbgen/Makefile.am: scan app/base/base-types.h for enums. * tools/pdbgen/enums.pl: regenerated. * app/[lots] * app/core/[of] * app/gui/[files] * app/pdb/[all] * app/tools/[over] * app/widgets/[the] * tools/pdbgen/pdb/[place]: changed #includes accordingly. And use base_config->value instead of the stuff from gimprc.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"
new directory app/base/ 2001-05-15 Michael Natterer <mitch@gimp.org> * configure.in: new directory app/base/ * app/Makefile.am * app/boundary.[ch] * app/brush_scale.[ch] * app/gimpchecks.h * app/gimplut.[ch] * app/pixel_processor.[ch] * app/pixel_region.[ch] * app/pixel_surround.[ch] * app/temp_buf.[ch] * app/tile.[ch] * app/tile_cache.[ch] * app/tile_manager.[ch] * app/tile_manager_pvt.h * app/tile_pvt.h * app/tile_swap.[ch]: moved to base/ * app/base/Makefile.am * app/base/base-types.h * app/base/*: new directory for the sub-object pixel maniplation and storage stuff. Does not include Gtk+ or anything outside base/. Did some cleanup in all files. * app/appenums.h * app/apptypes.h * app/core/gimpimage.h: removed types which are now in base/base-types.h. * app/base/base-config.[ch] * app/gimprc.[ch]: put the config variables for base/ to their own file so base/ doesn not have to include gimprc.h (does not yet work, i.e. the variables are un-configurable right now) * app/main.c: set a log handler for "Gimp-Base". * app/paint-funcs/Makefile.am * app/paint-funcs/paint-funcs.[ch]: removed the color hash which maps RGB to color indices because it's a totally standalone system which has nothing to do with the paint-funcs and introduced a GimpImage dependency. paint-funcs/ should be considered on the same sub-object (glib-only) level as base/, only in a different directory. * app/core/Makefile.am * app/core/gimpimage-colorhash.[ch]: put the color hash here. * app/gimage.c: don't invalidate the color hash here... * app/core/gimpimage.c: ... but in the colormap_changed() default inplementation. Initialize the hash in class_init(). * tools/pdbgen/Makefile.am: scan app/base/base-types.h for enums. * tools/pdbgen/enums.pl: regenerated. * app/[lots] * app/core/[of] * app/gui/[files] * app/pdb/[all] * app/tools/[over] * app/widgets/[the] * tools/pdbgen/pdb/[place]: changed #includes accordingly. And use base_config->value instead of the stuff from gimprc.h.
2001-05-15 19:25:25 +08:00
#include "tile-manager.h"
app/Makefile.am app/channel_pvt.h app/drawable_pvt.h app/gdisplayF.h 2000-12-29 Michael Natterer <mitch@gimp.org> * app/Makefile.am * app/channel_pvt.h * app/drawable_pvt.h * app/gdisplayF.h * app/gimpdrawableP.h * app/gimpimageP.h * app/layer_pvt.h * app/toolsF.h: removed these files. * app/apptypes.h * tools/pdbgen/enums.pl: added tons of opaque typedefs and enums. * tools/pdbgen/pdb/brush_select.pdb * tools/pdbgen/pdb/brushes.pdb * tools/pdbgen/pdb/channel.pdb * tools/pdbgen/pdb/color.pdb * tools/pdbgen/pdb/convert.pdb * tools/pdbgen/pdb/display.pdb * tools/pdbgen/pdb/drawable.pdb * tools/pdbgen/pdb/fileops.pdb * tools/pdbgen/pdb/gradient_select.pdb * tools/pdbgen/pdb/gradients.pdb * tools/pdbgen/pdb/help.pdb * tools/pdbgen/pdb/image.pdb * tools/pdbgen/pdb/layer.pdb * tools/pdbgen/pdb/pattern_select.pdb * tools/pdbgen/pdb/patterns.pdb * tools/pdbgen/pdb/selection.pdb * tools/pdbgen/pdb/tools.pdb * app/*: chainsaw #include cleanup: - Never (never!!) include stuff in header files except where we need access to structures' contents (like derived objects). - Added prototypes and proper formating in many files. - The #include order in *all* *.c files is as follows: #include "config.h" #include <system stuff> #include <gtk/gtk.h> #include "apptypes.h" #include "gimp stuff" #include "libgimp stuff" #include "libgimp/gimpintl.h" By following this scheme we can easily see a file's dependencies from it's #include's and can grep for the inclusion to find out where a file is used. * tools/pdbgen/app.pl: changed to follow the include scheme above. * libgimp/Makefile.am * libgimp/gimpuitypes.h: new file, included from libgimp/gimpui.h and from app/apptypes.h. * libgimp/gimpcolorbutton.[ch] * libgimp/gimpdialog.[ch] * libgimp/gimphelpui.[ch] * libgimp/gimpparasite.[ch] * libgimp/gimppatheditor.[ch] * libgimp/gimpprotocol.c * libgimp/gimpquerybox.[ch] * libgimp/gimpsizeentry.[ch] * libgimp/gimptypes.h * libgimp/gimpui.h * libgimp/gimpunit.h * libgimp/gimpunitmenu.[ch] * libgimp/gimpwidgets.[ch]: changed accordingly. * plug-ins/FractalExplorer/Dialogs.c * plug-ins/gdyntext/message_window.c * plug-ins/imagemap/imap_default_dialog.c * plug-ins/imagemap/imap_file.c: these files used to include "libgimp/gimpui.h" without including "libgimp/gimp.h". This is no longer possible because the libgimpui headers don't inlcude "libgimp/gimpunit.h" any more.
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
typedef struct _Boundary Boundary;
1997-11-25 06:05:25 +08:00
struct _Boundary
{
/* The array of segments */
BoundSeg *segs;
gint num_segs;
gint max_segs;
/* The array of vertical segments */
gint *vert_segs;
1997-11-25 06:05:25 +08:00
/* The empty segment arrays */
gint *empty_segs_n;
gint *empty_segs_c;
gint *empty_segs_l;
gint max_empty_segs;
};
1997-11-25 06:05:25 +08:00
/* local function prototypes */
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);
static gint find_segment (const BoundSeg *segs,
gint num_segs,
gint x,
gint y);
static void simplify_subdivide (const BoundSeg *segs,
gint start_idx,
gint end_idx,
GArray **ret_points);
/* 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 *
boundary_find (PixelRegion *maskPR,
BoundaryType type,
int x1,
int y1,
int x2,
int y2,
guchar threshold,
int *num_segs)
{
Boundary *boundary;
g_return_val_if_fail (maskPR != NULL, NULL);
g_return_val_if_fail (num_segs != NULL, NULL);
boundary = generate_boundary (maskPR, type, x1, y1, x2, y2, threshold);
*num_segs = boundary->num_segs;
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 *
boundary_sort (const BoundSeg *segs,
gint num_segs,
gint *num_groups)
{
Boundary *boundary;
gint i;
gint index;
gint x, y;
gint startx, starty;
gboolean empty;
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);
index = 0;
new_segs = NULL;
empty = FALSE;
while (! empty)
{
empty = TRUE;
/* find the index of a non-visited segment to start a group */
for (i = 0; i < num_segs; i++)
if (segs[i].visited == FALSE)
{
index = i;
empty = FALSE;
i = num_segs;
}
if (! empty)
{
boundary_add_seg (boundary,
segs[index].x1, segs[index].y1,
segs[index].x2, segs[index].y2,
segs[index].open);
((BoundSeg *) segs)[index].visited = TRUE;
startx = segs[index].x1;
starty = segs[index].y1;
x = segs[index].x2;
y = segs[index].y2;
while ((index = find_segment (segs, num_segs, x, y)) != -1)
{
/* make sure ordering is correct */
if (x == segs[index].x1 && y == segs[index].y1)
{
boundary_add_seg (boundary,
segs[index].x1, segs[index].y1,
segs[index].x2, segs[index].y2,
segs[index].open);
x = segs[index].x2;
y = segs[index].y2;
}
else
{
boundary_add_seg (boundary,
segs[index].x2, segs[index].y2,
segs[index].x1, segs[index].y1,
segs[index].open);
x = segs[index].x1;
y = segs[index].y1;
}
((BoundSeg *) segs)[index].visited = TRUE;
}
2006-07-06 20:27:32 +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);
}
}
return boundary_free (boundary, FALSE);
}
/**
* 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.
**/
BoundSeg *
boundary_simplify (BoundSeg *sorted_segs,
gint num_groups,
gint *num_segs)
{
GArray *new_bounds;
gint i, seg;
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);
new_bounds = g_array_new (FALSE, FALSE, sizeof (BoundSeg));
seg = 0;
for (i = 0; i < num_groups; i++)
{
gint start = seg;
gint n_points = 0;
while (sorted_segs[seg].x1 != -1 ||
sorted_segs[seg].x2 != -1 ||
sorted_segs[seg].y1 != -1 ||
sorted_segs[seg].y2 != -1)
{
n_points++;
seg++;
}
if (n_points > 0)
{
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 */
tmp_seg = sorted_segs[seg];
sorted_segs[seg] = sorted_segs[start];
simplify_subdivide (sorted_segs, start, start + n_points,
&tmp_points);
sorted_segs[seg] = tmp_seg;
for (j = 0; j < tmp_points->len; j++)
g_array_append_val (new_bounds,
sorted_segs[g_array_index (tmp_points,
gint, j)]);
g_array_append_val (new_bounds, sorted_segs[seg]);
g_array_free (tmp_points, TRUE);
}
seg++;
}
*num_segs = new_bounds->len;
return (BoundSeg *) g_array_free (new_bounds, FALSE);
}
/* private functions */
static Boundary *
boundary_new (PixelRegion *PR)
{
Boundary *boundary = g_slice_new0 (Boundary);
if (PR)
{
gint i;
/* array for determining the vertical line segments
* which must be drawn
*/
boundary->vert_segs = g_new (gint, PR->w + PR->x + 1);
for (i = 0; i <= (PR->w + PR->x); i++)
boundary->vert_segs[i] = -1;
/* 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);
g_slice_free (Boundary, boundary);
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 ++;
}
1997-11-25 06:05:25 +08:00
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)
1997-11-25 06:05:25 +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;
}
if (type == BOUNDARY_WITHIN_BOUNDS)
1997-11-25 06:05:25 +08:00
{
if (scanline < y1 || scanline >= y2)
{
empty_segs[(*num_empty)++] = 0;
empty_segs[(*num_empty)++] = G_MAXINT;
return;
}
1997-11-25 06:05:25 +08:00
start = x1;
end = x2;
1997-11-25 06:05:25 +08:00
}
else if (type == BOUNDARY_IGNORE_BOUNDS)
1997-11-25 06:05:25 +08:00
{
start = maskPR->x;
end = maskPR->x + maskPR->w;
1997-11-25 06:05:25 +08:00
if (scanline < y1 || scanline >= y2)
x2 = -1;
1997-11-25 06:05:25 +08:00
}
tilex = -1;
empty_segs[(*num_empty)++] = 0;
last = -1;
build color_cmds, lut_funcs, and pixel_processor feedback in the splash * app/Makefile.am: build color_cmds, lut_funcs, and pixel_processor * app/app_procs.c: feedback in the splash screen when loading parasites. * app/boundary.c: Optimized find_empty_segs. * app/brightness_contrast.[ch] * app/levels.[ch] * app/posterize.[ch]: moved pdb and lut calculation code. These files now contain only GUI functions. * app/channel.c: Optimized channel_bounds (fewer compares, better use of registers). Use color_region instead of channel_*_segment in channel_combine_rect. Optimized channel_combine_ellipse by skipping pixels inside of the ellipse. Use pixel_region_process_parallel in channel_combine_mask. Use a GimpLut in channel_invert, and channel_sharpen. * app/invert.c * app/equalize.c: moved the lut functions to lut_funcs.c * app/gimpdrawable.c, app/gimpdrawableP.h * app/gimpimage.c, app/gimpimageP.h: removed unused gimpmatrix variables/includes. * app/gimplut.[ch]: added new function gimp_lut_process_inline that operates on a single PixelRegion. * app/gimpparasite.[ch]: new functions to save/load parasiterc * app/parasitelist.[ch]: new functions to save/load ParasiteLists in/from files. * libgimp/parasite.[ch]: new functions to load/save parasites. * app/internal_procs.c: get some procs from new location in color_cmds.h. * app/pixel_region.[ch]: moved pixel_regions_process_parallel related functions to a new file. * app/color_cmds.[ch]: new files for PDB definitions/implementations of color correction functions. * app/lut_funcs.[ch]: new files to hold lut creation functions. * app/pixel_processor.[ch]: new files that contain the pixel_regions_process_parallel routines. Added some new capabilities that are currently unused.
1999-04-09 14:00:11 +08:00
l_num_empty = *num_empty;
if (! maskPR->tiles)
{
data = maskPR->data + scanline * maskPR->rowstride;
dstep = maskPR->bytes;
endx = end;
}
build color_cmds, lut_funcs, and pixel_processor feedback in the splash * app/Makefile.am: build color_cmds, lut_funcs, and pixel_processor * app/app_procs.c: feedback in the splash screen when loading parasites. * app/boundary.c: Optimized find_empty_segs. * app/brightness_contrast.[ch] * app/levels.[ch] * app/posterize.[ch]: moved pdb and lut calculation code. These files now contain only GUI functions. * app/channel.c: Optimized channel_bounds (fewer compares, better use of registers). Use color_region instead of channel_*_segment in channel_combine_rect. Optimized channel_combine_ellipse by skipping pixels inside of the ellipse. Use pixel_region_process_parallel in channel_combine_mask. Use a GimpLut in channel_invert, and channel_sharpen. * app/invert.c * app/equalize.c: moved the lut functions to lut_funcs.c * app/gimpdrawable.c, app/gimpdrawableP.h * app/gimpimage.c, app/gimpimageP.h: removed unused gimpmatrix variables/includes. * app/gimplut.[ch]: added new function gimp_lut_process_inline that operates on a single PixelRegion. * app/gimpparasite.[ch]: new functions to save/load parasiterc * app/parasitelist.[ch]: new functions to save/load ParasiteLists in/from files. * libgimp/parasite.[ch]: new functions to load/save parasites. * app/internal_procs.c: get some procs from new location in color_cmds.h. * app/pixel_region.[ch]: moved pixel_regions_process_parallel related functions to a new file. * app/color_cmds.[ch]: new files for PDB definitions/implementations of color correction functions. * app/lut_funcs.[ch]: new files to hold lut creation functions. * app/pixel_processor.[ch]: new files that contain the pixel_regions_process_parallel routines. Added some new capabilities that are currently unused.
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 */
if (maskPR->tiles)
{
if ((x / TILE_WIDTH) != tilex)
{
if (tile)
tile_release (tile, FALSE);
tile = tile_manager_get_tile (maskPR->tiles,
x, scanline, TRUE, FALSE);
data =
(const guchar *) tile_data_pointer (tile,
x % TILE_WIDTH,
scanline % TILE_HEIGHT) +
(tile_bpp(tile) - 1);
tilex = x / TILE_WIDTH;
dstep = tile_bpp (tile);
}
endx = x + (TILE_WIDTH - (x%TILE_WIDTH));
endx = MIN (end, endx);
}
if (type == BOUNDARY_IGNORE_BOUNDS && (endx > x1 || x < x2))
{
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;
}
}
build color_cmds, lut_funcs, and pixel_processor feedback in the splash * app/Makefile.am: build color_cmds, lut_funcs, and pixel_processor * app/app_procs.c: feedback in the splash screen when loading parasites. * app/boundary.c: Optimized find_empty_segs. * app/brightness_contrast.[ch] * app/levels.[ch] * app/posterize.[ch]: moved pdb and lut calculation code. These files now contain only GUI functions. * app/channel.c: Optimized channel_bounds (fewer compares, better use of registers). Use color_region instead of channel_*_segment in channel_combine_rect. Optimized channel_combine_ellipse by skipping pixels inside of the ellipse. Use pixel_region_process_parallel in channel_combine_mask. Use a GimpLut in channel_invert, and channel_sharpen. * app/invert.c * app/equalize.c: moved the lut functions to lut_funcs.c * app/gimpdrawable.c, app/gimpdrawableP.h * app/gimpimage.c, app/gimpimageP.h: removed unused gimpmatrix variables/includes. * app/gimplut.[ch]: added new function gimp_lut_process_inline that operates on a single PixelRegion. * app/gimpparasite.[ch]: new functions to save/load parasiterc * app/parasitelist.[ch]: new functions to save/load ParasiteLists in/from files. * libgimp/parasite.[ch]: new functions to load/save parasites. * app/internal_procs.c: get some procs from new location in color_cmds.h. * app/pixel_region.[ch]: moved pixel_regions_process_parallel related functions to a new file. * app/color_cmds.[ch]: new files for PDB definitions/implementations of color correction functions. * app/lut_funcs.[ch]: new files to hold lut creation functions. * app/pixel_processor.[ch]: new files that contain the pixel_regions_process_parallel routines. Added some new capabilities that are currently unused.
1999-04-09 14:00:11 +08:00
else
{
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
}
build color_cmds, lut_funcs, and pixel_processor feedback in the splash * app/Makefile.am: build color_cmds, lut_funcs, and pixel_processor * app/app_procs.c: feedback in the splash screen when loading parasites. * app/boundary.c: Optimized find_empty_segs. * app/brightness_contrast.[ch] * app/levels.[ch] * app/posterize.[ch]: moved pdb and lut calculation code. These files now contain only GUI functions. * app/channel.c: Optimized channel_bounds (fewer compares, better use of registers). Use color_region instead of channel_*_segment in channel_combine_rect. Optimized channel_combine_ellipse by skipping pixels inside of the ellipse. Use pixel_region_process_parallel in channel_combine_mask. Use a GimpLut in channel_invert, and channel_sharpen. * app/invert.c * app/equalize.c: moved the lut functions to lut_funcs.c * app/gimpdrawable.c, app/gimpdrawableP.h * app/gimpimage.c, app/gimpimageP.h: removed unused gimpmatrix variables/includes. * app/gimplut.[ch]: added new function gimp_lut_process_inline that operates on a single PixelRegion. * app/gimpparasite.[ch]: new functions to save/load parasiterc * app/parasitelist.[ch]: new functions to save/load ParasiteLists in/from files. * libgimp/parasite.[ch]: new functions to load/save parasites. * app/internal_procs.c: get some procs from new location in color_cmds.h. * app/pixel_region.[ch]: moved pixel_regions_process_parallel related functions to a new file. * app/color_cmds.[ch]: new files for PDB definitions/implementations of color correction functions. * app/lut_funcs.[ch]: new files to hold lut creation functions. * app/pixel_processor.[ch]: new files that contain the pixel_regions_process_parallel routines. Added some new capabilities that are currently unused.
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)
tile_release (tile, FALSE);
1997-11-25 06:05:25 +08:00
}
static void
process_horiz_seg (Boundary *boundary,
gint x1,
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. */
if (boundary->vert_segs[x1] >= 0)
1997-11-25 06:05:25 +08:00
{
boundary_add_seg (boundary, x1, boundary->vert_segs[x1], x1, y1, !open);
boundary->vert_segs[x1] = -1;
1997-11-25 06:05:25 +08:00
}
else
boundary->vert_segs[x1] = y1;
1997-11-25 06:05:25 +08:00
if (boundary->vert_segs[x2] >= 0)
1997-11-25 06:05:25 +08:00
{
boundary_add_seg (boundary, x2, boundary->vert_segs[x2], x2, y2, open);
boundary->vert_segs[x2] = -1;
1997-11-25 06:05:25 +08:00
}
else
boundary->vert_segs[x2] = y2;
1997-11-25 06:05:25 +08:00
boundary_add_seg (boundary, x1, y1, x2, y2, open);
1997-11-25 06:05:25 +08:00
}
static void
make_horiz_segs (Boundary *boundary,
gint start,
gint end,
gint scanline,
gint empty[],
gint num_empty,
gint top)
1997-11-25 06:05:25 +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++;
1997-11-25 06:05:25 +08:00
if (e_s <= start && e_e >= end)
process_horiz_seg (boundary,
start, scanline, end, scanline, top);
1997-11-25 06:05:25 +08:00
else if ((e_s > start && e_s < end) ||
(e_e < end && e_e > start))
process_horiz_seg (boundary,
MAX (e_s, start), scanline,
MIN (e_e, end), scanline, top);
1997-11-25 06:05:25 +08:00
}
}
static Boundary *
generate_boundary (PixelRegion *PR,
BoundaryType type,
gint x1,
gint y1,
gint x2,
gint y2,
guchar threshold)
1997-11-25 06:05:25 +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
boundary = boundary_new (PR);
the active_tool may change in the middle of gdisplay_canvas_events(), so 2001-07-08 Michael Natterer <mitch@gimp.org> * app/disp_callbacks.c: the active_tool may change in the middle of gdisplay_canvas_events(), so re-get it after using it for cursor_update. * app/base/boundary.c: made some global variables local. * app/core/gimpdrawable.c: don't call gimp_drawable_set_visible() in gimp_drawable_configure() because we don't want signal emissions while configuring the drawable. * app/gui/Makefile.am * app/gui/brushes-commands.[ch] * app/gui/data-commands.[ch] * app/gui/gradients-commands.[ch] * app/gui/palettes-commands.[ch] * app/gui/patterns-commands.[ch]: new files for new new item factories' callbacks. * app/gui/menus.[ch]: added context menus for brushes, patterns, ... * app/gui/gradient-editor.c * app/gui/palette-editor.c: removed the "Save as POV", "Import Palette" and "Merge Palattes" buttons as they are item factory callbacks now. * app/widgets/gimpbrushfactoryview.[ch] * app/widgets/gimpbufferview.[ch] * app/widgets/gimpcontainereditor.[ch] * app/widgets/gimpdatafactoryview.[ch]: pass a "GimpContainerContextFunc" pointer to all GimpContainerEditor subclasses' constructors. Use the function to show the context menu. * app/widgets/gimpcontainergridview.c: dispatch the previews' "context" signal. * app/widgets/gimppreview.[ch]: new signal "context" which is emitted on right-click. * app/gui/brush-select.c * app/gui/dialogs-constructors.c * app/gui/gradient-select.c * app/gui/layers-commands.c * app/gui/palette-select.c * app/gui/pattern-select.c * app/gui/test-commands.c: changed accordingly.
2001-07-09 05:44:52 +08:00
1997-11-25 06:05:25 +08:00
start = 0;
end = 0;
if (type == BOUNDARY_WITHIN_BOUNDS)
1997-11-25 06:05:25 +08:00
{
start = y1;
end = y2;
1997-11-25 06:05:25 +08:00
}
else if (type == BOUNDARY_IGNORE_BOUNDS)
1997-11-25 06:05:25 +08:00
{
the active_tool may change in the middle of gdisplay_canvas_events(), so 2001-07-08 Michael Natterer <mitch@gimp.org> * app/disp_callbacks.c: the active_tool may change in the middle of gdisplay_canvas_events(), so re-get it after using it for cursor_update. * app/base/boundary.c: made some global variables local. * app/core/gimpdrawable.c: don't call gimp_drawable_set_visible() in gimp_drawable_configure() because we don't want signal emissions while configuring the drawable. * app/gui/Makefile.am * app/gui/brushes-commands.[ch] * app/gui/data-commands.[ch] * app/gui/gradients-commands.[ch] * app/gui/palettes-commands.[ch] * app/gui/patterns-commands.[ch]: new files for new new item factories' callbacks. * app/gui/menus.[ch]: added context menus for brushes, patterns, ... * app/gui/gradient-editor.c * app/gui/palette-editor.c: removed the "Save as POV", "Import Palette" and "Merge Palattes" buttons as they are item factory callbacks now. * app/widgets/gimpbrushfactoryview.[ch] * app/widgets/gimpbufferview.[ch] * app/widgets/gimpcontainereditor.[ch] * app/widgets/gimpdatafactoryview.[ch]: pass a "GimpContainerContextFunc" pointer to all GimpContainerEditor subclasses' constructors. Use the function to show the context menu. * app/widgets/gimpcontainergridview.c: dispatch the previews' "context" signal. * app/widgets/gimppreview.[ch]: new signal "context" which is emitted on right-click. * app/gui/brush-select.c * app/gui/dialogs-constructors.c * app/gui/gradient-select.c * app/gui/layers-commands.c * app/gui/palette-select.c * app/gui/pattern-select.c * app/gui/test-commands.c: changed accordingly.
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 */
find_empty_segs (PR, start - 1, boundary->empty_segs_l,
boundary->max_empty_segs, &num_empty_l,
type, x1, y1, x2, y2,
threshold);
find_empty_segs (PR, start, boundary->empty_segs_c,
boundary->max_empty_segs, &num_empty_c,
type, x1, y1, x2, y2,
threshold);
1997-11-25 06:05:25 +08:00
for (scanline = start; scanline < end; scanline++)
{
/* find the empty segment list for the next scanline */
find_empty_segs (PR, scanline + 1, boundary->empty_segs_n,
boundary->max_empty_segs, &num_empty_n,
type, x1, y1, x2, y2,
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)
{
make_horiz_segs (boundary,
boundary->empty_segs_c [i],
boundary->empty_segs_c [i+1],
scanline, boundary->empty_segs_l, num_empty_l, 1);
make_horiz_segs (boundary,
boundary->empty_segs_c [i],
boundary->empty_segs_c [i+1],
scanline + 1, boundary->empty_segs_n, num_empty_n, 0);
}
1997-11-25 06:05:25 +08:00
/* get the next scanline of empty segments, swap others */
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
}
return boundary;
1997-11-25 06:05:25 +08:00
}
/* sorting utility functions */
1997-11-25 06:05:25 +08:00
static gint
find_segment (const BoundSeg *segs,
gint num_segs,
gint x,
gint y)
1997-11-25 06:05:25 +08:00
{
gint index;
1997-11-25 06:05:25 +08:00
for (index = 0; index < num_segs; index++)
if (((segs[index].x1 == x && segs[index].y1 == y) ||
(segs[index].x2 == x && segs[index].y2 == y)) &&
segs[index].visited == FALSE)
1997-11-25 06:05:25 +08:00
return index;
return -1;
}
/* simplifying utility functions */
static void
simplify_subdivide (const BoundSeg *segs,
gint start_idx,
gint end_idx,
GArray **ret_points)
{
gint maxdist_idx;
gint dist, maxdist;
gint i, dx, dy;
gdouble realdist;
/* g_printerr ("subdiv %d - %d\n", start_idx, end_idx); */
if (end_idx - start_idx < 2)
{
*ret_points = g_array_append_val (*ret_points, start_idx);
/* g_printerr (" %d\n", 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 */
dist = (SQR (segs[i].x1 - segs[start_idx].x1) +
SQR (segs[i].y1 - segs[start_idx].y1));
if (dist > maxdist)
{
maxdist = dist;
maxdist_idx = i;
}
}
realdist = sqrt ((gdouble) maxdist);
}
else
{
dx = segs[end_idx].x1 - segs[start_idx].x1;
dy = segs[end_idx].y1 - segs[start_idx].y1;
/* g_printerr ("dx: %d, dy: %d\n", dx, dy); */
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)))
*/
dist = (dx * (segs[start_idx].y1 - segs[i].y1) -
dy * (segs[start_idx].x1 - segs[i].x1));
if (dist < 0)
dist *= -1;
if (dist > maxdist)
{
maxdist = dist;
maxdist_idx = i;
}
}
realdist = ((gdouble) maxdist) / sqrt ((gdouble) (SQR (dx) + SQR (dy)));
}
/* g_printerr ("Index %d, x: %d, y: %d, distance: %.4f\n", maxdist_idx,
segs[maxdist_idx].x1, segs[maxdist_idx].y1, realdist); */
/* threshold is chosen to catch 45 degree stairs */
if (realdist <= 1.0)
{
*ret_points = g_array_append_val (*ret_points, start_idx);
/* g_printerr (" %d\n", 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);
}