gimp/app/scan_convert.c
Michael Natterer 3220f9ec94 app/channel.[ch] app/drawable.[ch] app/gdisplay.[ch] app/gimpdrawable.[ch]
2001-01-14  Michael Natterer  <mitch@gimp.org>

	* app/channel.[ch]
	* app/drawable.[ch]
	* app/gdisplay.[ch]
	* app/gimpdrawable.[ch]
	* app/layer.[ch]:

	- Removed all "typedef drawable_function gimp_drawable_function".
	- Renamed all *_get_ID() functions to *_get_by_ID().
	- For symmetry reasons, renamed drawable_ID() to gimp_drawable_get_ID().
	- Removed the *_get_ID() functions of GimpLayer, GimpLayerMask
	  and GimpChannel.

	* app/airbrush.c
	* app/bezier_select.c
	* app/blend.c
	* app/brightness_contrast.c
	* app/bucket_fill.c
	* app/by_color_select.c
	* app/clone.c
	* app/color_balance.c
	* app/color_picker.c
	* app/convert.c
	* app/convolve.c
	* app/crop.c
	* app/curves.c
	* app/desaturate.c
	* app/dodgeburn.c
	* app/edit_selection.c
	* app/eraser.c
	* app/fileops.c
	* app/flip_tool.c
	* app/floating_sel.c
	* app/fuzzy_select.c
	* app/gimage.c
	* app/gimage_mask.c
	* app/gimphistogram.c
	* app/gimpimage.c
	* app/global_edit.c
	* app/histogram_tool.c
	* app/hue_saturation.c
	* app/image_map.c
	* app/ink.c
	* app/invert.c
	* app/layer_select.c
	* app/layers_dialog.c
	* app/levels.c
	* app/paint_core.c
	* app/paintbrush.c
	* app/pencil.c
	* app/plug_in.c
	* app/posterize.c
	* app/scan_convert.c
	* app/smudge.c
	* app/text_tool.c
	* app/threshold.c
	* app/transform_core.c
	* app/undo.c
	* app/undo_history.c

	* app/channel_cmds.c
	* app/channel_ops_cmds.c
	* app/color_cmds.c
	* app/display_cmds.c
	* app/drawable_cmds.c
	* app/edit_cmds.c
	* app/floating_sel_cmds.c
	* app/image_cmds.c
	* app/layer_cmds.c
	* app/parasite_cmds.c
	* app/selection_cmds.c
	* app/text_tool_cmds.c
	* app/tools_cmds.c
	* libgimp/gimpdrawable_pdb.c
	* tools/pdbgen/pdb.pl
	* tools/pdbgen/pdb/channel_ops.pdb
	* tools/pdbgen/pdb/color.pdb
	* tools/pdbgen/pdb/drawable.pdb
	* tools/pdbgen/pdb/edit.pdb
	* tools/pdbgen/pdb/image.pdb
	* tools/pdbgen/pdb/selection.pdb
	* tools/pdbgen/pdb/tools.pdb: changed accordingly.
2001-01-14 21:11:52 +00:00

356 lines
8 KiB
C

