Commit b0e08ede authored by Owen Taylor's avatar Owen Taylor Committed by Owen Taylor

Added new files for handling scan-converted convex polygons. (From gsumi)

Sun Jun 14 18:37:06 1998  Owen Taylor  <otaylor@gtk.org>

	* Makefile.am app/blob.[ch] (blob_bounds): Added new files for
	handling scan-converted convex polygons. (From
	gsumi) (these will be moved out to a plug-in tool directory
	when such a thing exists)

	* app/ink.[ch]: New tool for drawing with a hard-edged
	pressure and tilt-sensitive brush.

	* gimprc.c interface.c paint_core.c pixmaps.h tools.[ch]
	All the modifications for adding a new tool. We need
	to fix this.
parent f741ad24
Sun Jun 14 18:37:06 1998 Owen Taylor <otaylor@gtk.org>
* app/blob.[ch] (blob_bounds): Added new files for
handling scan-converted convex polygons. (From
gsumi) (these will be moved out to a plug-in tool directory
when such a thing exists)
* app/ink.[ch]: New tool for drawing with a hard-edged
pressure and tilt-sensitive brush.
Sun Jun 14 16:03:29 EDT 1998 Adrian Likins <adrian@gimp.org>
* TODO: cleaned up and expanded the todo
......
......@@ -23,6 +23,8 @@ gimp_SOURCES = \
bezier_selectP.h \
blend.c \
blend.h \
blob.c \
blob.h \
boundary.c \
boundary.h \
brightness_contrast.c \
......@@ -161,6 +163,8 @@ gimp_SOURCES = \
info_dialog.h \
info_window.c \
info_window.h \
ink.c \
ink.h \
install.c \
install.h \
interface.c \
......
/* blob.c: routines for manipulating scan converted convex
* polygons.
*
* Copyright 1998, Owen Taylor <otaylor@gtk.org>
*
* > Please contact the above author before modifying the copy <
* > of this file in the GIMP distribution. Thanks. <
*
* 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 "blob.h"
#include "glib.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ROUND(A) floor((A)+0.5)
static Blob *
blob_new (int y, int height)
{
Blob *result;
result = g_malloc (sizeof (Blob) + sizeof(BlobSpan) * height);
result->y = y;
result->height = height;
return result;
}
typedef enum {
NONE = 0,
LEFT = 1 << 0,
RIGHT = 1 << 1,
} EdgeType;
Blob *
blob_convex_union (Blob *b1, Blob *b2)
{
Blob *result;
int y, x1, x2, y1, y2, i1, i2;
int i, j;
int start;
EdgeType *present;
/* Create the storage for the result */
y = MIN(b1->y,b2->y);
result = blob_new (y, MAX(b1->y+b1->height,b2->y+b2->height)-y);
if (result->height == 0)
return result;
present = g_new (EdgeType, result->height);
memset (present, 0, result->height * sizeof(EdgeType));
/* Initialize spans from original objects */
for (i=0, j=b1->y-y; i<b1->height; i++,j++)
{
if (b1->data[i].right >= b1->data[i].left)
{
present[j] = LEFT | RIGHT;
result->data[j].left = b1->data[i].left;
result->data[j].right = b1->data[i].right;
}
}
for (i=0, j=b2->y-y; i<b2->height; i++,j++)
{
if (b2->data[i].right >= b2->data[i].left)
{
if (present[j])
{
if (result->data[j].left > b2->data[i].left)
result->data[j].left = b2->data[i].left;
if (result->data[j].right < b2->data[i].right)
result->data[j].right = b2->data[i].right;
}
else
{
present[j] = LEFT | RIGHT;
result->data[j].left = b2->data[i].left;
result->data[j].right = b2->data[i].right;
}
}
}
/* Now walk through edges, deleting points that aren't on convex hull */
start = 0;
while (!(present[start])) start++;
/* left edge */
i1 = start-1;
i2 = start;
x1 = result->data[start].left - result->data[start].right;
y1 = 0;
for (i=start+1;i<result->height;i++)
{
if (!(present[i] & LEFT))
continue;
x2 = result->data[i].left - result->data[i2].left;
y2 = i-i2;
while (x2*y1 - x1*y2 < 0) /* clockwise rotation */
{
present[i2] &= ~LEFT;
i2 = i1;
while (!(present[--i1] & LEFT) && i1>=start);
if (i1<start)
{
x1 = result->data[start].left - result->data[start].right;
y1 = 0;
}
else
{
x1 = result->data[i2].left - result->data[i1].left;
y1 = i2 - i1;
}
x2 = result->data[i].left - result->data[i2].left;
y2 = i - i2;
}
x1 = x2;
y1 = y2;
i1 = i2;
i2 = i;
}
/* Right edge */
i1 = start -1;
i2 = start;
x1 = result->data[start].right - result->data[start].left;
y1 = 0;
for (i=start+1;i<result->height;i++)
{
if (!(present[i] & RIGHT))
continue;
x2 = result->data[i].right - result->data[i2].right;
y2 = i-i2;
while (x2*y1 - x1*y2 > 0) /* counter-clockwise rotation */
{
present[i2] &= ~RIGHT;
i2 = i1;
while (!(present[--i1] & RIGHT) && i1>=start);
if (i1<start)
{
x1 = result->data[start].right - result->data[start].left;
y1 = 0;
}
else
{
x1 = result->data[i2].right - result->data[i1].right;
y1 = i2 - i1;
}
x2 = result->data[i].right - result->data[i2].right;
y2 = i - i2;
}
x1 = x2;
y1 = y2;
i1 = i2;
i2 = i;
}
/* Restore edges of spans that were deleted in last step or never present */
/* We fill only interior regions of convex hull, as if we were filling
polygons. But since we draw ellipses with nearest points, not interior
points, maybe it would look better if we did the same here. Probably
not a big deal either way after anti-aliasing */
/* left edge */
for (i1=start; i1<result->height-1; i1++)
{
/* Find empty gaps */
if (!(present[i1+1] & LEFT))
{
int increment; /* fractional part */
int denom; /* denominator of fraction */
int step; /* integral step */
int frac; /* fractional step */
int reverse;
/* find bottom of gap */
i2 = i1+2;
while (!(present[i2] & LEFT) && i2 < result->height) i2++;
if (i2 < result->height)
{
denom = i2-i1;
x1 = result->data[i1].left;
x2 = result->data[i2].left;
step = (x2-x1)/denom;
frac = x2-x1 - step*denom;
if (frac < 0)
{
frac = -frac;
reverse = 1;
}
else
reverse = 0;
increment = 0;
for (i=i1+1; i<i2; i++)
{
x1 += step;
increment += frac;
if (increment >= denom)
{
increment -= denom;
x1 += reverse ? -1 : 1;
}
if (increment == 0 || reverse)
result->data[i].left = x1;
else
result->data[i].left = x1 + 1;
}
}
i1 = i2-1; /* advance to next possibility */
}
}
/* right edge */
for (i1=start; i1<result->height-1; i1++)
{
/* Find empty gaps */
if (!(present[i1+1] & RIGHT))
{
int increment; /* fractional part */
int denom; /* denominator of fraction */
int step; /* integral step */
int frac; /* fractional step */
int reverse;
/* find bottom of gap */
i2 = i1+2;
while (!(present[i2] & RIGHT) && i2 < result->height) i2++;
if (i2 < result->height)
{
denom = i2-i1;
x1 = result->data[i1].right;
x2 = result->data[i2].right;
step = (x2-x1)/denom;
frac = x2-x1 - step*denom;
if (frac < 0)
{
frac = -frac;
reverse = 1;
}
else
reverse = 0;
increment = 0;
for (i=i1+1; i<i2; i++)
{
x1 += step;
increment += frac;
if (increment >= denom)
{
increment -= denom;
x1 += reverse ? -1 : 1;
}
if (reverse && increment != 0)
result->data[i].right = x1 - 1;
else
result->data[i].right = x1;
}
}
i1 = i2-1; /* advance to next possibility */
}
}
/* Mark empty lines at top and bottom as unused */
for (i=0;i<start;i++)
{
result->data[i].left = 0;
result->data[i].right = -1;
}
for (i=result->height-1;!present[i];i--)
{
result->data[i].left = 0;
result->data[i].right = -1;
}
g_free (present);
return result;
}
static void
blob_line_add_pixel (Blob *b, int x, int y)
{
if (b->data[y-b->y].left > b->data[y-b->y].right)
b->data[y-b->y].left = b->data[y-b->y].right = x;
else
{
b->data[y-b->y].left = MIN (b->data[y-b->y].left, x);
b->data[y-b->y].right = MAX (b->data[y-b->y].right, x);
}
}
void
blob_line (Blob *b, int x0, int y0, int x1, int y1)
{
int dx, dy, d;
int incrE, incrNE;
int x, y;
int xstep = 1;
int ystep = 1;
dx = x1 - x0;
dy = y1 - y0;
if (dx < 0)
{
dx = -dx;
xstep = -1;
}
if (dy < 0)
{
dy = -dy;
ystep = -1;
}
/* for (y = y0; y != y1 + ystep ; y += ystep)
{
b->data[y-b->y].left = 0;
b->data[y-b->y].right = -1;
}*/
x = x0;
y = y0;
if (dy < dx)
{
d = 2*dy - dx; /* initial value of d */
incrE = 2 * dy; /* increment used for move to E */
incrNE = 2 * (dy-dx); /* increment used for move to NE */
blob_line_add_pixel (b, x, y);
while (x != x1)
{
if (d <= 0)
{
d += incrE;
x += xstep;
}
else
{
d += incrNE;
x += xstep;
y += ystep;
}
blob_line_add_pixel (b, x, y);
}
}
else
{
d = 2*dx - dy; /* initial value of d */
incrE = 2 * dx; /* increment used for move to E */
incrNE = 2 * (dx-dy); /* increment used for move to NE */
blob_line_add_pixel (b, x, y);
while (y != y1)
{
if (d <= 0)
{
d += incrE;
y += ystep;
}
else
{
d += incrNE;
x += xstep;
y += ystep;
}
blob_line_add_pixel (b, x, y);
}
}
}
/****************************************************************
* Code to scan convert an arbitrary ellipse into a Blob. Based
* on Van Aken's conic algorithm in Foley and Van Damn
****************************************************************/
/* Return octant from gradient */
static int
blob_get_octant (int D, int E)
{
if (D>=0)
{
if (E<0)
return (D<-E) ? 1 : 2;
else
return (D>E) ? 3 : 4;
}
else
if (E>0)
return (-D<E) ? 5 : 6;
else
return (-D>-E) ? 7 : 8;
}
static void
blob_conic_add_pixel (Blob *b, EdgeType *present, int x, int y, int octant)
{
/* printf ("%d %d\n",x,y); */
if (y<b->y || y>=b->y+b->height)
{
/* g_warning("Out of bounds!\n"); */
}
else
{
if (octant <= 4)
{
if (present[y-b->y] & RIGHT)
b->data[y-b->y].right = MAX(b->data[y-b->y].right,x);
else
{
b->data[y-b->y].right = x;
present[y-b->y] |= RIGHT;
}
}
else
{
if (present[y-b->y] & LEFT)
b->data[y-b->y].left = MIN(b->data[y-b->y].left,x);
else
{
b->data[y-b->y].left = x;
present[y-b->y] |= LEFT;
}
}
}
}
static void
blob_conic (Blob *b, int xs, int ys,
int A, int B, int C, int D, int E, int F)
{
int x,y; /* current point */
int octant; /* current octant */
int dxsquare, dysquare; /* change in (x,y) for square moves */
int dxdiag, dydiag; /* change in (x,y) for diagonal moves */
int d,u,v,k1,k2,k3; /* decision variables and increments */
int octantCount; /* number of octants to be drawn */
int count; /* number of steps for last octant */
int tmp, i;
EdgeType *present;
present = g_new (EdgeType, b->height);
memset (present, 0, b->height * sizeof(EdgeType));
octant = blob_get_octant (D,E);
switch (octant)
{
case 1:
d = ROUND (A+B/2.+C/4.+D+E/2.+F);
u = ROUND (A+B/2.+D);
v = ROUND (A+B/2.+D+E);
k1 = 2*A;
k2 = 2*A + B;
k3 = k2 + B + 2*C;
dxsquare = 1;
dysquare = 0;
dxdiag = 1;
dydiag = 1;
break;
case 2:
d = ROUND (A/4.+B/2.+C+D/2.+E+F);
u = ROUND (B/2.+C+E);
v = ROUND (B/2.+C+D+E);
k1 = 2*C;
k2 = B + 2*C;
k3 = 2*A + 2*B + 2*C;
dxsquare = 0;
dysquare = 1;
dxdiag = 1;
dydiag = 1;
break;
case 3:
d = ROUND (A/4.-B/2.+C-D/2.+E+F);
u = ROUND (-B/2.+C+E);
v = ROUND (-B/2.+C-D+E);
k1 = 2*C;
k2 = 2*C - B;
k3 = 2*A - 2*B + 2*C;
dxsquare = 0;
dysquare = 1;
dxdiag = -1;
dydiag = 1;
break;
case 4:
d = ROUND (A-B/2.+C/4.-D+E/2.+F);
u = ROUND (A-B/2.-D);
v = ROUND (A-B/2.-D+E);
k1 = 2*A;
k2 = 2*A - B;
k3 = k2 - B + 2*C;
dxsquare = -1;
dysquare = 0;
dxdiag = -1;
dydiag = 1;
break;
case 5:
d = ROUND (A+B/2.+C/4.-D-E/2.+F);
u = ROUND (A+B/2.-D);
v = ROUND (A+B/2.-D-E);
k1 = 2*A;
k2 = 2*A + B;
k3 = k2 + B + 2*C;
dxsquare = -1;
dysquare = 0;
dxdiag = -1;
dydiag = -1;
break;
case 6:
d = ROUND (A/4.+B/2.+C-D/2.-E+F);
u = ROUND (B/2.+C-E);
v = ROUND (B/2.+C-D-E);
k1 = 2*C;
k2 = B + 2*C;
k3 = 2*A + 2*B + 2*C;
dxsquare = 0;
dysquare = -1;
dxdiag = -1;
dydiag = -1;
break;
case 7:
d = ROUND (A/4.-B/2.+C+D/2.-E+F);
u = ROUND (-B/2.+C-E);
v = ROUND (-B/2.+C+D-E);
k1 = 2*C;
k2 = 2*C - B;
k3 = 2*A - 2*B + 2*C;
dxsquare = 0;
dysquare = -1;
dxdiag = 1;
dydiag = -1;
break;
default: /* case 8: */
d = ROUND (A-B/2.+C/4.+D-E/2.+F);
u = ROUND (A-B/2.+D);
v = ROUND (A-B/2.+D-E);
k1 = 2*A;
k2 = 2*A - B;
k3 = k2 - B + 2*C;
dxsquare = 1;
dysquare = 0;
dxdiag = 1;
dydiag = -1;
break;
}
octantCount = 8;
x = xs;
y = ys;
count = 0; /* ignore until last octant */
/* Initialize boundary checking - we keep track of the discriminants
for the conic as quadratics in x and y, and when they go negative
we know we are beyond the boundaries of the conic. */
while (1)
{
if (octantCount == 0)
{
/* figure out remaining steps in square direction */
switch (octant)
{
case 1:
case 8:
count = xs - x;
break;
case 2:
case 3:
count = ys - y;