diff packages/services/gfx/mw/current/src/engine/devarc.c @ 208:e0c0827131d1 ecos

Merge from eCos master repository on 2002-05-20-20:11:54-BST
author jlarmour
date Mon, 20 May 2002 22:19:26 +0000
parents
children
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/services/gfx/mw/current/src/engine/devarc.c
@@ -0,0 +1,589 @@
+/*
+ * Copyright (c) 2000-2001 Greg Haerr <greg@censoft.com>
+ *
+ * Device-independent arc, pie and ellipse routines.
+ * GdArc is integer only and requires start/end points.
+ * GdArcAngle requires floating point and uses angles.
+ * GdArcAngle uses qsin() and qcos() instead of sin() / cos() 
+ * so no math lib needed.
+ *
+ * Portions Copyright (c) 1991 David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * Arc line clipping and integer qsin/qcos routines used by permission:
+ * Copyright (C) 1997-1998 by Eero Tamminen
+ * Bugfixed by Greg Haerr
+ */
+
+#include <stdio.h>
+#include "device.h"
+
+#if HAVEFLOAT			/* =1 compiles in GdArcAngle*/
+#define HIGHPRECISION	0	/* =1 for high precision angles, uses mathlib*/
+
+#if !HIGHPRECISION
+typedef float	FLOAT;
+/*
+ * qsin/qcos - calculate sin() and cos() approximations from a lookup table
+ *
+ * This uses a cosine lookup table of 0-90 degrees at one degree steps
+ * with the difference between successive values used for interpolation.
+ * The achieved accuracy should be about +/-0.0001.  If you want more
+ * accuracy, use doubles and smaller steps.  If you want more speed, use
+ * fixed point arithmetics.
+ */
+static float cosine[91][2] = {
+	{ 1.000000, -1.523048e-04 },
+	{ 0.999848, -4.568681e-04 },
+	{ 0.999391, -7.612923e-04 },
+	{ 0.998630, -1.065484e-03 },
+	{ 0.997564, -1.369352e-03 },
+	{ 0.996195, -1.672803e-03 },
+	{ 0.994522, -1.975744e-03 },
+	{ 0.992546, -2.278083e-03 },
+	{ 0.990268, -2.579728e-03 },
+	{ 0.987688, -2.880588e-03 },
+	{ 0.984808, -3.180570e-03 },
+	{ 0.981627, -3.479583e-03 },
+	{ 0.978148, -3.777536e-03 },
+	{ 0.974370, -4.074339e-03 },
+	{ 0.970296, -4.369900e-03 },
+	{ 0.965926, -4.664130e-03 },
+	{ 0.961262, -4.956940e-03 },
+	{ 0.956305, -5.248240e-03 },
+	{ 0.951057, -5.537941e-03 },
+	{ 0.945519, -5.825955e-03 },
+	{ 0.939693, -6.112194e-03 },
+	{ 0.933580, -6.396572e-03 },
+	{ 0.927184, -6.679001e-03 },
+	{ 0.920505, -6.959396e-03 },
+	{ 0.913545, -7.237671e-03 },
+	{ 0.906308, -7.513741e-03 },
+	{ 0.898794, -7.787522e-03 },
+	{ 0.891007, -8.058931e-03 },
+	{ 0.882948, -8.327886e-03 },
+	{ 0.874620, -8.594303e-03 },
+	{ 0.866025, -8.858103e-03 },
+	{ 0.857167, -9.119205e-03 },
+	{ 0.848048, -9.377528e-03 },
+	{ 0.838671, -9.632995e-03 },
+	{ 0.829038, -9.885528e-03 },
+	{ 0.819152, -1.013505e-02 },
+	{ 0.809017, -1.038148e-02 },
+	{ 0.798636, -1.062476e-02 },
+	{ 0.788011, -1.086479e-02 },
+	{ 0.777146, -1.110152e-02 },
+	{ 0.766044, -1.133486e-02 },
+	{ 0.754710, -1.156475e-02 },
+	{ 0.743145, -1.179112e-02 },
+	{ 0.731354, -1.201390e-02 },
+	{ 0.719340, -1.223302e-02 },
+	{ 0.707107, -1.244841e-02 },
+	{ 0.694658, -1.266001e-02 },
+	{ 0.681998, -1.286775e-02 },
+	{ 0.669131, -1.307158e-02 },
+	{ 0.656059, -1.327142e-02 },
+	{ 0.642788, -1.346722e-02 },
+	{ 0.629320, -1.365892e-02 },
+	{ 0.615661, -1.384645e-02 },
+	{ 0.601815, -1.402977e-02 },
+	{ 0.587785, -1.420882e-02 },
+	{ 0.573576, -1.438353e-02 },
+	{ 0.559193, -1.455387e-02 },
+	{ 0.544639, -1.471977e-02 },
+	{ 0.529919, -1.488119e-02 },
+	{ 0.515038, -1.503807e-02 },
+	{ 0.500000, -1.519038e-02 },
+	{ 0.484810, -1.533806e-02 },
+	{ 0.469472, -1.548106e-02 },
+	{ 0.453990, -1.561935e-02 },
+	{ 0.438371, -1.575289e-02 },
+	{ 0.422618, -1.588162e-02 },
+	{ 0.406737, -1.600551e-02 },
+	{ 0.390731, -1.612454e-02 },
+	{ 0.374607, -1.623864e-02 },
+	{ 0.358368, -1.634781e-02 },
+	{ 0.342020, -1.645199e-02 },
+	{ 0.325568, -1.655116e-02 },
+	{ 0.309017, -1.664529e-02 },
+	{ 0.292372, -1.673435e-02 },
+	{ 0.275637, -1.681831e-02 },
+	{ 0.258819, -1.689715e-02 },
+	{ 0.241922, -1.697084e-02 },
+	{ 0.224951, -1.703936e-02 },
+	{ 0.207912, -1.710270e-02 },
+	{ 0.190809, -1.716082e-02 },
+	{ 0.173648, -1.721371e-02 },
+	{ 0.156434, -1.726136e-02 },
+	{ 0.139173, -1.730376e-02 },
+	{ 0.121869, -1.734088e-02 },
+	{ 0.104528, -1.737272e-02 },
+	{ 0.087156, -1.739927e-02 },
+	{ 0.069756, -1.742052e-02 },
+	{ 0.052336, -1.743646e-02 },
+	{ 0.034899, -1.744709e-02 },
+	{ 0.017452, -1.745241e-02 },
+	{ 0.000000, -1.745241e-02 }
+};
+
+static float
+qcos(FLOAT angle)
+{
+	short a, b, c;
+
+	a = angle;
+	if (a < 0) {
+		angle = a - angle;
+		a = -a;
+	} else {
+		angle = angle - a;
+	}
+	b = a / 90;
+	c = a - b * 90;
+
+	/* interpolate according to angle */
+	switch(b&3) {
+		case 3:
+			c = 90 - c;
+			return cosine[c][0] - cosine[c-1][1] * angle;
+		case 2:
+			return -(cosine[c][0] + cosine[c][1] * angle);
+		case 1:
+			c = 90 - c;
+			return cosine[c-1][1] * angle - cosine[c][0];
+		default:
+			return cosine[c][0] + cosine[c][1] * angle;
+	}
+}
+
+static float
+qsin(FLOAT angle)
+{
+	short a, b, c;
+
+	/* change to cosine by subtracting 90 */
+	a = (int)angle - 90;
+	if (a < 0) {
+		angle = (a + 90) - angle;
+		a = -a;
+	} else {
+		angle = angle - (a + 90);
+	}
+	b = a / 90;
+	c = a - b * 90;
+
+	/* interpolate according to angle */
+	switch(b&3) {
+		case 3:
+			c = 90 - c;
+			return cosine[c][0] - cosine[c-1][1] * angle;
+		case 2:
+			return -(cosine[c][0] + cosine[c][1] * angle);
+		case 1:
+			c = 90 - c;
+			return cosine[c-1][1] * angle - cosine[c][0];
+		default:
+			return cosine[c][0] + cosine[c][1] * angle;
+	}
+}
+#else /* HIGHPRECISION*/
+
+#include <math.h>
+#define qcos	QCOS
+#define qsin	QSIN
+typedef double	FLOAT;
+
+FLOAT QCOS(FLOAT a)
+{
+	return cos(a * M_PI / 180.);
+}
+
+FLOAT QSIN(FLOAT a)
+{
+	return sin(a * M_PI / 180.);
+}
+#endif /* HIGHPRECISION*/
+#endif /* HAVEFLOAT*/
+
+/* 
+ * Draw an arc or pie, angles are specified in 64th's of a degree.
+ * This function requires floating point, use GdArc for integer only.
+ */
+void
+GdArcAngle(PSD psd, MWCOORD x0, MWCOORD y0, MWCOORD rx, MWCOORD ry,
+	MWCOORD angle1, MWCOORD angle2, int type)
+{
+#if HAVEFLOAT
+	MWCOORD	ax, ay, bx, by;
+	FLOAT	a, b;
+
+	/* calculate pie edge offsets from center to the ellipse rim */
+	ax = qcos(angle1/64.) * rx;
+	bx = qcos(angle2/64.) * rx;
+
+	a = -qsin(angle1/64.);
+	b = -qsin(angle2/64.);
+	ay = a * ry;
+	by = b * ry;
+
+	/* call integer routine*/
+	GdArc(psd, x0, y0, rx, ry, ax, ay, bx, by, type);
+#endif /* HAVEFLOAT*/
+}
+
+/* argument holder for pie, arc and ellipse functions*/
+typedef struct {
+	PSD	psd;
+	MWCOORD	x0, y0;
+	MWCOORD	rx, ry;
+	MWCOORD	ax, ay;
+	MWCOORD	bx, by;
+	int	adir, bdir;
+	int	type;
+} SLICE;
+
+extern void drawpoint(PSD psd, MWCOORD x, MWCOORD y);
+extern void drawrow(PSD psd, MWCOORD x1, MWCOORD x2, MWCOORD y);
+
+/*
+ * Clip a line segment for arc or pie drawing.
+ * Returns 0 if line is clipped or on acceptable side, 1 if it's vertically
+ * on other side, otherwise 3.
+ */
+static int
+clip_line(SLICE *slice, MWCOORD xe, MWCOORD ye, int dir, MWCOORD y, MWCOORD *x0,
+	MWCOORD *x1)
+{
+#if 0
+	/*
+	 * kluge: handle 180 degree case
+	 */
+	if (y >= 0 && ye == 0) {
+/*printf("cl %d,%d %d,%d %d,%d %d,%d %d,%d\n", xe, ye, y, dir,
+slice->ax, slice->ay, slice->bx, slice->by, slice->adir, slice->bdir);*/
+		/* bottom 180*/
+		if (slice->adir < 0) {
+			if (slice->ay || slice->by)
+				return 1;
+			if (slice->ax == -slice->bx)
+				return 0;
+		}
+		return 3;
+	}
+#endif
+	/* hline on the same vertical side with the given edge? */
+	if ((y >= 0 && ye >= 0) || (y < 0 && ye < 0)) {
+		MWCOORD x;
+
+		if (ye == 0) x = xe; else
+		x = (MWCOORD)(long)xe * y / ye;
+
+		if (x >= *x0 && x <= *x1) {
+			if (dir > 0)
+				*x0 = x;
+			else
+				*x1 = x;
+			return 0;
+		} else {
+			if (dir > 0) {
+				if (x <= *x0)
+					return 0;
+			} else {
+				if (x >= *x1)
+					return 0;
+			}
+		}
+		return 3;
+	}
+	return 1;
+}
+
+/* relative offsets, direction from left to right. */
+static void
+draw_line(SLICE *slice, MWCOORD x0, MWCOORD y, MWCOORD x1)
+{
+	int	dbl = (slice->adir > 0 && slice->bdir < 0);
+	int 	discard, ret;
+	MWCOORD	x2 = x0, x3 = x1;
+
+	if (y == 0) {
+		if (slice->type != MWPIE)
+			return;
+		/* edges on different sides */
+		if ((slice->ay <= 0 && slice->by >= 0) ||
+		    (slice->ay >= 0 && slice->by <= 0)) {
+			if (slice->adir < 0)  {
+				if (x1 > 0)
+					x1 = 0;
+			}
+			if (slice->bdir > 0) {
+				if (x0 < 0)
+					x0 = 0;
+			}
+		} else {
+			if (!dbl) {
+				/* FIXME leaving in draws dot in center*/
+				drawpoint(slice->psd, slice->x0, slice->y0);
+				return;
+			}
+		}
+		drawrow(slice->psd, slice->x0 + x0, slice->x0 + x1, slice->y0);
+		return;
+	}
+
+	/* clip left edge / line */
+	ret = clip_line(slice, slice->ax, slice->ay, slice->adir, y, &x0, &x1);
+
+	if (dbl) {
+		if (!ret) {
+			/* edges separate line to two parts */
+			drawrow(slice->psd, slice->x0 + x0, slice->x0 + x1,
+				slice->y0 + y);
+			x0 = x2;
+			x1 = x3;
+		}
+	} else {
+		if (ret > 1) {
+			return;
+		}
+	}
+
+	discard = ret;
+	ret = clip_line(slice, slice->bx, slice->by, slice->bdir, y, &x0, &x1);
+
+	discard += ret;
+	if (discard > 2 && !(dbl && ret == 0 && discard == 3)) {
+		return;
+	}
+	if (discard == 2) {
+		/* line on other side than slice */
+		if (slice->adir < 0 || slice->bdir > 0) {
+			return;
+		}
+	}
+	drawrow(slice->psd, slice->x0 + x0, slice->x0 + x1, slice->y0 + y);
+}
+
+/* draw one line segment or set of points, called from drawarc routine*/
+static void
+drawarcsegment(SLICE *slice, MWCOORD xp, MWCOORD yp)
+{
+	switch (slice->type) {
+	case MWELLIPSEFILL:
+		/* draw ellipse fill segment*/
+		drawrow(slice->psd, slice->x0-xp, slice->x0+xp, slice->y0-yp);
+		drawrow(slice->psd, slice->x0-xp, slice->x0+xp, slice->y0+yp);
+		return;
+
+	case MWELLIPSE:
+		/* set four points symmetrically situated around a point*/
+		drawpoint(slice->psd, slice->x0 + xp, slice->y0 + yp);
+		drawpoint(slice->psd, slice->x0 - xp, slice->y0 + yp);
+		drawpoint(slice->psd, slice->x0 + xp, slice->y0 - yp);
+		drawpoint(slice->psd, slice->x0 - xp, slice->y0 - yp);
+		return;
+
+	case MWPIE:
+		/* draw top and bottom halfs of pie*/
+		draw_line(slice, -xp, -yp, +xp);
+		draw_line(slice, -xp, +yp, +xp);
+		return;
+
+	default:	/* MWARC, MWARCOUTLINE*/
+		/* set four points symmetrically around a point and clip*/
+		draw_line(slice, +xp, +yp, +xp);
+		draw_line(slice, -xp, +yp, -xp);
+		draw_line(slice, +xp, -yp, +xp);
+		draw_line(slice, -xp, -yp, -xp);
+		return;
+	}
+}
+
+/* General routine to plot points on an arc.  Used by arc, pie and ellipse*/
+static void
+drawarc(SLICE *slice)
+{
+	MWCOORD xp, yp;		/* current point (based on center) */
+	MWCOORD rx, ry;
+	long Asquared;		/* square of x semi axis */
+	long TwoAsquared;
+	long Bsquared;		/* square of y semi axis */
+	long TwoBsquared;
+	long d;
+	long dx, dy;
+
+	rx = slice->rx;
+	ry = slice->ry;
+
+	xp = 0;
+	yp = ry;
+	Asquared = rx * rx;
+	TwoAsquared = 2 * Asquared;
+	Bsquared = ry * ry;
+	TwoBsquared = 2 * Bsquared;
+	d = Bsquared - Asquared * ry + (Asquared >> 2);
+	dx = 0;
+	dy = TwoAsquared * ry;
+
+	while (dx < dy) {
+		drawarcsegment(slice, xp, yp);
+		if (d > 0) {
+			yp--;
+			dy -= TwoAsquared;
+			d -= dy;
+		}
+		xp++;
+		dx += TwoBsquared;
+		d += (Bsquared + dx);
+	}
+
+	d += ((3L * (Asquared - Bsquared) / 2L - (dx + dy)) >> 1);
+
+	while (yp >= 0) {
+		drawarcsegment(slice, xp, yp);
+		if (d < 0) {
+			xp++;
+			dx += TwoBsquared;
+			d += dx;
+		}
+		yp--;
+		dy -= TwoAsquared;
+		d += (Asquared - dy);
+	}
+
+}
+
+/* 
+ * Draw an arc or pie using start/end points.
+ * Integer only routine.  To specify start/end angles, 
+ * use GdArcAngle, which requires floating point.
+ */
+void
+GdArc(PSD psd, MWCOORD x0, MWCOORD y0, MWCOORD rx, MWCOORD ry,
+	MWCOORD ax, MWCOORD ay, MWCOORD bx, MWCOORD by, int type)
+{
+	MWCOORD	adir, bdir;
+	SLICE	slice;
+
+	if (rx <= 0 || ry <= 0)
+		return;
+
+	/*
+	 * Calculate right/left side clipping, based on quadrant.
+	 * dir is positive when right side is filled and negative when
+	 * left side is to be filled.
+	 *
+	 * >= 0 is bottom half
+	 */
+	if (ay >= 0)
+		adir = 1;
+	else
+		adir = -1;
+
+	if (by >= 0)
+		bdir = -1;
+	else
+		bdir = 1;
+
+	/*
+	 * The clip_line routine has problems around the 0 and
+	 * 180 degree axes.
+	 * This <fix> is required to make the clip_line algorithm
+	 * work.  Getting these routines to work for all angles is
+	 * a bitch.  And they're still buggy.  Doing this causes
+	 * half circles to be outlined with a slightly bent line
+	 * on the x axis. FIXME
+	 */
+	if (ay == 0) ++ay;
+	if (by == 0) ++by;
+
+	/* swap rightmost edge first */
+	if (bx > ax) {
+		MWCOORD swap;
+
+		swap = ax;
+		ax = bx;
+		bx = swap;
+
+		swap = ay;
+		ay = by;
+		by = swap;
+
+		swap = adir;
+		adir = bdir;
+		bdir = swap;
+	}
+
+	/* check for entire area clipped, draw with per-point clipping*/
+	if (GdClipArea(psd, x0-rx, y0-ry, x0+rx, y0+ry) == CLIP_INVISIBLE)
+		return;
+
+	slice.psd = psd;
+	slice.x0 = x0;
+	slice.y0 = y0;
+	slice.rx = rx;
+	slice.ry = ry;
+	slice.ax = ax;
+	slice.ay = ay;
+	slice.bx = bx;
+	slice.by = by;
+	slice.adir = adir;
+	slice.bdir = bdir;
+	slice.type = type;
+
+	drawarc(&slice);
+
+	if (type & MWOUTLINE) {
+		/* draw two lines from rx,ry to arc endpoints*/
+		GdLine(psd, x0, y0, x0+ax, y0+ay, TRUE);
+		GdLine(psd, x0, y0, x0+bx, y0+by, TRUE);
+	}
+
+	GdFixCursor(psd);
+}
+
+/*
+ * Draw an ellipse using the current clipping region and foreground color.
+ * This draws in the outline of the ellipse, or fills it.
+ * Integer only routine.
+ */
+void
+GdEllipse(PSD psd, MWCOORD x, MWCOORD y, MWCOORD rx, MWCOORD ry, MWBOOL fill)
+{
+	SLICE	slice;
+
+	if (rx < 0 || ry < 0)
+		return;
+
+	/* Check if the ellipse bounding box is either totally visible
+	 * or totally invisible.  Draw with per-point clipping.
+	 */
+	switch (GdClipArea(psd, x - rx, y - ry, x + rx, y + ry)) {
+	case CLIP_VISIBLE:
+		/*
+		 * For size considerations, there's no low-level ellipse
+		 * draw, so we've got to draw all ellipses
+		 * with per-point clipping for the time being
+		psd->DrawEllipse(psd, x, y, rx, ry, fill, gr_foreground);
+		GdFixCursor(psd);
+		return;
+		 */
+		break;
+
+	case CLIP_INVISIBLE:
+		return;
+  	}
+
+	slice.psd = psd;
+	slice.x0 = x;
+	slice.y0 = y;
+	slice.rx = rx;
+	slice.ry = ry;
+	slice.type = fill? MWELLIPSEFILL: MWELLIPSE;
+	/* other elements unused*/
+
+	drawarc(&slice);
+
+	GdFixCursor(psd);
+}