/* The GIMP -- an image manipulation program
* Copyright (C) 1995-1999 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.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include "apptypes.h"
#include "channel.h"
#include "drawable.h"
#include "gimpimage.h"
#include "pixel_region.h"
#include "scan_convert.h"
#include "libgimp/gimpmath.h"
#ifdef DEBUG
#define TRC(x) printf x
#else
#define TRC(x)
#endif
/* Reveal our private structure */
struct ScanConverterPrivate
{
guint width;
guint height;
GSList **scanlines; /* array of height*antialias scanlines */
guint antialias; /* how much to oversample by */
/* record the first and last points so we can close the curve */
gboolean got_first;
ScanConvertPoint first;
gboolean got_last;
ScanConvertPoint last;
};
/* Local helper routines to scan convert the polygon */
static GSList *
insert_into_sorted_list (GSList *list,
gint x)
{
GSList *orig = list;
GSList *rest;
if (!list)
return g_slist_prepend (list, GINT_TO_POINTER (x));
while (list)
{
rest = g_slist_next (list);
if (x < GPOINTER_TO_INT (list->data))
{
rest = g_slist_prepend (rest, list->data);
list->next = rest;
list->data = GINT_TO_POINTER (x);
return orig;
}
else if (!rest)
{
g_slist_append (list, GINT_TO_POINTER (x));
return orig;
}
list = g_slist_next (list);
}
return orig;
}
static void
convert_segment (ScanConverter *sc,
gint x1,
gint y1,
gint x2,
gint y2)
{
gint ydiff, y, tmp;
gint width;
gint height;
GSList **scanlines;
gfloat xinc, xstart;
/* pre-calculate invariant commonly used values */
width = sc->width * sc->antialias;
height = sc->height * sc->antialias;
scanlines = sc->scanlines;
x1 = CLAMP (x1, 0, width - 1);
y1 = CLAMP (y1, 0, height - 1);
x2 = CLAMP (x2, 0, width - 1);
y2 = CLAMP (y2, 0, height - 1);
if (y1 > y2)
{
tmp = y2; y2 = y1; y1 = tmp;
tmp = x2; x2 = x1; x1 = tmp;
}
ydiff = (y2 - y1);
if (ydiff)
{
xinc = (float) (x2 - x1) / (float) ydiff;
xstart = x1 + 0.5 * xinc;
for (y = y1 ; y < y2; y++)
{
scanlines[y] = insert_into_sorted_list (scanlines[y], ROUND (xstart));
xstart += xinc;
}
}
else
{
/* horizontal line */
scanlines[y1] = insert_into_sorted_list (scanlines[y1], ROUND (x1));
scanlines[y1] = insert_into_sorted_list (scanlines[y1], ROUND (x2));
}
}
/**************************************************************/
/* Exported functions */
/* Create a new scan conversion context */
ScanConverter *
scan_converter_new (guint width,
guint height,
guint antialias)
{
ScanConverter *sc;
g_return_val_if_fail (width > 0, NULL);
g_return_val_if_fail (height > 0, NULL);
g_return_val_if_fail (antialias > 0, NULL);
sc = g_new0 (ScanConverter, 1);
sc->antialias = antialias;
sc->width = width;
sc->height = height;
sc->scanlines = g_new0 (GSList *, height * antialias);
return sc;
}
void
scan_converter_free (ScanConverter *sc)
{
g_free (sc->scanlines);
g_free (sc);
}
/* Add "npoints" from "pointlist" to the polygon currently being
* described by "scan_converter". */
void
scan_converter_add_points (ScanConverter *sc,
guint npoints,
ScanConvertPoint *pointlist)
{
gint i;
guint antialias;
g_return_if_fail (sc != NULL);
g_return_if_fail (pointlist != NULL);
antialias = sc->antialias;
if (!sc->got_first && npoints > 0)
{
sc->got_first = TRUE;
sc->first = pointlist[0];
}
/* link from previous point */
if (sc->got_last && npoints > 0)
{
TRC (("|| %g,%g -> %g,%g\n",
sc->last.x, sc->last.y,
pointlist[0].x, pointlist[0].y));
convert_segment (sc,
(int)sc->last.x * antialias,
(int)sc->last.y * antialias,
(int)pointlist[0].x * antialias,
(int)pointlist[0].y * antialias);
}
for (i = 0; i < (npoints - 1); i++)
{
convert_segment (sc,
(int) pointlist[i].x * antialias,
(int) pointlist[i].y * antialias,
(int) pointlist[i + 1].x * antialias,
(int) pointlist[i + 1].y * antialias);
}
TRC (("[] %g,%g -> %g,%g\n",
pointlist[0].x, pointlist[0].y,
pointlist[npoints-1].x, pointlist[npoints-1].y));
if (npoints > 0)
{
sc->got_last = TRUE;
sc->last = pointlist[npoints - 1];
}
}
/* Scan convert the polygon described by the list of points passed to
* scan_convert_add_points, and return a channel with a bits set if
* they fall within the polygon defined. The polygon is filled
* according to the even-odd rule. The polygon is closed by
* joining the final point to the initial point.
*/
Channel *
scan_converter_to_channel (ScanConverter *sc,
GimpImage *gimage)
{
Channel *mask;
GSList *list;
PixelRegion maskPR;
guint widtha;
guint heighta;
guint antialias;
guint antialias2;
guchar *buf;
guchar *b;
gint *vals;
gint val;
gint x, x2, w;
gint i, j;
antialias = sc->antialias;
antialias2 = antialias * antialias;
/* do we need to close the polygon? */
if (sc->got_first && sc->got_last &&
(sc->first.x != sc->last.x || sc->first.y != sc->last.y))
{
convert_segment (sc,
(int) sc->last.x * antialias,
(int) sc->last.y * antialias,
(int) sc->first.x * antialias,
(int) sc->first.y * antialias);
}
mask = channel_new_mask (gimage, sc->width, sc->height);
buf = g_new0 (guchar, sc->width);
widtha = sc->width * antialias;
heighta = sc->height * antialias;
/* allocate value array */
vals = g_new (gint, widtha);
/* dump scanlines */
for (i = 0; i < heighta; i++)
{
list = sc->scanlines[i];
TRC (("%03d: ", i));
while (list)
{
TRC (("%3d ", GPOINTER_TO_INT (list->data)));
list = g_slist_next (list);
}
TRC (("\n"));
}
pixel_region_init (&maskPR, gimp_drawable_data (GIMP_DRAWABLE(mask)), 0, 0,
gimp_drawable_width (GIMP_DRAWABLE(mask)),
gimp_drawable_height (GIMP_DRAWABLE(mask)), TRUE);
for (i = 0; i < heighta; i++)
{
list = sc->scanlines[i];
/* zero the vals array */
if (!(i % antialias))
memset (vals, 0, widtha * sizeof (int));
while (list)
{
x = GPOINTER_TO_INT (list->data);
list = g_slist_next (list);
if (!list)
{
g_message ("Cannot properly scanline convert polygon!\n");
}
else
{
/* bounds checking */
x = CLAMP (x, 0, widtha);
x2 = CLAMP (GPOINTER_TO_INT (list->data), 0, widtha);
w = x2 - x;
if (w > 0)
{
if (antialias == 1)
{
channel_add_segment (mask, x, i, w, 255);
}
else
{
for (j = 0; j < w; j++)
vals[j + x] += 255;
}
}
list = g_slist_next (list);
}
}
if (antialias != 1 && !((i+1) % antialias))
{
b = buf;
for (j = 0; j < widtha; j += antialias)
{
val = 0;
for (x = 0; x < antialias; x++)
val += vals[j + x];
*b++ = (guchar) (val / antialias2);
}
pixel_region_set_row (&maskPR, 0, (i / antialias), sc->width, buf);
}
}
g_free (vals);
g_free (buf);
return mask;
}