miarc.c revision 4642e01f
105b261ecSmrg/***********************************************************
205b261ecSmrg
305b261ecSmrgCopyright 1987, 1998  The Open Group
405b261ecSmrg
505b261ecSmrgPermission to use, copy, modify, distribute, and sell this software and its
605b261ecSmrgdocumentation for any purpose is hereby granted without fee, provided that
705b261ecSmrgthe above copyright notice appear in all copies and that both that
805b261ecSmrgcopyright notice and this permission notice appear in supporting
905b261ecSmrgdocumentation.
1005b261ecSmrg
1105b261ecSmrgThe above copyright notice and this permission notice shall be included in
1205b261ecSmrgall copies or substantial portions of the Software.
1305b261ecSmrg
1405b261ecSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1505b261ecSmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1605b261ecSmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
1705b261ecSmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
1805b261ecSmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1905b261ecSmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2005b261ecSmrg
2105b261ecSmrgExcept as contained in this notice, the name of The Open Group shall not be
2205b261ecSmrgused in advertising or otherwise to promote the sale, use or other dealings
2305b261ecSmrgin this Software without prior written authorization from The Open Group.
2405b261ecSmrg
2505b261ecSmrg
2605b261ecSmrgCopyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
2705b261ecSmrg
2805b261ecSmrg                        All Rights Reserved
2905b261ecSmrg
3005b261ecSmrgPermission to use, copy, modify, and distribute this software and its
3105b261ecSmrgdocumentation for any purpose and without fee is hereby granted,
3205b261ecSmrgprovided that the above copyright notice appear in all copies and that
3305b261ecSmrgboth that copyright notice and this permission notice appear in
3405b261ecSmrgsupporting documentation, and that the name of Digital not be
3505b261ecSmrgused in advertising or publicity pertaining to distribution of the
3605b261ecSmrgsoftware without specific, written prior permission.
3705b261ecSmrg
3805b261ecSmrgDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
3905b261ecSmrgALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
4005b261ecSmrgDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
4105b261ecSmrgANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
4205b261ecSmrgWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
4305b261ecSmrgARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
4405b261ecSmrgSOFTWARE.
4505b261ecSmrg
4605b261ecSmrg******************************************************************/
4705b261ecSmrg/* Author: Keith Packard and Bob Scheifler */
4805b261ecSmrg/* Warning: this code is toxic, do not dally very long here. */
4905b261ecSmrg
5005b261ecSmrg#ifdef HAVE_DIX_CONFIG_H
5105b261ecSmrg#include <dix-config.h>
5205b261ecSmrg#endif
5305b261ecSmrg
5405b261ecSmrg#include <math.h>
5505b261ecSmrg#include <X11/X.h>
5605b261ecSmrg#include <X11/Xprotostr.h>
5705b261ecSmrg#include "misc.h"
5805b261ecSmrg#include "gcstruct.h"
5905b261ecSmrg#include "scrnintstr.h"
6005b261ecSmrg#include "pixmapstr.h"
6105b261ecSmrg#include "windowstr.h"
6205b261ecSmrg#include "mifpoly.h"
6305b261ecSmrg#include "mi.h"
6405b261ecSmrg#include "mifillarc.h"
6505b261ecSmrg#include <X11/Xfuncproto.h>
6605b261ecSmrg
6705b261ecSmrgstatic double miDsin(double a);
6805b261ecSmrgstatic double miDcos(double a);
6905b261ecSmrgstatic double miDasin(double v);
7005b261ecSmrgstatic double miDatan2(double dy, double dx);
7105b261ecSmrg
7205b261ecSmrg#ifndef HAVE_CBRT
7305b261ecSmrgstatic double
7405b261ecSmrgcbrt(double x)
7505b261ecSmrg{
7605b261ecSmrg    if (x > 0.0)
7705b261ecSmrg	return pow(x, 1.0/3.0);
7805b261ecSmrg    else
7905b261ecSmrg	return -pow(-x, 1.0/3.0);
8005b261ecSmrg}
8105b261ecSmrg#endif
8205b261ecSmrg
8305b261ecSmrg/*
8405b261ecSmrg * some interesting sematic interpretation of the protocol:
8505b261ecSmrg *
8605b261ecSmrg * Self intersecting arcs (i.e. those spanning 360 degrees)
8705b261ecSmrg *  never join with other arcs, and are drawn without caps
8805b261ecSmrg *  (unless on/off dashed, in which case each dash segment
8905b261ecSmrg *  is capped, except when the last segment meets the
9005b261ecSmrg *  first segment, when no caps are drawn)
9105b261ecSmrg *
9205b261ecSmrg * double dash arcs are drawn in two parts, first the
9305b261ecSmrg *  odd dashes (drawn in background) then the even dashes
9405b261ecSmrg *  (drawn in foreground).  This means that overlapping
9505b261ecSmrg *  sections of foreground/background are drawn twice,
9605b261ecSmrg *  first in background then in foreground.  The double-draw
9705b261ecSmrg *  occurs even when the function uses the destination values
9805b261ecSmrg *  (e.g. xor mode).  This is the same way the wide-line
9905b261ecSmrg *  code works and should be "fixed".
10005b261ecSmrg *
10105b261ecSmrg */
10205b261ecSmrg
10305b261ecSmrg#undef max
10405b261ecSmrg#undef min
10505b261ecSmrg
10605b261ecSmrg_X_INLINE static int max (const int x, const int y)
10705b261ecSmrg{
10805b261ecSmrg	return x>y? x:y;
10905b261ecSmrg}
11005b261ecSmrg
11105b261ecSmrg_X_INLINE static int min (const int x, const int y)
11205b261ecSmrg{
11305b261ecSmrg	return x<y? x:y;
11405b261ecSmrg}
11505b261ecSmrg
11605b261ecSmrgstruct bound {
11705b261ecSmrg	double	min, max;
11805b261ecSmrg};
11905b261ecSmrg
12005b261ecSmrgstruct ibound {
12105b261ecSmrg	int	min, max;
12205b261ecSmrg};
12305b261ecSmrg
12405b261ecSmrg#define boundedLe(value, bounds)\
12505b261ecSmrg	((bounds).min <= (value) && (value) <= (bounds).max)
12605b261ecSmrg
12705b261ecSmrgstruct line {
12805b261ecSmrg	double	m, b;
12905b261ecSmrg	int	valid;
13005b261ecSmrg};
13105b261ecSmrg
13205b261ecSmrg#define intersectLine(y,line) (line.m * (y) + line.b)
13305b261ecSmrg
13405b261ecSmrg/*
13505b261ecSmrg * these are all y value bounds
13605b261ecSmrg */
13705b261ecSmrg
13805b261ecSmrgstruct arc_bound {
13905b261ecSmrg	struct bound	ellipse;
14005b261ecSmrg	struct bound	inner;
14105b261ecSmrg	struct bound	outer;
14205b261ecSmrg	struct bound	right;
14305b261ecSmrg	struct bound	left;
14405b261ecSmrg	struct ibound	inneri;
14505b261ecSmrg	struct ibound	outeri;
14605b261ecSmrg};
14705b261ecSmrg
14805b261ecSmrgstruct accelerators {
14905b261ecSmrg	double		tail_y;
15005b261ecSmrg	double		h2;
15105b261ecSmrg	double		w2;
15205b261ecSmrg	double		h4;
15305b261ecSmrg	double		w4;
15405b261ecSmrg	double		h2mw2;
15505b261ecSmrg	double		h2l;
15605b261ecSmrg	double		w2l;
15705b261ecSmrg	double		fromIntX;
15805b261ecSmrg	double		fromIntY;
15905b261ecSmrg	struct line	left, right;
16005b261ecSmrg	int		yorgu;
16105b261ecSmrg	int		yorgl;
16205b261ecSmrg	int		xorg;
16305b261ecSmrg};
16405b261ecSmrg
16505b261ecSmrgstruct arc_def {
16605b261ecSmrg	double	w, h, l;
16705b261ecSmrg	double	a0, a1;
16805b261ecSmrg};
16905b261ecSmrg
17005b261ecSmrg# define todeg(xAngle)	(((double) (xAngle)) / 64.0)
17105b261ecSmrg
17205b261ecSmrg# define RIGHT_END	0
17305b261ecSmrg# define LEFT_END	1
17405b261ecSmrg
17505b261ecSmrgtypedef struct _miArcJoin {
17605b261ecSmrg	int	arcIndex0, arcIndex1;
17705b261ecSmrg	int	phase0, phase1;
17805b261ecSmrg	int	end0, end1;
17905b261ecSmrg} miArcJoinRec, *miArcJoinPtr;
18005b261ecSmrg
18105b261ecSmrgtypedef struct _miArcCap {
18205b261ecSmrg	int		arcIndex;
18305b261ecSmrg	int		end;
18405b261ecSmrg} miArcCapRec, *miArcCapPtr;
18505b261ecSmrg
18605b261ecSmrgtypedef struct _miArcFace {
18705b261ecSmrg	SppPointRec	clock;
18805b261ecSmrg	SppPointRec	center;
18905b261ecSmrg	SppPointRec	counterClock;
19005b261ecSmrg} miArcFaceRec, *miArcFacePtr;
19105b261ecSmrg
19205b261ecSmrgtypedef struct _miArcData {
19305b261ecSmrg	xArc		arc;
19405b261ecSmrg	int		render;		/* non-zero means render after drawing */
19505b261ecSmrg	int		join;		/* related join */
19605b261ecSmrg	int		cap;		/* related cap */
19705b261ecSmrg	int		selfJoin;	/* final dash meets first dash */
19805b261ecSmrg	miArcFaceRec	bounds[2];
19905b261ecSmrg	double		x0, y0, x1, y1;
20005b261ecSmrg} miArcDataRec, *miArcDataPtr;
20105b261ecSmrg
20205b261ecSmrg/*
20305b261ecSmrg * This is an entire sequence of arcs, computed and categorized according
20405b261ecSmrg * to operation.  miDashArcs generates either one or two of these.
20505b261ecSmrg */
20605b261ecSmrg
20705b261ecSmrgtypedef struct _miPolyArc {
20805b261ecSmrg	int		narcs;
20905b261ecSmrg	miArcDataPtr	arcs;
21005b261ecSmrg	int		ncaps;
21105b261ecSmrg	miArcCapPtr	caps;
21205b261ecSmrg	int		njoins;
21305b261ecSmrg	miArcJoinPtr	joins;
21405b261ecSmrg} miPolyArcRec, *miPolyArcPtr;
21505b261ecSmrg
21605b261ecSmrg#define GCValsFunction		0
21705b261ecSmrg#define GCValsForeground 	1
21805b261ecSmrg#define GCValsBackground 	2
21905b261ecSmrg#define GCValsLineWidth 	3
22005b261ecSmrg#define GCValsCapStyle 		4
22105b261ecSmrg#define GCValsJoinStyle		5
22205b261ecSmrg#define GCValsMask		(GCFunction | GCForeground | GCBackground | \
22305b261ecSmrg				 GCLineWidth | GCCapStyle | GCJoinStyle)
22405b261ecSmrgstatic CARD32 gcvals[6];
22505b261ecSmrg
22605b261ecSmrgstatic void fillSpans(DrawablePtr pDrawable, GCPtr pGC);
22705b261ecSmrgstatic void newFinalSpan(int y, int xmin, int xmax);
22805b261ecSmrgstatic void drawArc(xArc *tarc, int l, int a0, int a1, miArcFacePtr right,
22905b261ecSmrg		    miArcFacePtr left);
23005b261ecSmrgstatic void drawZeroArc(DrawablePtr pDraw, GCPtr pGC, xArc *tarc, int lw,
23105b261ecSmrg			miArcFacePtr left, miArcFacePtr right);
23205b261ecSmrgstatic void miArcJoin(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pLeft,
23305b261ecSmrg		      miArcFacePtr pRight, int xOrgLeft, int yOrgLeft,
23405b261ecSmrg		      double xFtransLeft, double yFtransLeft,
23505b261ecSmrg		      int xOrgRight, int yOrgRight,
23605b261ecSmrg		      double xFtransRight, double yFtransRight);
23705b261ecSmrgstatic void miArcCap(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pFace,
23805b261ecSmrg		     int end, int xOrg, int yOrg, double xFtrans,
23905b261ecSmrg		     double yFtrans);
24005b261ecSmrgstatic void miRoundCap(DrawablePtr pDraw, GCPtr pGC, SppPointRec pCenter,
24105b261ecSmrg		       SppPointRec pEnd, SppPointRec pCorner,
24205b261ecSmrg		       SppPointRec pOtherCorner, int fLineEnd,
24305b261ecSmrg		       int xOrg, int yOrg, double xFtrans, double yFtrans);
24405b261ecSmrgstatic void miFreeArcs(miPolyArcPtr arcs, GCPtr pGC);
24505b261ecSmrgstatic miPolyArcPtr miComputeArcs(xArc *parcs, int narcs, GCPtr pGC);
24605b261ecSmrgstatic int miGetArcPts(SppArcPtr parc, int cpt, SppPointPtr *ppPts);
24705b261ecSmrg
24805b261ecSmrg# define CUBED_ROOT_2	1.2599210498948732038115849718451499938964
24905b261ecSmrg# define CUBED_ROOT_4	1.5874010519681993173435330390930175781250
25005b261ecSmrg
25105b261ecSmrg/*
25205b261ecSmrg * draw one segment of the arc using the arc spans generation routines
25305b261ecSmrg */
25405b261ecSmrg
25505b261ecSmrgstatic void
25605b261ecSmrgmiArcSegment(
25705b261ecSmrg    DrawablePtr   pDraw,
25805b261ecSmrg    GCPtr         pGC,
25905b261ecSmrg    xArc          tarc,
26005b261ecSmrg    miArcFacePtr	right,
26105b261ecSmrg    miArcFacePtr	left)
26205b261ecSmrg{
26305b261ecSmrg    int l = pGC->lineWidth;
26405b261ecSmrg    int a0, a1, startAngle, endAngle;
26505b261ecSmrg    miArcFacePtr temp;
26605b261ecSmrg
26705b261ecSmrg    if (!l)
26805b261ecSmrg	l = 1;
26905b261ecSmrg
27005b261ecSmrg    if (tarc.width == 0 || tarc.height == 0) {
27105b261ecSmrg    	drawZeroArc (pDraw, pGC, &tarc, l, left, right);
27205b261ecSmrg	return;
27305b261ecSmrg    }
27405b261ecSmrg
27505b261ecSmrg    if (pGC->miTranslate) {
27605b261ecSmrg	tarc.x += pDraw->x;
27705b261ecSmrg	tarc.y += pDraw->y;
27805b261ecSmrg    }
27905b261ecSmrg
28005b261ecSmrg    a0 = tarc.angle1;
28105b261ecSmrg    a1 = tarc.angle2;
28205b261ecSmrg    if (a1 > FULLCIRCLE)
28305b261ecSmrg	a1 = FULLCIRCLE;
28405b261ecSmrg    else if (a1 < -FULLCIRCLE)
28505b261ecSmrg	a1 = -FULLCIRCLE;
28605b261ecSmrg    if (a1 < 0) {
28705b261ecSmrg    	startAngle = a0 + a1;
28805b261ecSmrg	endAngle = a0;
28905b261ecSmrg	temp = right;
29005b261ecSmrg	right = left;
29105b261ecSmrg	left = temp;
29205b261ecSmrg    } else {
29305b261ecSmrg	startAngle = a0;
29405b261ecSmrg	endAngle = a0 + a1;
29505b261ecSmrg    }
29605b261ecSmrg    /*
29705b261ecSmrg     * bounds check the two angles
29805b261ecSmrg     */
29905b261ecSmrg    if (startAngle < 0)
30005b261ecSmrg	startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
30105b261ecSmrg    if (startAngle >= FULLCIRCLE)
30205b261ecSmrg	startAngle = startAngle % FULLCIRCLE;
30305b261ecSmrg    if (endAngle < 0)
30405b261ecSmrg	endAngle = FULLCIRCLE - (-endAngle) % FULLCIRCLE;
30505b261ecSmrg    if (endAngle > FULLCIRCLE)
30605b261ecSmrg	endAngle = (endAngle-1) % FULLCIRCLE + 1;
30705b261ecSmrg    if ((startAngle == endAngle) && a1) {
30805b261ecSmrg	startAngle = 0;
30905b261ecSmrg	endAngle = FULLCIRCLE;
31005b261ecSmrg    }
31105b261ecSmrg
31205b261ecSmrg    drawArc (&tarc, l, startAngle, endAngle, right, left);
31305b261ecSmrg}
31405b261ecSmrg
31505b261ecSmrg/*
31605b261ecSmrg
31705b261ecSmrgThree equations combine to describe the boundaries of the arc
31805b261ecSmrg
31905b261ecSmrgx^2/w^2 + y^2/h^2 = 1			ellipse itself
32005b261ecSmrg(X-x)^2 + (Y-y)^2 = r^2			circle at (x, y) on the ellipse
32105b261ecSmrg(Y-y) = (X-x)*w^2*y/(h^2*x)		normal at (x, y) on the ellipse
32205b261ecSmrg
32305b261ecSmrgThese lead to a quartic relating Y and y
32405b261ecSmrg
32505b261ecSmrgy^4 - (2Y)y^3 + (Y^2 + (h^4 - w^2*r^2)/(w^2 - h^2))y^2
32605b261ecSmrg    - (2Y*h^4/(w^2 - h^2))y + (Y^2*h^4)/(w^2 - h^2) = 0
32705b261ecSmrg
32805b261ecSmrgThe reducible cubic obtained from this quartic is
32905b261ecSmrg
33005b261ecSmrgz^3 - (3N)z^2 - 2V = 0
33105b261ecSmrg
33205b261ecSmrgwhere
33305b261ecSmrg
33405b261ecSmrgN = (Y^2 + (h^4 - w^2*r^2/(w^2 - h^2)))/6
33505b261ecSmrgV = w^2*r^2*Y^2*h^4/(4 *(w^2 - h^2)^2)
33605b261ecSmrg
33705b261ecSmrgLet
33805b261ecSmrg
33905b261ecSmrgt = z - N
34005b261ecSmrgp = -N^2
34105b261ecSmrgq = -N^3 - V
34205b261ecSmrg
34305b261ecSmrgThen we get
34405b261ecSmrg
34505b261ecSmrgt^3 + 3pt + 2q = 0
34605b261ecSmrg
34705b261ecSmrgThe discriminant of this cubic is
34805b261ecSmrg
34905b261ecSmrgD = q^2 + p^3
35005b261ecSmrg
35105b261ecSmrgWhen D > 0, a real root is obtained as
35205b261ecSmrg
35305b261ecSmrgz = N + cbrt(-q+sqrt(D)) + cbrt(-q-sqrt(D))
35405b261ecSmrg
35505b261ecSmrgWhen D < 0, a real root is obtained as
35605b261ecSmrg
35705b261ecSmrgz = N - 2m*cos(acos(-q/m^3)/3)
35805b261ecSmrg
35905b261ecSmrgwhere
36005b261ecSmrg
36105b261ecSmrgm = sqrt(|p|) * sign(q)
36205b261ecSmrg
36305b261ecSmrgGiven a real root Z of the cubic, the roots of the quartic are the roots
36405b261ecSmrgof the two quadratics
36505b261ecSmrg
36605b261ecSmrgy^2 + ((b+A)/2)y + (Z + (bZ - d)/A) = 0
36705b261ecSmrg
36805b261ecSmrgwhere
36905b261ecSmrg
37005b261ecSmrgA = +/- sqrt(8Z + b^2 - 4c)
37105b261ecSmrgb, c, d are the cubic, quadratic, and linear coefficients of the quartic
37205b261ecSmrg
37305b261ecSmrgSome experimentation is then required to determine which solutions
37405b261ecSmrgcorrespond to the inner and outer boundaries.
37505b261ecSmrg
37605b261ecSmrg*/
37705b261ecSmrg
37805b261ecSmrgtypedef struct {
37905b261ecSmrg    short lx, lw, rx, rw;
38005b261ecSmrg} miArcSpan;
38105b261ecSmrg
38205b261ecSmrgtypedef struct {
38305b261ecSmrg    miArcSpan *spans;
38405b261ecSmrg    int count1, count2, k;
38505b261ecSmrg    char top, bot, hole;
38605b261ecSmrg} miArcSpanData;
38705b261ecSmrg
38805b261ecSmrgtypedef struct {
38905b261ecSmrg    unsigned long lrustamp;
39005b261ecSmrg    unsigned short lw;
39105b261ecSmrg    unsigned short width, height;
39205b261ecSmrg    miArcSpanData *spdata;
39305b261ecSmrg} arcCacheRec;
39405b261ecSmrg
39505b261ecSmrg#define CACHESIZE 25
39605b261ecSmrg
39705b261ecSmrgstatic void drawQuadrant(struct arc_def *def, struct accelerators *acc,
39805b261ecSmrg			 int a0, int a1, int mask, miArcFacePtr right,
39905b261ecSmrg			 miArcFacePtr left, miArcSpanData *spdata);
40005b261ecSmrg
40105b261ecSmrgstatic arcCacheRec arcCache[CACHESIZE];
40205b261ecSmrgstatic unsigned long lrustamp;
40305b261ecSmrgstatic arcCacheRec *lastCacheHit = &arcCache[0];
40405b261ecSmrgstatic RESTYPE cacheType;
40505b261ecSmrg
40605b261ecSmrgstatic int
40705b261ecSmrgmiFreeArcCache (pointer data, XID id)
40805b261ecSmrg{
40905b261ecSmrg    int k;
41005b261ecSmrg    arcCacheRec *cent;
41105b261ecSmrg
41205b261ecSmrg    if (id)
41305b261ecSmrg	cacheType = 0;
41405b261ecSmrg
41505b261ecSmrg    for (k = CACHESIZE, cent = &arcCache[0]; --k >= 0; cent++)
41605b261ecSmrg    {
41705b261ecSmrg	if (cent->spdata)
41805b261ecSmrg	{
41905b261ecSmrg	    cent->lrustamp = 0;
42005b261ecSmrg	    cent->lw = 0;
42105b261ecSmrg	    xfree(cent->spdata);
42205b261ecSmrg	    cent->spdata = NULL;
42305b261ecSmrg	}
42405b261ecSmrg    }
42505b261ecSmrg    lrustamp = 0;
42605b261ecSmrg    return Success;
42705b261ecSmrg}
42805b261ecSmrg
42905b261ecSmrgstatic void
43005b261ecSmrgmiComputeCircleSpans(
43105b261ecSmrg    int lw,
43205b261ecSmrg    xArc *parc,
43305b261ecSmrg    miArcSpanData *spdata)
43405b261ecSmrg{
43505b261ecSmrg    miArcSpan *span;
43605b261ecSmrg    int doinner;
43705b261ecSmrg    int x, y, e;
43805b261ecSmrg    int xk, yk, xm, ym, dx, dy;
43905b261ecSmrg    int slw, inslw;
44005b261ecSmrg    int inx = 0, iny, ine = 0;
44105b261ecSmrg    int inxk = 0, inyk = 0, inxm = 0, inym = 0;
44205b261ecSmrg
44305b261ecSmrg    doinner = -lw;
44405b261ecSmrg    slw = parc->width - doinner;
44505b261ecSmrg    y = parc->height >> 1;
44605b261ecSmrg    dy = parc->height & 1;
44705b261ecSmrg    dx = 1 - dy;
44805b261ecSmrg    MIWIDEARCSETUP(x, y, dy, slw, e, xk, xm, yk, ym);
44905b261ecSmrg    inslw = parc->width + doinner;
45005b261ecSmrg    if (inslw > 0)
45105b261ecSmrg    {
45205b261ecSmrg	spdata->hole = spdata->top;
45305b261ecSmrg	MIWIDEARCSETUP(inx, iny, dy, inslw, ine, inxk, inxm, inyk, inym);
45405b261ecSmrg    }
45505b261ecSmrg    else
45605b261ecSmrg    {
45705b261ecSmrg	spdata->hole = FALSE;
45805b261ecSmrg	doinner = -y;
45905b261ecSmrg    }
46005b261ecSmrg    spdata->count1 = -doinner - spdata->top;
46105b261ecSmrg    spdata->count2 = y + doinner;
46205b261ecSmrg    span = spdata->spans;
46305b261ecSmrg    while (y)
46405b261ecSmrg    {
46505b261ecSmrg	MIFILLARCSTEP(slw);
46605b261ecSmrg	span->lx = dy - x;
46705b261ecSmrg	if (++doinner <= 0)
46805b261ecSmrg 	{
46905b261ecSmrg	    span->lw = slw;
47005b261ecSmrg	    span->rx = 0;
47105b261ecSmrg	    span->rw = span->lx + slw;
47205b261ecSmrg	}
47305b261ecSmrg	else
47405b261ecSmrg	{
47505b261ecSmrg	    MIFILLINARCSTEP(inslw);
47605b261ecSmrg	    span->lw = x - inx;
47705b261ecSmrg	    span->rx = dy - inx + inslw;
47805b261ecSmrg	    span->rw = inx - x + slw - inslw;
47905b261ecSmrg	}
48005b261ecSmrg	span++;
48105b261ecSmrg    }
48205b261ecSmrg    if (spdata->bot)
48305b261ecSmrg    {
48405b261ecSmrg	if (spdata->count2)
48505b261ecSmrg	    spdata->count2--;
48605b261ecSmrg	else
48705b261ecSmrg	{
48805b261ecSmrg	    if (lw > (int)parc->height)
48905b261ecSmrg		span[-1].rx = span[-1].rw = -((lw - (int)parc->height) >> 1);
49005b261ecSmrg	    else
49105b261ecSmrg		span[-1].rw = 0;
49205b261ecSmrg	    spdata->count1--;
49305b261ecSmrg	}
49405b261ecSmrg    }
49505b261ecSmrg}
49605b261ecSmrg
49705b261ecSmrgstatic void
49805b261ecSmrgmiComputeEllipseSpans(
49905b261ecSmrg    int lw,
50005b261ecSmrg    xArc *parc,
50105b261ecSmrg    miArcSpanData *spdata)
50205b261ecSmrg{
50305b261ecSmrg    miArcSpan *span;
50405b261ecSmrg    double w, h, r, xorg;
50505b261ecSmrg    double Hs, Hf, WH, K, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
50605b261ecSmrg    double A, T, b, d, x, y, t, inx, outx = 0.0, hepp, hepm;
50705b261ecSmrg    int flip, solution;
50805b261ecSmrg
50905b261ecSmrg    w = (double)parc->width / 2.0;
51005b261ecSmrg    h = (double)parc->height / 2.0;
51105b261ecSmrg    r = lw / 2.0;
51205b261ecSmrg    rs = r * r;
51305b261ecSmrg    Hs = h * h;
51405b261ecSmrg    WH = w * w - Hs;
51505b261ecSmrg    Nk = w * r;
51605b261ecSmrg    Vk = (Nk * Hs) / (WH + WH);
51705b261ecSmrg    Hf = Hs * Hs;
51805b261ecSmrg    Nk = (Hf - Nk * Nk) / WH;
51905b261ecSmrg    Fk = Hf / WH;
52005b261ecSmrg    hepp = h + EPSILON;
52105b261ecSmrg    hepm = h - EPSILON;
52205b261ecSmrg    K = h + ((lw - 1) >> 1);
52305b261ecSmrg    span = spdata->spans;
52405b261ecSmrg    if (parc->width & 1)
52505b261ecSmrg	xorg = .5;
52605b261ecSmrg    else
52705b261ecSmrg	xorg = 0.0;
52805b261ecSmrg    if (spdata->top)
52905b261ecSmrg    {
53005b261ecSmrg	span->lx = 0;
53105b261ecSmrg	span->lw = 1;
53205b261ecSmrg	span++;
53305b261ecSmrg    }
53405b261ecSmrg    spdata->count1 = 0;
53505b261ecSmrg    spdata->count2 = 0;
53605b261ecSmrg    spdata->hole = (spdata->top &&
53705b261ecSmrg		 (int)parc->height * lw <= (int)(parc->width * parc->width) &&
53805b261ecSmrg		    lw < (int)parc->height);
53905b261ecSmrg    for (; K > 0.0; K -= 1.0)
54005b261ecSmrg    {
54105b261ecSmrg	N = (K * K + Nk) / 6.0;
54205b261ecSmrg	Nc = N * N * N;
54305b261ecSmrg	Vr = Vk * K;
54405b261ecSmrg	t = Nc + Vr * Vr;
54505b261ecSmrg	d = Nc + t;
54605b261ecSmrg	if (d < 0.0) {
54705b261ecSmrg	    d = Nc;
54805b261ecSmrg	    b = N;
54905b261ecSmrg	    if ( (b < 0.0) == (t < 0.0) )
55005b261ecSmrg	    {
55105b261ecSmrg		b = -b;
55205b261ecSmrg		d = -d;
55305b261ecSmrg	    }
55405b261ecSmrg	    Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
55505b261ecSmrg	    if ( (Z < 0.0) == (Vr < 0.0) )
55605b261ecSmrg		flip = 2;
55705b261ecSmrg	    else
55805b261ecSmrg		flip = 1;
55905b261ecSmrg	}
56005b261ecSmrg	else
56105b261ecSmrg	{
56205b261ecSmrg	    d = Vr * sqrt(d);
56305b261ecSmrg	    Z = N + cbrt(t + d) + cbrt(t - d);
56405b261ecSmrg	    flip = 0;
56505b261ecSmrg	}
56605b261ecSmrg	A = sqrt((Z + Z) - Nk);
56705b261ecSmrg	T = (Fk - Z) * K / A;
56805b261ecSmrg	inx = 0.0;
56905b261ecSmrg	solution = FALSE;
57005b261ecSmrg	b = -A + K;
57105b261ecSmrg	d = b * b - 4 * (Z + T);
57205b261ecSmrg	if (d >= 0)
57305b261ecSmrg	{
57405b261ecSmrg	    d = sqrt(d);
57505b261ecSmrg	    y = (b + d) / 2;
57605b261ecSmrg	    if ((y >= 0.0) && (y < hepp))
57705b261ecSmrg	    {
57805b261ecSmrg		solution = TRUE;
57905b261ecSmrg		if (y > hepm)
58005b261ecSmrg		    y = h;
58105b261ecSmrg		t = y / h;
58205b261ecSmrg		x = w * sqrt(1 - (t * t));
58305b261ecSmrg		t = K - y;
58405b261ecSmrg		if (rs - (t * t) >= 0)
58505b261ecSmrg		   t = sqrt(rs - (t * t));
58605b261ecSmrg		else
58705b261ecSmrg		   t = 0;
58805b261ecSmrg		if (flip == 2)
58905b261ecSmrg		    inx = x - t;
59005b261ecSmrg		else
59105b261ecSmrg		    outx = x + t;
59205b261ecSmrg	    }
59305b261ecSmrg	}
59405b261ecSmrg	b = A + K;
59505b261ecSmrg	d = b * b - 4 * (Z - T);
59605b261ecSmrg	/* Because of the large magnitudes involved, we lose enough precision
59705b261ecSmrg	 * that sometimes we end up with a negative value near the axis, when
59805b261ecSmrg	 * it should be positive.  This is a workaround.
59905b261ecSmrg	 */
60005b261ecSmrg	if (d < 0 && !solution)
60105b261ecSmrg	    d = 0.0;
60205b261ecSmrg	if (d >= 0) {
60305b261ecSmrg	    d = sqrt(d);
60405b261ecSmrg	    y = (b + d) / 2;
60505b261ecSmrg	    if (y < hepp)
60605b261ecSmrg	    {
60705b261ecSmrg		if (y > hepm)
60805b261ecSmrg		    y = h;
60905b261ecSmrg		t = y / h;
61005b261ecSmrg		x = w * sqrt(1 - (t * t));
61105b261ecSmrg		t = K - y;
61205b261ecSmrg		if (rs - (t * t) >= 0)
61305b261ecSmrg		   inx = x - sqrt(rs - (t * t));
61405b261ecSmrg		else
61505b261ecSmrg		   inx = x;
61605b261ecSmrg	    }
61705b261ecSmrg	    y = (b - d) / 2;
61805b261ecSmrg	    if (y >= 0.0)
61905b261ecSmrg	    {
62005b261ecSmrg		if (y > hepm)
62105b261ecSmrg		    y = h;
62205b261ecSmrg		t = y / h;
62305b261ecSmrg		x = w * sqrt(1 - (t * t));
62405b261ecSmrg		t = K - y;
62505b261ecSmrg		if (rs - (t * t) >= 0)
62605b261ecSmrg		   t = sqrt(rs - (t * t));
62705b261ecSmrg		else
62805b261ecSmrg		   t = 0;
62905b261ecSmrg		if (flip == 1)
63005b261ecSmrg		    inx = x - t;
63105b261ecSmrg		else
63205b261ecSmrg		    outx = x + t;
63305b261ecSmrg	    }
63405b261ecSmrg	}
63505b261ecSmrg	span->lx = ICEIL(xorg - outx);
63605b261ecSmrg	if (inx <= 0.0)
63705b261ecSmrg	{
63805b261ecSmrg	    spdata->count1++;
63905b261ecSmrg	    span->lw = ICEIL(xorg + outx) - span->lx;
64005b261ecSmrg	    span->rx = ICEIL(xorg + inx);
64105b261ecSmrg	    span->rw = -ICEIL(xorg - inx);
64205b261ecSmrg	}
64305b261ecSmrg	else
64405b261ecSmrg	{
64505b261ecSmrg	    spdata->count2++;
64605b261ecSmrg	    span->lw = ICEIL(xorg - inx) - span->lx;
64705b261ecSmrg	    span->rx = ICEIL(xorg + inx);
64805b261ecSmrg	    span->rw = ICEIL(xorg + outx) - span->rx;
64905b261ecSmrg	}
65005b261ecSmrg	span++;
65105b261ecSmrg    }
65205b261ecSmrg    if (spdata->bot)
65305b261ecSmrg    {
65405b261ecSmrg	outx = w + r;
65505b261ecSmrg	if (r >= h && r <= w)
65605b261ecSmrg	    inx = 0.0;
65705b261ecSmrg	else if (Nk < 0.0 && -Nk < Hs)
65805b261ecSmrg	{
65905b261ecSmrg	    inx = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
66005b261ecSmrg	    if (inx > w - r)
66105b261ecSmrg		inx = w - r;
66205b261ecSmrg	}
66305b261ecSmrg	else
66405b261ecSmrg	    inx = w - r;
66505b261ecSmrg	span->lx = ICEIL(xorg - outx);
66605b261ecSmrg	if (inx <= 0.0)
66705b261ecSmrg	{
66805b261ecSmrg	    span->lw = ICEIL(xorg + outx) - span->lx;
66905b261ecSmrg	    span->rx = ICEIL(xorg + inx);
67005b261ecSmrg	    span->rw = -ICEIL(xorg - inx);
67105b261ecSmrg	}
67205b261ecSmrg	else
67305b261ecSmrg	{
67405b261ecSmrg	    span->lw = ICEIL(xorg - inx) - span->lx;
67505b261ecSmrg	    span->rx = ICEIL(xorg + inx);
67605b261ecSmrg	    span->rw = ICEIL(xorg + outx) - span->rx;
67705b261ecSmrg	}
67805b261ecSmrg    }
67905b261ecSmrg    if (spdata->hole)
68005b261ecSmrg    {
68105b261ecSmrg	span = &spdata->spans[spdata->count1];
68205b261ecSmrg	span->lw = -span->lx;
68305b261ecSmrg	span->rx = 1;
68405b261ecSmrg	span->rw = span->lw;
68505b261ecSmrg	spdata->count1--;
68605b261ecSmrg	spdata->count2++;
68705b261ecSmrg    }
68805b261ecSmrg}
68905b261ecSmrg
69005b261ecSmrgstatic double
69105b261ecSmrgtailX(
69205b261ecSmrg    double K,
69305b261ecSmrg    struct arc_def *def,
69405b261ecSmrg    struct arc_bound *bounds,
69505b261ecSmrg    struct accelerators *acc)
69605b261ecSmrg{
69705b261ecSmrg    double w, h, r;
69805b261ecSmrg    double Hs, Hf, WH, Vk, Nk, Fk, Vr, N, Nc, Z, rs;
69905b261ecSmrg    double A, T, b, d, x, y, t, hepp, hepm;
70005b261ecSmrg    int flip, solution;
70105b261ecSmrg    double xs[2];
70205b261ecSmrg    double *xp;
70305b261ecSmrg
70405b261ecSmrg    w = def->w;
70505b261ecSmrg    h = def->h;
70605b261ecSmrg    r = def->l;
70705b261ecSmrg    rs = r * r;
70805b261ecSmrg    Hs = acc->h2;
70905b261ecSmrg    WH = -acc->h2mw2;
71005b261ecSmrg    Nk = def->w * r;
71105b261ecSmrg    Vk = (Nk * Hs) / (WH + WH);
71205b261ecSmrg    Hf = acc->h4;
71305b261ecSmrg    Nk = (Hf - Nk * Nk) / WH;
71405b261ecSmrg    if (K == 0.0) {
71505b261ecSmrg	if (Nk < 0.0 && -Nk < Hs) {
71605b261ecSmrg	    xs[0] = w * sqrt(1 + Nk / Hs) - sqrt(rs + Nk);
71705b261ecSmrg	    xs[1] = w - r;
71805b261ecSmrg	    if (acc->left.valid && boundedLe(K, bounds->left) &&
71905b261ecSmrg		!boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
72005b261ecSmrg		return xs[1];
72105b261ecSmrg	    if (acc->right.valid && boundedLe(K, bounds->right) &&
72205b261ecSmrg		!boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
72305b261ecSmrg		return xs[1];
72405b261ecSmrg	    return xs[0];
72505b261ecSmrg	}
72605b261ecSmrg	return w - r;
72705b261ecSmrg    }
72805b261ecSmrg    Fk = Hf / WH;
72905b261ecSmrg    hepp = h + EPSILON;
73005b261ecSmrg    hepm = h - EPSILON;
73105b261ecSmrg    N = (K * K + Nk) / 6.0;
73205b261ecSmrg    Nc = N * N * N;
73305b261ecSmrg    Vr = Vk * K;
73405b261ecSmrg    xp = xs;
73505b261ecSmrg    xs[0] = 0.0;
73605b261ecSmrg    t = Nc + Vr * Vr;
73705b261ecSmrg    d = Nc + t;
73805b261ecSmrg    if (d < 0.0) {
73905b261ecSmrg	d = Nc;
74005b261ecSmrg	b = N;
74105b261ecSmrg	if ( (b < 0.0) == (t < 0.0) )
74205b261ecSmrg	{
74305b261ecSmrg	    b = -b;
74405b261ecSmrg	    d = -d;
74505b261ecSmrg	}
74605b261ecSmrg	Z = N - 2.0 * b * cos(acos(-t / d) / 3.0);
74705b261ecSmrg	if ( (Z < 0.0) == (Vr < 0.0) )
74805b261ecSmrg	    flip = 2;
74905b261ecSmrg	else
75005b261ecSmrg	    flip = 1;
75105b261ecSmrg    }
75205b261ecSmrg    else
75305b261ecSmrg    {
75405b261ecSmrg	d = Vr * sqrt(d);
75505b261ecSmrg	Z = N + cbrt(t + d) + cbrt(t - d);
75605b261ecSmrg	flip = 0;
75705b261ecSmrg    }
75805b261ecSmrg    A = sqrt((Z + Z) - Nk);
75905b261ecSmrg    T = (Fk - Z) * K / A;
76005b261ecSmrg    solution = FALSE;
76105b261ecSmrg    b = -A + K;
76205b261ecSmrg    d = b * b - 4 * (Z + T);
76305b261ecSmrg    if (d >= 0 && flip == 2)
76405b261ecSmrg    {
76505b261ecSmrg	d = sqrt(d);
76605b261ecSmrg	y = (b + d) / 2;
76705b261ecSmrg	if ((y >= 0.0) && (y < hepp))
76805b261ecSmrg	{
76905b261ecSmrg	    solution = TRUE;
77005b261ecSmrg	    if (y > hepm)
77105b261ecSmrg		y = h;
77205b261ecSmrg	    t = y / h;
77305b261ecSmrg	    x = w * sqrt(1 - (t * t));
77405b261ecSmrg	    t = K - y;
77505b261ecSmrg	    if (rs - (t * t) >= 0)
77605b261ecSmrg	       t = sqrt(rs - (t * t));
77705b261ecSmrg	    else
77805b261ecSmrg	       t = 0;
77905b261ecSmrg	    *xp++ = x - t;
78005b261ecSmrg	}
78105b261ecSmrg    }
78205b261ecSmrg    b = A + K;
78305b261ecSmrg    d = b * b - 4 * (Z - T);
78405b261ecSmrg    /* Because of the large magnitudes involved, we lose enough precision
78505b261ecSmrg     * that sometimes we end up with a negative value near the axis, when
78605b261ecSmrg     * it should be positive.  This is a workaround.
78705b261ecSmrg     */
78805b261ecSmrg    if (d < 0 && !solution)
78905b261ecSmrg	d = 0.0;
79005b261ecSmrg    if (d >= 0) {
79105b261ecSmrg	d = sqrt(d);
79205b261ecSmrg	y = (b + d) / 2;
79305b261ecSmrg	if (y < hepp)
79405b261ecSmrg	{
79505b261ecSmrg	    if (y > hepm)
79605b261ecSmrg		y = h;
79705b261ecSmrg	    t = y / h;
79805b261ecSmrg	    x = w * sqrt(1 - (t * t));
79905b261ecSmrg	    t = K - y;
80005b261ecSmrg	    if (rs - (t * t) >= 0)
80105b261ecSmrg	       *xp++ = x - sqrt(rs - (t * t));
80205b261ecSmrg	    else
80305b261ecSmrg	       *xp++ = x;
80405b261ecSmrg	}
80505b261ecSmrg	y = (b - d) / 2;
80605b261ecSmrg	if (y >= 0.0 && flip == 1)
80705b261ecSmrg	{
80805b261ecSmrg	    if (y > hepm)
80905b261ecSmrg		y = h;
81005b261ecSmrg	    t = y / h;
81105b261ecSmrg	    x = w * sqrt(1 - (t * t));
81205b261ecSmrg	    t = K - y;
81305b261ecSmrg	    if (rs - (t * t) >= 0)
81405b261ecSmrg	       t = sqrt(rs - (t * t));
81505b261ecSmrg	    else
81605b261ecSmrg	       t = 0;
81705b261ecSmrg	    *xp++ = x - t;
81805b261ecSmrg	}
81905b261ecSmrg    }
82005b261ecSmrg    if (xp > &xs[1]) {
82105b261ecSmrg	if (acc->left.valid && boundedLe(K, bounds->left) &&
82205b261ecSmrg	    !boundedLe(K, bounds->outer) && xs[0] >= 0.0 && xs[1] >= 0.0)
82305b261ecSmrg	    return xs[1];
82405b261ecSmrg	if (acc->right.valid && boundedLe(K, bounds->right) &&
82505b261ecSmrg	    !boundedLe(K, bounds->inner) && xs[0] <= 0.0 && xs[1] <= 0.0)
82605b261ecSmrg	    return xs[1];
82705b261ecSmrg    }
82805b261ecSmrg    return xs[0];
82905b261ecSmrg}
83005b261ecSmrg
83105b261ecSmrgstatic miArcSpanData *
83205b261ecSmrgmiComputeWideEllipse(
83305b261ecSmrg    int  lw,
83405b261ecSmrg    xArc *parc,
83505b261ecSmrg    Bool *mustFree)
83605b261ecSmrg{
83705b261ecSmrg    miArcSpanData *spdata;
83805b261ecSmrg    arcCacheRec *cent, *lruent;
83905b261ecSmrg    int k;
84005b261ecSmrg    arcCacheRec fakeent;
84105b261ecSmrg
84205b261ecSmrg    if (!lw)
84305b261ecSmrg	lw = 1;
84405b261ecSmrg    if (parc->height <= 1500)
84505b261ecSmrg    {
84605b261ecSmrg	*mustFree = FALSE;
84705b261ecSmrg	cent = lastCacheHit;
84805b261ecSmrg	if (cent->lw == lw &&
84905b261ecSmrg	    cent->width == parc->width && cent->height == parc->height)
85005b261ecSmrg	{
85105b261ecSmrg	    cent->lrustamp = ++lrustamp;
85205b261ecSmrg	    return cent->spdata;
85305b261ecSmrg	}
85405b261ecSmrg	lruent = &arcCache[0];
85505b261ecSmrg	for (k = CACHESIZE, cent = lruent; --k >= 0; cent++)
85605b261ecSmrg	{
85705b261ecSmrg	    if (cent->lw == lw &&
85805b261ecSmrg		cent->width == parc->width && cent->height == parc->height)
85905b261ecSmrg	    {
86005b261ecSmrg		cent->lrustamp = ++lrustamp;
86105b261ecSmrg		lastCacheHit = cent;
86205b261ecSmrg		return cent->spdata;
86305b261ecSmrg	    }
86405b261ecSmrg	    if (cent->lrustamp < lruent->lrustamp)
86505b261ecSmrg		lruent = cent;
86605b261ecSmrg	}
86705b261ecSmrg	if (!cacheType)
86805b261ecSmrg	{
86905b261ecSmrg	    cacheType = CreateNewResourceType(miFreeArcCache);
87005b261ecSmrg	    (void) AddResource(FakeClientID(0), cacheType, NULL);
87105b261ecSmrg	}
87205b261ecSmrg    } else {
87305b261ecSmrg	lruent = &fakeent;
87405b261ecSmrg	lruent->spdata = NULL;
87505b261ecSmrg	*mustFree = TRUE;
87605b261ecSmrg    }
87705b261ecSmrg    k = (parc->height >> 1) + ((lw - 1) >> 1);
87805b261ecSmrg    spdata = lruent->spdata;
87905b261ecSmrg    if (!spdata || spdata->k != k)
88005b261ecSmrg    {
88105b261ecSmrg	if (spdata)
88205b261ecSmrg	    xfree(spdata);
88305b261ecSmrg	spdata = (miArcSpanData *)xalloc(sizeof(miArcSpanData) +
88405b261ecSmrg					 sizeof(miArcSpan) * (k + 2));
88505b261ecSmrg	lruent->spdata = spdata;
88605b261ecSmrg	if (!spdata)
88705b261ecSmrg	{
88805b261ecSmrg	    lruent->lrustamp = 0;
88905b261ecSmrg	    lruent->lw = 0;
89005b261ecSmrg	    return spdata;
89105b261ecSmrg	}
89205b261ecSmrg	spdata->spans = (miArcSpan *)(spdata + 1);
89305b261ecSmrg	spdata->k = k;
89405b261ecSmrg    }
89505b261ecSmrg    spdata->top = !(lw & 1) && !(parc->width & 1);
89605b261ecSmrg    spdata->bot = !(parc->height & 1);
89705b261ecSmrg    lruent->lrustamp = ++lrustamp;
89805b261ecSmrg    lruent->lw = lw;
89905b261ecSmrg    lruent->width = parc->width;
90005b261ecSmrg    lruent->height = parc->height;
90105b261ecSmrg    if (lruent != &fakeent)
90205b261ecSmrg	lastCacheHit = lruent;
90305b261ecSmrg    if (parc->width == parc->height)
90405b261ecSmrg	miComputeCircleSpans(lw, parc, spdata);
90505b261ecSmrg    else
90605b261ecSmrg	miComputeEllipseSpans(lw, parc, spdata);
90705b261ecSmrg    return spdata;
90805b261ecSmrg}
90905b261ecSmrg
91005b261ecSmrgstatic void
91105b261ecSmrgmiFillWideEllipse(
91205b261ecSmrg    DrawablePtr	pDraw,
91305b261ecSmrg    GCPtr	pGC,
91405b261ecSmrg    xArc	*parc)
91505b261ecSmrg{
91605b261ecSmrg    DDXPointPtr points;
91705b261ecSmrg    DDXPointPtr pts;
91805b261ecSmrg    int *widths;
91905b261ecSmrg    int *wids;
92005b261ecSmrg    miArcSpanData *spdata;
92105b261ecSmrg    Bool mustFree;
92205b261ecSmrg    miArcSpan *span;
92305b261ecSmrg    int xorg, yorgu, yorgl;
92405b261ecSmrg    int n;
92505b261ecSmrg
92605b261ecSmrg    yorgu = parc->height + pGC->lineWidth;
92705b261ecSmrg    n = (sizeof(int) * 2) * yorgu;
9284642e01fSmrg    widths = (int *)xalloc(n + (sizeof(DDXPointRec) * 2) * yorgu);
92905b261ecSmrg    if (!widths)
93005b261ecSmrg	return;
93105b261ecSmrg    points = (DDXPointPtr)((char *)widths + n);
93205b261ecSmrg    spdata = miComputeWideEllipse((int)pGC->lineWidth, parc, &mustFree);
93305b261ecSmrg    if (!spdata)
93405b261ecSmrg    {
9354642e01fSmrg	xfree(widths);
93605b261ecSmrg	return;
93705b261ecSmrg    }
93805b261ecSmrg    pts = points;
93905b261ecSmrg    wids = widths;
94005b261ecSmrg    span = spdata->spans;
94105b261ecSmrg    xorg = parc->x + (parc->width >> 1);
94205b261ecSmrg    yorgu = parc->y + (parc->height >> 1);
94305b261ecSmrg    yorgl = yorgu + (parc->height & 1);
94405b261ecSmrg    if (pGC->miTranslate)
94505b261ecSmrg    {
94605b261ecSmrg	xorg += pDraw->x;
94705b261ecSmrg	yorgu += pDraw->y;
94805b261ecSmrg	yorgl += pDraw->y;
94905b261ecSmrg    }
95005b261ecSmrg    yorgu -= spdata->k;
95105b261ecSmrg    yorgl += spdata->k;
95205b261ecSmrg    if (spdata->top)
95305b261ecSmrg    {
95405b261ecSmrg	pts->x = xorg;
95505b261ecSmrg	pts->y = yorgu - 1;
95605b261ecSmrg	pts++;
95705b261ecSmrg	*wids++ = 1;
95805b261ecSmrg	span++;
95905b261ecSmrg    }
96005b261ecSmrg    for (n = spdata->count1; --n >= 0; )
96105b261ecSmrg    {
96205b261ecSmrg	pts[0].x = xorg + span->lx;
96305b261ecSmrg	pts[0].y = yorgu;
96405b261ecSmrg	wids[0] = span->lw;
96505b261ecSmrg	pts[1].x = pts[0].x;
96605b261ecSmrg	pts[1].y = yorgl;
96705b261ecSmrg	wids[1] = wids[0];
96805b261ecSmrg	yorgu++;
96905b261ecSmrg	yorgl--;
97005b261ecSmrg	pts += 2;
97105b261ecSmrg	wids += 2;
97205b261ecSmrg	span++;
97305b261ecSmrg    }
97405b261ecSmrg    if (spdata->hole)
97505b261ecSmrg    {
97605b261ecSmrg	pts[0].x = xorg;
97705b261ecSmrg	pts[0].y = yorgl;
97805b261ecSmrg	wids[0] = 1;
97905b261ecSmrg	pts++;
98005b261ecSmrg	wids++;
98105b261ecSmrg    }
98205b261ecSmrg    for (n = spdata->count2; --n >= 0; )
98305b261ecSmrg    {
98405b261ecSmrg	pts[0].x = xorg + span->lx;
98505b261ecSmrg	pts[0].y = yorgu;
98605b261ecSmrg	wids[0] = span->lw;
98705b261ecSmrg	pts[1].x = xorg + span->rx;
98805b261ecSmrg	pts[1].y = pts[0].y;
98905b261ecSmrg	wids[1] = span->rw;
99005b261ecSmrg	pts[2].x = pts[0].x;
99105b261ecSmrg	pts[2].y = yorgl;
99205b261ecSmrg	wids[2] = wids[0];
99305b261ecSmrg	pts[3].x = pts[1].x;
99405b261ecSmrg	pts[3].y = pts[2].y;
99505b261ecSmrg	wids[3] = wids[1];
99605b261ecSmrg	yorgu++;
99705b261ecSmrg	yorgl--;
99805b261ecSmrg	pts += 4;
99905b261ecSmrg	wids += 4;
100005b261ecSmrg	span++;
100105b261ecSmrg    }
100205b261ecSmrg    if (spdata->bot)
100305b261ecSmrg    {
100405b261ecSmrg	if (span->rw <= 0)
100505b261ecSmrg	{
100605b261ecSmrg	    pts[0].x = xorg + span->lx;
100705b261ecSmrg	    pts[0].y = yorgu;
100805b261ecSmrg	    wids[0] = span->lw;
100905b261ecSmrg	    pts++;
101005b261ecSmrg	    wids++;
101105b261ecSmrg	}
101205b261ecSmrg	else
101305b261ecSmrg	{
101405b261ecSmrg	    pts[0].x = xorg + span->lx;
101505b261ecSmrg	    pts[0].y = yorgu;
101605b261ecSmrg	    wids[0] = span->lw;
101705b261ecSmrg	    pts[1].x = xorg + span->rx;
101805b261ecSmrg	    pts[1].y = pts[0].y;
101905b261ecSmrg	    wids[1] = span->rw;
102005b261ecSmrg	    pts += 2;
102105b261ecSmrg	    wids += 2;
102205b261ecSmrg	}
102305b261ecSmrg    }
102405b261ecSmrg    if (mustFree)
102505b261ecSmrg	xfree(spdata);
102605b261ecSmrg    (*pGC->ops->FillSpans)(pDraw, pGC, pts - points, points, widths, FALSE);
102705b261ecSmrg
10284642e01fSmrg    xfree(widths);
102905b261ecSmrg}
103005b261ecSmrg
103105b261ecSmrg/*
103205b261ecSmrg * miPolyArc strategy:
103305b261ecSmrg *
103405b261ecSmrg * If arc is zero width and solid, we don't have to worry about the rasterop
103505b261ecSmrg * or join styles.  For wide solid circles, we use a fast integer algorithm.
103605b261ecSmrg * For wide solid ellipses, we use special case floating point code.
103705b261ecSmrg * Otherwise, we set up pDrawTo and pGCTo according to the rasterop, then
103805b261ecSmrg * draw using pGCTo and pDrawTo.  If the raster-op was "tricky," that is,
103905b261ecSmrg * if it involves the destination, then we use PushPixels to move the bits
104005b261ecSmrg * from the scratch drawable to pDraw. (See the wide line code for a
104105b261ecSmrg * fuller explanation of this.)
104205b261ecSmrg */
104305b261ecSmrg
104405b261ecSmrg_X_EXPORT void
10454642e01fSmrgmiPolyArc(DrawablePtr pDraw, GCPtr pGC, int narcs, xArc *parcs)
104605b261ecSmrg{
104705b261ecSmrg    int		i;
104805b261ecSmrg    xArc	*parc;
104905b261ecSmrg    int		xMin, xMax, yMin, yMax;
105005b261ecSmrg    int		pixmapWidth = 0, pixmapHeight = 0;
105105b261ecSmrg    int		xOrg = 0, yOrg = 0;
105205b261ecSmrg    int		width;
105305b261ecSmrg    Bool	fTricky;
105405b261ecSmrg    DrawablePtr	pDrawTo;
105505b261ecSmrg    CARD32	fg, bg;
105605b261ecSmrg    GCPtr	pGCTo;
105705b261ecSmrg    miPolyArcPtr polyArcs;
105805b261ecSmrg    int		cap[2], join[2];
105905b261ecSmrg    int		iphase;
106005b261ecSmrg    int		halfWidth;
106105b261ecSmrg
106205b261ecSmrg    width = pGC->lineWidth;
106305b261ecSmrg    if(width == 0 && pGC->lineStyle == LineSolid)
106405b261ecSmrg    {
106505b261ecSmrg	for(i = narcs, parc = parcs; --i >= 0; parc++)
106605b261ecSmrg	    miArcSegment( pDraw, pGC, *parc,
106705b261ecSmrg 	    (miArcFacePtr) 0, (miArcFacePtr) 0 );
106805b261ecSmrg	fillSpans (pDraw, pGC);
106905b261ecSmrg    }
107005b261ecSmrg    else
107105b261ecSmrg    {
107205b261ecSmrg	if ((pGC->lineStyle == LineSolid) && narcs)
107305b261ecSmrg	{
107405b261ecSmrg	    while (parcs->width && parcs->height &&
107505b261ecSmrg		   (parcs->angle2 >= FULLCIRCLE ||
107605b261ecSmrg		    parcs->angle2 <= -FULLCIRCLE))
107705b261ecSmrg	    {
107805b261ecSmrg		miFillWideEllipse(pDraw, pGC, parcs);
107905b261ecSmrg		if (!--narcs)
108005b261ecSmrg		    return;
108105b261ecSmrg		parcs++;
108205b261ecSmrg	    }
108305b261ecSmrg	}
108405b261ecSmrg
108505b261ecSmrg	/* Set up pDrawTo and pGCTo based on the rasterop */
108605b261ecSmrg	switch(pGC->alu)
108705b261ecSmrg	{
108805b261ecSmrg	  case GXclear:		/* 0 */
108905b261ecSmrg	  case GXcopy:		/* src */
109005b261ecSmrg	  case GXcopyInverted:	/* NOT src */
109105b261ecSmrg	  case GXset:		/* 1 */
109205b261ecSmrg	    fTricky = FALSE;
109305b261ecSmrg	    pDrawTo = pDraw;
109405b261ecSmrg	    pGCTo = pGC;
109505b261ecSmrg	    break;
109605b261ecSmrg	  default:
109705b261ecSmrg	    fTricky = TRUE;
109805b261ecSmrg
109905b261ecSmrg	    /* find bounding box around arcs */
110005b261ecSmrg	    xMin = yMin = MAXSHORT;
110105b261ecSmrg	    xMax = yMax = MINSHORT;
110205b261ecSmrg
110305b261ecSmrg	    for(i = narcs, parc = parcs; --i >= 0; parc++)
110405b261ecSmrg	    {
110505b261ecSmrg		xMin = min (xMin, parc->x);
110605b261ecSmrg		yMin = min (yMin, parc->y);
110705b261ecSmrg		xMax = max (xMax, (parc->x + (int) parc->width));
110805b261ecSmrg		yMax = max (yMax, (parc->y + (int) parc->height));
110905b261ecSmrg	    }
111005b261ecSmrg
111105b261ecSmrg	    /* expand box to deal with line widths */
111205b261ecSmrg	    halfWidth = (width + 1)/2;
111305b261ecSmrg	    xMin -= halfWidth;
111405b261ecSmrg	    yMin -= halfWidth;
111505b261ecSmrg	    xMax += halfWidth;
111605b261ecSmrg	    yMax += halfWidth;
111705b261ecSmrg
111805b261ecSmrg	    /* compute pixmap size; limit it to size of drawable */
111905b261ecSmrg	    xOrg = max(xMin, 0);
112005b261ecSmrg	    yOrg = max(yMin, 0);
112105b261ecSmrg	    pixmapWidth = min(xMax, pDraw->width) - xOrg;
112205b261ecSmrg	    pixmapHeight = min(yMax, pDraw->height) - yOrg;
112305b261ecSmrg
112405b261ecSmrg	    /* if nothing left, return */
112505b261ecSmrg	    if ( (pixmapWidth <= 0) || (pixmapHeight <= 0) ) return;
112605b261ecSmrg
112705b261ecSmrg	    for(i = narcs, parc = parcs; --i >= 0; parc++)
112805b261ecSmrg	    {
112905b261ecSmrg		parc->x -= xOrg;
113005b261ecSmrg		parc->y -= yOrg;
113105b261ecSmrg	    }
113205b261ecSmrg	    if (pGC->miTranslate)
113305b261ecSmrg	    {
113405b261ecSmrg		xOrg += pDraw->x;
113505b261ecSmrg		yOrg += pDraw->y;
113605b261ecSmrg	    }
113705b261ecSmrg
113805b261ecSmrg	    /* set up scratch GC */
113905b261ecSmrg
114005b261ecSmrg	    pGCTo = GetScratchGC(1, pDraw->pScreen);
114105b261ecSmrg	    if (!pGCTo)
114205b261ecSmrg		return;
114305b261ecSmrg	    gcvals[GCValsFunction] = GXcopy;
114405b261ecSmrg	    gcvals[GCValsForeground] = 1;
114505b261ecSmrg	    gcvals[GCValsBackground] = 0;
114605b261ecSmrg	    gcvals[GCValsLineWidth] = pGC->lineWidth;
114705b261ecSmrg	    gcvals[GCValsCapStyle] = pGC->capStyle;
114805b261ecSmrg	    gcvals[GCValsJoinStyle] = pGC->joinStyle;
114905b261ecSmrg	    dixChangeGC(NullClient, pGCTo, GCValsMask, gcvals, NULL);
115005b261ecSmrg
115105b261ecSmrg	    /* allocate a 1 bit deep pixmap of the appropriate size, and
115205b261ecSmrg	     * validate it */
115305b261ecSmrg	    pDrawTo = (DrawablePtr)(*pDraw->pScreen->CreatePixmap)
11544642e01fSmrg				(pDraw->pScreen, pixmapWidth, pixmapHeight, 1,
11554642e01fSmrg				 CREATE_PIXMAP_USAGE_SCRATCH);
115605b261ecSmrg	    if (!pDrawTo)
115705b261ecSmrg	    {
115805b261ecSmrg		FreeScratchGC(pGCTo);
115905b261ecSmrg		return;
116005b261ecSmrg	    }
116105b261ecSmrg	    ValidateGC(pDrawTo, pGCTo);
116205b261ecSmrg	    miClearDrawable(pDrawTo, pGCTo);
116305b261ecSmrg	}
116405b261ecSmrg
116505b261ecSmrg	fg = pGC->fgPixel;
116605b261ecSmrg	bg = pGC->bgPixel;
116705b261ecSmrg	if ((pGC->fillStyle == FillTiled) ||
116805b261ecSmrg	    (pGC->fillStyle == FillOpaqueStippled))
116905b261ecSmrg	    bg = fg; /* the protocol sez these don't cause color changes */
117005b261ecSmrg
117105b261ecSmrg	polyArcs = miComputeArcs (parcs, narcs, pGC);
117205b261ecSmrg
117305b261ecSmrg	if (!polyArcs)
117405b261ecSmrg	{
117505b261ecSmrg	    if (fTricky) {
117605b261ecSmrg		(*pDraw->pScreen->DestroyPixmap) ((PixmapPtr)pDrawTo);
117705b261ecSmrg		FreeScratchGC (pGCTo);
117805b261ecSmrg	    }
117905b261ecSmrg	    return;
118005b261ecSmrg	}
118105b261ecSmrg
118205b261ecSmrg	cap[0] = cap[1] = 0;
118305b261ecSmrg	join[0] = join[1] = 0;
118405b261ecSmrg	for (iphase = ((pGC->lineStyle == LineDoubleDash) ? 1 : 0);
118505b261ecSmrg 	     iphase >= 0;
118605b261ecSmrg	     iphase--)
118705b261ecSmrg	{
118805b261ecSmrg	    if (iphase == 1) {
118905b261ecSmrg		dixChangeGC (NullClient, pGC, GCForeground, &bg, NULL);
119005b261ecSmrg		ValidateGC (pDraw, pGC);
119105b261ecSmrg	    } else if (pGC->lineStyle == LineDoubleDash) {
119205b261ecSmrg		dixChangeGC (NullClient, pGC, GCForeground, &fg, NULL);
119305b261ecSmrg		ValidateGC (pDraw, pGC);
119405b261ecSmrg	    }
119505b261ecSmrg	    for (i = 0; i < polyArcs[iphase].narcs; i++) {
119605b261ecSmrg		miArcDataPtr	arcData;
119705b261ecSmrg
119805b261ecSmrg		arcData = &polyArcs[iphase].arcs[i];
119905b261ecSmrg		miArcSegment(pDrawTo, pGCTo, arcData->arc,
120005b261ecSmrg			     &arcData->bounds[RIGHT_END],
120105b261ecSmrg			     &arcData->bounds[LEFT_END]);
120205b261ecSmrg		if (polyArcs[iphase].arcs[i].render) {
120305b261ecSmrg		    fillSpans (pDrawTo, pGCTo);
120405b261ecSmrg		    /*
120505b261ecSmrg		     * don't cap self-joining arcs
120605b261ecSmrg		     */
120705b261ecSmrg		    if (polyArcs[iphase].arcs[i].selfJoin &&
120805b261ecSmrg		        cap[iphase] < polyArcs[iphase].arcs[i].cap)
120905b261ecSmrg		    	cap[iphase]++;
121005b261ecSmrg		    while (cap[iphase] < polyArcs[iphase].arcs[i].cap) {
121105b261ecSmrg			int	arcIndex, end;
121205b261ecSmrg			miArcDataPtr	arcData0;
121305b261ecSmrg
121405b261ecSmrg			arcIndex = polyArcs[iphase].caps[cap[iphase]].arcIndex;
121505b261ecSmrg			end = polyArcs[iphase].caps[cap[iphase]].end;
121605b261ecSmrg			arcData0 = &polyArcs[iphase].arcs[arcIndex];
121705b261ecSmrg			miArcCap (pDrawTo, pGCTo,
121805b261ecSmrg 				  &arcData0->bounds[end], end,
121905b261ecSmrg				  arcData0->arc.x, arcData0->arc.y,
122005b261ecSmrg				  (double) arcData0->arc.width / 2.0,
122105b261ecSmrg 				  (double) arcData0->arc.height / 2.0);
122205b261ecSmrg			++cap[iphase];
122305b261ecSmrg		    }
122405b261ecSmrg		    while (join[iphase] < polyArcs[iphase].arcs[i].join) {
122505b261ecSmrg			int	arcIndex0, arcIndex1, end0, end1;
122605b261ecSmrg			int	phase0, phase1;
122705b261ecSmrg			miArcDataPtr	arcData0, arcData1;
122805b261ecSmrg			miArcJoinPtr	joinp;
122905b261ecSmrg
123005b261ecSmrg			joinp = &polyArcs[iphase].joins[join[iphase]];
123105b261ecSmrg			arcIndex0 = joinp->arcIndex0;
123205b261ecSmrg			end0 = joinp->end0;
123305b261ecSmrg			arcIndex1 = joinp->arcIndex1;
123405b261ecSmrg			end1 = joinp->end1;
123505b261ecSmrg			phase0 = joinp->phase0;
123605b261ecSmrg			phase1 = joinp->phase1;
123705b261ecSmrg			arcData0 = &polyArcs[phase0].arcs[arcIndex0];
123805b261ecSmrg			arcData1 = &polyArcs[phase1].arcs[arcIndex1];
123905b261ecSmrg			miArcJoin (pDrawTo, pGCTo,
124005b261ecSmrg				  &arcData0->bounds[end0],
124105b261ecSmrg 				  &arcData1->bounds[end1],
124205b261ecSmrg				  arcData0->arc.x, arcData0->arc.y,
124305b261ecSmrg				  (double) arcData0->arc.width / 2.0,
124405b261ecSmrg 				  (double) arcData0->arc.height / 2.0,
124505b261ecSmrg				  arcData1->arc.x, arcData1->arc.y,
124605b261ecSmrg				  (double) arcData1->arc.width / 2.0,
124705b261ecSmrg 				  (double) arcData1->arc.height / 2.0);
124805b261ecSmrg			++join[iphase];
124905b261ecSmrg		    }
125005b261ecSmrg		    if (fTricky) {
125105b261ecSmrg			if (pGC->serialNumber != pDraw->serialNumber)
125205b261ecSmrg			    ValidateGC (pDraw, pGC);
125305b261ecSmrg		    	(*pGC->ops->PushPixels) (pGC, (PixmapPtr)pDrawTo,
125405b261ecSmrg				 pDraw, pixmapWidth, pixmapHeight, xOrg, yOrg);
125505b261ecSmrg			miClearDrawable ((DrawablePtr) pDrawTo, pGCTo);
125605b261ecSmrg		    }
125705b261ecSmrg		}
125805b261ecSmrg	    }
125905b261ecSmrg	}
126005b261ecSmrg	miFreeArcs(polyArcs, pGC);
126105b261ecSmrg
126205b261ecSmrg	if(fTricky)
126305b261ecSmrg	{
126405b261ecSmrg	    (*pGCTo->pScreen->DestroyPixmap)((PixmapPtr)pDrawTo);
126505b261ecSmrg	    FreeScratchGC(pGCTo);
126605b261ecSmrg	}
126705b261ecSmrg    }
126805b261ecSmrg}
126905b261ecSmrg
127005b261ecSmrgstatic double
127105b261ecSmrgangleBetween (SppPointRec center, SppPointRec point1, SppPointRec point2)
127205b261ecSmrg{
127305b261ecSmrg	double	a1, a2, a;
127405b261ecSmrg
127505b261ecSmrg	/*
127605b261ecSmrg	 * reflect from X coordinates back to ellipse
127705b261ecSmrg	 * coordinates -- y increasing upwards
127805b261ecSmrg	 */
127905b261ecSmrg	a1 = miDatan2 (- (point1.y - center.y), point1.x - center.x);
128005b261ecSmrg	a2 = miDatan2 (- (point2.y - center.y), point2.x - center.x);
128105b261ecSmrg	a = a2 - a1;
128205b261ecSmrg	if (a <= -180.0)
128305b261ecSmrg		a += 360.0;
128405b261ecSmrg	else if (a > 180.0)
128505b261ecSmrg		a -= 360.0;
128605b261ecSmrg	return a;
128705b261ecSmrg}
128805b261ecSmrg
128905b261ecSmrgstatic void
129005b261ecSmrgtranslateBounds (
129105b261ecSmrg	miArcFacePtr	b,
129205b261ecSmrg	int		x,
129305b261ecSmrg	int		y,
129405b261ecSmrg	double		fx,
129505b261ecSmrg	double		fy)
129605b261ecSmrg{
129705b261ecSmrg	fx += x;
129805b261ecSmrg	fy += y;
129905b261ecSmrg	b->clock.x -= fx;
130005b261ecSmrg	b->clock.y -= fy;
130105b261ecSmrg	b->center.x -= fx;
130205b261ecSmrg	b->center.y -= fy;
130305b261ecSmrg	b->counterClock.x -= fx;
130405b261ecSmrg	b->counterClock.y -= fy;
130505b261ecSmrg}
130605b261ecSmrg
130705b261ecSmrgstatic void
130805b261ecSmrgmiArcJoin(DrawablePtr pDraw, GCPtr pGC, miArcFacePtr pLeft,
130905b261ecSmrg	  miArcFacePtr pRight, int xOrgLeft, int yOrgLeft,
131005b261ecSmrg	  double xFtransLeft, double yFtransLeft,
131105b261ecSmrg	  int xOrgRight, int yOrgRight,
131205b261ecSmrg	  double xFtransRight, double yFtransRight)
131305b261ecSmrg{
131405b261ecSmrg	SppPointRec	center, corner, otherCorner;
131505b261ecSmrg	SppPointRec	poly[5], e;
131605b261ecSmrg	SppPointPtr	pArcPts;
131705b261ecSmrg	int		cpt;
131805b261ecSmrg	SppArcRec	arc;
131905b261ecSmrg	miArcFaceRec	Right, Left;
132005b261ecSmrg	int		polyLen = 0;
132105b261ecSmrg	int		xOrg, yOrg;
132205b261ecSmrg	double		xFtrans, yFtrans;
132305b261ecSmrg	double		a;
132405b261ecSmrg	double		ae, ac2, ec2, bc2, de;
132505b261ecSmrg	double		width;
132605b261ecSmrg
132705b261ecSmrg	xOrg = (xOrgRight + xOrgLeft) / 2;
132805b261ecSmrg	yOrg = (yOrgRight + yOrgLeft) / 2;
132905b261ecSmrg	xFtrans = (xFtransLeft + xFtransRight) / 2;
133005b261ecSmrg	yFtrans = (yFtransLeft + yFtransRight) / 2;
133105b261ecSmrg	Right = *pRight;
133205b261ecSmrg	translateBounds (&Right, xOrg - xOrgRight, yOrg - yOrgRight,
133305b261ecSmrg				 xFtrans - xFtransRight, yFtrans - yFtransRight);
133405b261ecSmrg	Left = *pLeft;
133505b261ecSmrg	translateBounds (&Left, xOrg - xOrgLeft, yOrg - yOrgLeft,
133605b261ecSmrg				 xFtrans - xFtransLeft, yFtrans - yFtransLeft);
133705b261ecSmrg	pRight = &Right;
133805b261ecSmrg	pLeft = &Left;
133905b261ecSmrg
134005b261ecSmrg	if (pRight->clock.x == pLeft->counterClock.x &&
134105b261ecSmrg	    pRight->clock.y == pLeft->counterClock.y)
134205b261ecSmrg		return;
134305b261ecSmrg	center = pRight->center;
134405b261ecSmrg	if (0 <= (a = angleBetween (center, pRight->clock, pLeft->counterClock))
134505b261ecSmrg 	    && a <= 180.0)
134605b261ecSmrg 	{
134705b261ecSmrg		corner = pRight->clock;
134805b261ecSmrg		otherCorner = pLeft->counterClock;
134905b261ecSmrg	} else {
135005b261ecSmrg		a = angleBetween (center, pLeft->clock, pRight->counterClock);
135105b261ecSmrg		corner = pLeft->clock;
135205b261ecSmrg		otherCorner = pRight->counterClock;
135305b261ecSmrg	}
135405b261ecSmrg	switch (pGC->joinStyle) {
135505b261ecSmrg	case JoinRound:
135605b261ecSmrg		width = (pGC->lineWidth ? (double)pGC->lineWidth : (double)1);
135705b261ecSmrg
135805b261ecSmrg		arc.x = center.x - width/2;
135905b261ecSmrg		arc.y = center.y - width/2;
136005b261ecSmrg		arc.width = width;
136105b261ecSmrg		arc.height = width;
136205b261ecSmrg		arc.angle1 = -miDatan2 (corner.y - center.y, corner.x - center.x);
136305b261ecSmrg		arc.angle2 = a;
136405b261ecSmrg		pArcPts = (SppPointPtr) xalloc (3 * sizeof (SppPointRec));
136505b261ecSmrg		if (!pArcPts)
136605b261ecSmrg		    return;
136705b261ecSmrg		pArcPts[0].x = otherCorner.x;
136805b261ecSmrg		pArcPts[0].y = otherCorner.y;
136905b261ecSmrg		pArcPts[1].x = center.x;
137005b261ecSmrg		pArcPts[1].y = center.y;
137105b261ecSmrg		pArcPts[2].x = corner.x;
137205b261ecSmrg		pArcPts[2].y = corner.y;
137305b261ecSmrg		if( (cpt = miGetArcPts(&arc, 3, &pArcPts)) )
137405b261ecSmrg		{
137505b261ecSmrg			/* by drawing with miFillSppPoly and setting the endpoints of the arc
137605b261ecSmrg			 * to be the corners, we assure that the cap will meet up with the
137705b261ecSmrg			 * rest of the line */
137805b261ecSmrg			miFillSppPoly(pDraw, pGC, cpt, pArcPts, xOrg, yOrg, xFtrans, yFtrans);
137905b261ecSmrg		}
138005b261ecSmrg		xfree(pArcPts);
138105b261ecSmrg		return;
138205b261ecSmrg	case JoinMiter:
138305b261ecSmrg		/*
138405b261ecSmrg		 * don't miter arcs with less than 11 degrees between them
138505b261ecSmrg		 */
138605b261ecSmrg		if (a < 169.0) {
138705b261ecSmrg			poly[0] = corner;
138805b261ecSmrg			poly[1] = center;
138905b261ecSmrg			poly[2] = otherCorner;
139005b261ecSmrg			bc2 = (corner.x - otherCorner.x) * (corner.x - otherCorner.x) +
139105b261ecSmrg			      (corner.y - otherCorner.y) * (corner.y - otherCorner.y);
139205b261ecSmrg			ec2 = bc2 / 4;
139305b261ecSmrg			ac2 = (corner.x - center.x) * (corner.x - center.x) +
139405b261ecSmrg			      (corner.y - center.y) * (corner.y - center.y);
139505b261ecSmrg			ae = sqrt (ac2 - ec2);
139605b261ecSmrg			de = ec2 / ae;
139705b261ecSmrg			e.x = (corner.x + otherCorner.x) / 2;
139805b261ecSmrg			e.y = (corner.y + otherCorner.y) / 2;
139905b261ecSmrg			poly[3].x = e.x + de * (e.x - center.x) / ae;
140005b261ecSmrg			poly[3].y = e.y + de * (e.y - center.y) / ae;
140105b261ecSmrg			poly[4] = corner;
140205b261ecSmrg			polyLen = 5;
140305b261ecSmrg			break;
140405b261ecSmrg		}
140505b261ecSmrg	case JoinBevel:
140605b261ecSmrg		poly[0] = corner;
140705b261ecSmrg		poly[1] = center;
140805b261ecSmrg		poly[2] = otherCorner;
140905b261ecSmrg		poly[3] = corner;
141005b261ecSmrg		polyLen = 4;
141105b261ecSmrg		break;
141205b261ecSmrg	}
141305b261ecSmrg	miFillSppPoly (pDraw, pGC, polyLen, poly, xOrg, yOrg, xFtrans, yFtrans);
141405b261ecSmrg}
141505b261ecSmrg
141605b261ecSmrg/*ARGSUSED*/
141705b261ecSmrgstatic void
141805b261ecSmrgmiArcCap (
141905b261ecSmrg	DrawablePtr	pDraw,
142005b261ecSmrg	GCPtr		pGC,
142105b261ecSmrg	miArcFacePtr	pFace,
142205b261ecSmrg	int		end,
142305b261ecSmrg	int		xOrg,
142405b261ecSmrg	int		yOrg,
142505b261ecSmrg	double		xFtrans,
142605b261ecSmrg	double		yFtrans)
142705b261ecSmrg{
142805b261ecSmrg	SppPointRec	corner, otherCorner, center, endPoint, poly[5];
142905b261ecSmrg
143005b261ecSmrg	corner = pFace->clock;
143105b261ecSmrg	otherCorner = pFace->counterClock;
143205b261ecSmrg	center = pFace->center;
143305b261ecSmrg	switch (pGC->capStyle) {
143405b261ecSmrg	case CapProjecting:
143505b261ecSmrg		poly[0].x = otherCorner.x;
143605b261ecSmrg		poly[0].y = otherCorner.y;
143705b261ecSmrg		poly[1].x = corner.x;
143805b261ecSmrg		poly[1].y = corner.y;
143905b261ecSmrg		poly[2].x = corner.x -
144005b261ecSmrg 				(center.y - corner.y);
144105b261ecSmrg		poly[2].y = corner.y +
144205b261ecSmrg 				(center.x - corner.x);
144305b261ecSmrg		poly[3].x = otherCorner.x -
144405b261ecSmrg 				(otherCorner.y - center.y);
144505b261ecSmrg		poly[3].y = otherCorner.y +
144605b261ecSmrg 				(otherCorner.x - center.x);
144705b261ecSmrg		poly[4].x = otherCorner.x;
144805b261ecSmrg		poly[4].y = otherCorner.y;
144905b261ecSmrg		miFillSppPoly (pDraw, pGC, 5, poly, xOrg, yOrg, xFtrans, yFtrans);
145005b261ecSmrg		break;
145105b261ecSmrg	case CapRound:
145205b261ecSmrg		/*
145305b261ecSmrg		 * miRoundCap just needs these to be unequal.
145405b261ecSmrg		 */
145505b261ecSmrg		endPoint = center;
145605b261ecSmrg		endPoint.x = endPoint.x + 100;
145705b261ecSmrg		miRoundCap (pDraw, pGC, center, endPoint, corner, otherCorner, 0,
145805b261ecSmrg			    -xOrg, -yOrg, xFtrans, yFtrans);
145905b261ecSmrg		break;
146005b261ecSmrg	}
146105b261ecSmrg}
146205b261ecSmrg
146305b261ecSmrg/* MIROUNDCAP -- a private helper function
146405b261ecSmrg * Put Rounded cap on end. pCenter is the center of this end of the line
146505b261ecSmrg * pEnd is the center of the other end of the line. pCorner is one of the
146605b261ecSmrg * two corners at this end of the line.
146705b261ecSmrg * NOTE:  pOtherCorner must be counter-clockwise from pCorner.
146805b261ecSmrg */
146905b261ecSmrg/*ARGSUSED*/
147005b261ecSmrgstatic void
147105b261ecSmrgmiRoundCap(
147205b261ecSmrg    DrawablePtr	pDraw,
147305b261ecSmrg    GCPtr	pGC,
147405b261ecSmrg    SppPointRec	pCenter,
147505b261ecSmrg    SppPointRec	pEnd,
147605b261ecSmrg    SppPointRec	pCorner,
147705b261ecSmrg    SppPointRec	pOtherCorner,
147805b261ecSmrg    int		fLineEnd,
147905b261ecSmrg    int		xOrg,
148005b261ecSmrg    int		yOrg,
148105b261ecSmrg    double	xFtrans,
148205b261ecSmrg    double	yFtrans)
148305b261ecSmrg{
148405b261ecSmrg    int		cpt;
148505b261ecSmrg    double	width;
148605b261ecSmrg    SppArcRec	arc;
148705b261ecSmrg    SppPointPtr	pArcPts;
148805b261ecSmrg
148905b261ecSmrg    width = (pGC->lineWidth ? (double)pGC->lineWidth : (double)1);
149005b261ecSmrg
149105b261ecSmrg    arc.x = pCenter.x - width/2;
149205b261ecSmrg    arc.y = pCenter.y - width/2;
149305b261ecSmrg    arc.width = width;
149405b261ecSmrg    arc.height = width;
149505b261ecSmrg    arc.angle1 = -miDatan2 (pCorner.y - pCenter.y, pCorner.x - pCenter.x);
149605b261ecSmrg    if(PTISEQUAL(pCenter, pEnd))
149705b261ecSmrg	arc.angle2 = - 180.0;
149805b261ecSmrg    else {
149905b261ecSmrg	arc.angle2 = -miDatan2 (pOtherCorner.y - pCenter.y, pOtherCorner.x - pCenter.x) - arc.angle1;
150005b261ecSmrg	if (arc.angle2 < 0)
150105b261ecSmrg	    arc.angle2 += 360.0;
150205b261ecSmrg    }
150305b261ecSmrg    pArcPts = (SppPointPtr) NULL;
150405b261ecSmrg    if( (cpt = miGetArcPts(&arc, 0, &pArcPts)) )
150505b261ecSmrg    {
150605b261ecSmrg	/* by drawing with miFillSppPoly and setting the endpoints of the arc
150705b261ecSmrg	 * to be the corners, we assure that the cap will meet up with the
150805b261ecSmrg	 * rest of the line */
150905b261ecSmrg	miFillSppPoly(pDraw, pGC, cpt, pArcPts, -xOrg, -yOrg, xFtrans, yFtrans);
151005b261ecSmrg    }
151105b261ecSmrg    xfree(pArcPts);
151205b261ecSmrg}
151305b261ecSmrg
151405b261ecSmrg/*
151505b261ecSmrg * To avoid inaccuracy at the cardinal points, use trig functions
151605b261ecSmrg * which are exact for those angles
151705b261ecSmrg */
151805b261ecSmrg
151905b261ecSmrg#ifndef M_PI
152005b261ecSmrg#define M_PI	3.14159265358979323846
152105b261ecSmrg#endif
152205b261ecSmrg#ifndef M_PI_2
152305b261ecSmrg#define M_PI_2	1.57079632679489661923
152405b261ecSmrg#endif
152505b261ecSmrg
152605b261ecSmrg# define Dsin(d)	((d) == 0.0 ? 0.0 : ((d) == 90.0 ? 1.0 : sin(d*M_PI/180.0)))
152705b261ecSmrg# define Dcos(d)	((d) == 0.0 ? 1.0 : ((d) == 90.0 ? 0.0 : cos(d*M_PI/180.0)))
152805b261ecSmrg# define mod(a,b)	((a) >= 0 ? (a) % (b) : (b) - (-a) % (b))
152905b261ecSmrg
153005b261ecSmrgstatic double
153105b261ecSmrgmiDcos (double a)
153205b261ecSmrg{
153305b261ecSmrg	int	i;
153405b261ecSmrg
153505b261ecSmrg	if (floor (a/90) == a/90) {
153605b261ecSmrg		i = (int) (a/90.0);
153705b261ecSmrg		switch (mod (i, 4)) {
153805b261ecSmrg		case 0: return 1;
153905b261ecSmrg		case 1: return 0;
154005b261ecSmrg		case 2: return -1;
154105b261ecSmrg		case 3: return 0;
154205b261ecSmrg		}
154305b261ecSmrg	}
154405b261ecSmrg	return cos (a * M_PI / 180.0);
154505b261ecSmrg}
154605b261ecSmrg
154705b261ecSmrgstatic double
154805b261ecSmrgmiDsin (double a)
154905b261ecSmrg{
155005b261ecSmrg	int	i;
155105b261ecSmrg
155205b261ecSmrg	if (floor (a/90) == a/90) {
155305b261ecSmrg		i = (int) (a/90.0);
155405b261ecSmrg		switch (mod (i, 4)) {
155505b261ecSmrg		case 0: return 0;
155605b261ecSmrg		case 1: return 1;
155705b261ecSmrg		case 2: return 0;
155805b261ecSmrg		case 3: return -1;
155905b261ecSmrg		}
156005b261ecSmrg	}
156105b261ecSmrg	return sin (a * M_PI / 180.0);
156205b261ecSmrg}
156305b261ecSmrg
156405b261ecSmrgstatic double
156505b261ecSmrgmiDasin (double v)
156605b261ecSmrg{
156705b261ecSmrg    if (v == 0)
156805b261ecSmrg	return 0.0;
156905b261ecSmrg    if (v == 1.0)
157005b261ecSmrg	return 90.0;
157105b261ecSmrg    if (v == -1.0)
157205b261ecSmrg	return -90.0;
157305b261ecSmrg    return asin(v) * (180.0 / M_PI);
157405b261ecSmrg}
157505b261ecSmrg
157605b261ecSmrgstatic double
157705b261ecSmrgmiDatan2 (double dy, double dx)
157805b261ecSmrg{
157905b261ecSmrg    if (dy == 0) {
158005b261ecSmrg	if (dx >= 0)
158105b261ecSmrg	    return 0.0;
158205b261ecSmrg	return 180.0;
158305b261ecSmrg    } else if (dx == 0) {
158405b261ecSmrg	if (dy > 0)
158505b261ecSmrg	    return 90.0;
158605b261ecSmrg	return -90.0;
158705b261ecSmrg    } else if (Fabs (dy) == Fabs (dx)) {
158805b261ecSmrg	if (dy > 0) {
158905b261ecSmrg	    if (dx > 0)
159005b261ecSmrg		return 45.0;
159105b261ecSmrg	    return 135.0;
159205b261ecSmrg	} else {
159305b261ecSmrg	    if (dx > 0)
159405b261ecSmrg		return 315.0;
159505b261ecSmrg	    return 225.0;
159605b261ecSmrg	}
159705b261ecSmrg    } else {
159805b261ecSmrg	return atan2 (dy, dx) * (180.0 / M_PI);
159905b261ecSmrg    }
160005b261ecSmrg}
160105b261ecSmrg
160205b261ecSmrg/* MIGETARCPTS -- Converts an arc into a set of line segments -- a helper
160305b261ecSmrg * routine for filled arc and line (round cap) code.
160405b261ecSmrg * Returns the number of points in the arc.  Note that it takes a pointer
160505b261ecSmrg * to a pointer to where it should put the points and an index (cpt).
160605b261ecSmrg * This procedure allocates the space necessary to fit the arc points.
160705b261ecSmrg * Sometimes it's convenient for those points to be at the end of an existing
160805b261ecSmrg * array. (For example, if we want to leave a spare point to make sectors
160905b261ecSmrg * instead of segments.)  So we pass in the xalloc()ed chunk that contains the
161005b261ecSmrg * array and an index saying where we should start stashing the points.
161105b261ecSmrg * If there isn't an array already, we just pass in a null pointer and
161205b261ecSmrg * count on xrealloc() to handle the null pointer correctly.
161305b261ecSmrg */
161405b261ecSmrgstatic int
161505b261ecSmrgmiGetArcPts(
161605b261ecSmrg    SppArcPtr	parc,	/* points to an arc */
161705b261ecSmrg    int		cpt,	/* number of points already in arc list */
161805b261ecSmrg    SppPointPtr	*ppPts) /* pointer to pointer to arc-list -- modified */
161905b261ecSmrg{
162005b261ecSmrg    double 	st,	/* Start Theta, start angle */
162105b261ecSmrg                et,	/* End Theta, offset from start theta */
162205b261ecSmrg		dt,	/* Delta Theta, angle to sweep ellipse */
162305b261ecSmrg		cdt,	/* Cos Delta Theta, actually 2 cos(dt) */
162405b261ecSmrg    		x0, y0,	/* the recurrence formula needs two points to start */
162505b261ecSmrg		x1, y1,
162605b261ecSmrg		x2, y2, /* this will be the new point generated */
162705b261ecSmrg		xc, yc; /* the center point */
162805b261ecSmrg    int		count, i;
162905b261ecSmrg    SppPointPtr	poly;
163005b261ecSmrg
163105b261ecSmrg    /* The spec says that positive angles indicate counterclockwise motion.
163205b261ecSmrg     * Given our coordinate system (with 0,0 in the upper left corner),
163305b261ecSmrg     * the screen appears flipped in Y.  The easiest fix is to negate the
163405b261ecSmrg     * angles given */
163505b261ecSmrg
163605b261ecSmrg    st = - parc->angle1;
163705b261ecSmrg
163805b261ecSmrg    et = - parc->angle2;
163905b261ecSmrg
164005b261ecSmrg    /* Try to get a delta theta that is within 1/2 pixel.  Then adjust it
164105b261ecSmrg     * so that it divides evenly into the total.
164205b261ecSmrg     * I'm just using cdt 'cause I'm lazy.
164305b261ecSmrg     */
164405b261ecSmrg    cdt = parc->width;
164505b261ecSmrg    if (parc->height > cdt)
164605b261ecSmrg	cdt = parc->height;
164705b261ecSmrg    cdt /= 2.0;
164805b261ecSmrg    if(cdt <= 0)
164905b261ecSmrg	return 0;
165005b261ecSmrg    if (cdt < 1.0)
165105b261ecSmrg	cdt = 1.0;
165205b261ecSmrg    dt = miDasin ( 1.0 / cdt ); /* minimum step necessary */
165305b261ecSmrg    count = et/dt;
165405b261ecSmrg    count = abs(count) + 1;
165505b261ecSmrg    dt = et/count;
165605b261ecSmrg    count++;
165705b261ecSmrg
165805b261ecSmrg    cdt = 2 * miDcos(dt);
165905b261ecSmrg    if (!(poly = (SppPointPtr) xrealloc((pointer)*ppPts,
166005b261ecSmrg					(cpt + count) * sizeof(SppPointRec))))
166105b261ecSmrg	return(0);
166205b261ecSmrg    *ppPts = poly;
166305b261ecSmrg
166405b261ecSmrg    xc = parc->width/2.0;		/* store half width and half height */
166505b261ecSmrg    yc = parc->height/2.0;
166605b261ecSmrg
166705b261ecSmrg    x0 = xc * miDcos(st);
166805b261ecSmrg    y0 = yc * miDsin(st);
166905b261ecSmrg    x1 = xc * miDcos(st + dt);
167005b261ecSmrg    y1 = yc * miDsin(st + dt);
167105b261ecSmrg    xc += parc->x;		/* by adding initial point, these become */
167205b261ecSmrg    yc += parc->y;		/* the center point */
167305b261ecSmrg
167405b261ecSmrg    poly[cpt].x = (xc + x0);
167505b261ecSmrg    poly[cpt].y = (yc + y0);
167605b261ecSmrg    poly[cpt + 1].x = (xc + x1);
167705b261ecSmrg    poly[cpt + 1].y = (yc + y1);
167805b261ecSmrg
167905b261ecSmrg    for(i = 2; i < count; i++)
168005b261ecSmrg    {
168105b261ecSmrg	x2 = cdt * x1 - x0;
168205b261ecSmrg	y2 = cdt * y1 - y0;
168305b261ecSmrg
168405b261ecSmrg 	poly[cpt + i].x = (xc + x2);
168505b261ecSmrg 	poly[cpt + i].y = (yc + y2);
168605b261ecSmrg
168705b261ecSmrg	x0 = x1; y0 = y1;
168805b261ecSmrg	x1 = x2; y1 = y2;
168905b261ecSmrg    }
169005b261ecSmrg    /* adjust the last point */
169105b261ecSmrg    if (abs(parc->angle2) >= 360.0)
169205b261ecSmrg	poly[cpt +i -1] = poly[0];
169305b261ecSmrg    else {
169405b261ecSmrg	poly[cpt +i -1].x = (miDcos(st + et) * parc->width/2.0 + xc);
169505b261ecSmrg	poly[cpt +i -1].y = (miDsin(st + et) * parc->height/2.0 + yc);
169605b261ecSmrg    }
169705b261ecSmrg
169805b261ecSmrg    return(count);
169905b261ecSmrg}
170005b261ecSmrg
170105b261ecSmrgstruct arcData {
170205b261ecSmrg	double	x0, y0, x1, y1;
170305b261ecSmrg	int	selfJoin;
170405b261ecSmrg};
170505b261ecSmrg
170605b261ecSmrg# define ADD_REALLOC_STEP	20
170705b261ecSmrg
170805b261ecSmrgstatic void
170905b261ecSmrgaddCap (
171005b261ecSmrg	miArcCapPtr	*capsp,
171105b261ecSmrg	int		*ncapsp,
171205b261ecSmrg	int		*sizep,
171305b261ecSmrg	int		end,
171405b261ecSmrg	int		arcIndex)
171505b261ecSmrg{
171605b261ecSmrg	int newsize;
171705b261ecSmrg	miArcCapPtr	cap;
171805b261ecSmrg
171905b261ecSmrg	if (*ncapsp == *sizep)
172005b261ecSmrg	{
172105b261ecSmrg	    newsize = *sizep + ADD_REALLOC_STEP;
172205b261ecSmrg	    cap = (miArcCapPtr) xrealloc (*capsp,
172305b261ecSmrg					  newsize * sizeof (**capsp));
172405b261ecSmrg	    if (!cap)
172505b261ecSmrg		return;
172605b261ecSmrg	    *sizep = newsize;
172705b261ecSmrg	    *capsp = cap;
172805b261ecSmrg	}
172905b261ecSmrg	cap = &(*capsp)[*ncapsp];
173005b261ecSmrg	cap->end = end;
173105b261ecSmrg	cap->arcIndex = arcIndex;
173205b261ecSmrg	++*ncapsp;
173305b261ecSmrg}
173405b261ecSmrg
173505b261ecSmrgstatic void
173605b261ecSmrgaddJoin (
173705b261ecSmrg	miArcJoinPtr	*joinsp,
173805b261ecSmrg	int		*njoinsp,
173905b261ecSmrg	int		*sizep,
174005b261ecSmrg	int		end0,
174105b261ecSmrg	int		index0,
174205b261ecSmrg	int		phase0,
174305b261ecSmrg	int		end1,
174405b261ecSmrg	int		index1,
174505b261ecSmrg	int		phase1)
174605b261ecSmrg{
174705b261ecSmrg	int newsize;
174805b261ecSmrg	miArcJoinPtr	join;
174905b261ecSmrg
175005b261ecSmrg	if (*njoinsp == *sizep)
175105b261ecSmrg	{
175205b261ecSmrg	    newsize = *sizep + ADD_REALLOC_STEP;
175305b261ecSmrg	    join = (miArcJoinPtr) xrealloc (*joinsp,
175405b261ecSmrg					    newsize * sizeof (**joinsp));
175505b261ecSmrg	    if (!join)
175605b261ecSmrg		return;
175705b261ecSmrg	    *sizep = newsize;
175805b261ecSmrg	    *joinsp = join;
175905b261ecSmrg	}
176005b261ecSmrg	join = &(*joinsp)[*njoinsp];
176105b261ecSmrg	join->end0 = end0;
176205b261ecSmrg	join->arcIndex0 = index0;
176305b261ecSmrg	join->phase0 = phase0;
176405b261ecSmrg	join->end1 = end1;
176505b261ecSmrg	join->arcIndex1 = index1;
176605b261ecSmrg	join->phase1 = phase1;
176705b261ecSmrg	++*njoinsp;
176805b261ecSmrg}
176905b261ecSmrg
177005b261ecSmrgstatic miArcDataPtr
177105b261ecSmrgaddArc (
177205b261ecSmrg	miArcDataPtr	*arcsp,
177305b261ecSmrg	int		*narcsp,
177405b261ecSmrg	int		*sizep,
177505b261ecSmrg	xArc		*xarc)
177605b261ecSmrg{
177705b261ecSmrg	int newsize;
177805b261ecSmrg	miArcDataPtr	arc;
177905b261ecSmrg
178005b261ecSmrg	if (*narcsp == *sizep)
178105b261ecSmrg	{
178205b261ecSmrg	    newsize = *sizep + ADD_REALLOC_STEP;
178305b261ecSmrg	    arc = (miArcDataPtr) xrealloc (*arcsp,
178405b261ecSmrg					   newsize * sizeof (**arcsp));
178505b261ecSmrg	    if (!arc)
178605b261ecSmrg		return (miArcDataPtr)NULL;
178705b261ecSmrg	    *sizep = newsize;
178805b261ecSmrg	    *arcsp = arc;
178905b261ecSmrg	}
179005b261ecSmrg	arc = &(*arcsp)[*narcsp];
179105b261ecSmrg	arc->arc = *xarc;
179205b261ecSmrg	++*narcsp;
179305b261ecSmrg	return arc;
179405b261ecSmrg}
179505b261ecSmrg
179605b261ecSmrgstatic void
179705b261ecSmrgmiFreeArcs(
179805b261ecSmrg    miPolyArcPtr arcs,
179905b261ecSmrg    GCPtr pGC)
180005b261ecSmrg{
180105b261ecSmrg	int iphase;
180205b261ecSmrg
180305b261ecSmrg	for (iphase = ((pGC->lineStyle == LineDoubleDash) ? 1 : 0);
180405b261ecSmrg 	     iphase >= 0;
180505b261ecSmrg	     iphase--)
180605b261ecSmrg	{
180705b261ecSmrg	    if (arcs[iphase].narcs > 0)
180805b261ecSmrg		xfree(arcs[iphase].arcs);
180905b261ecSmrg	    if (arcs[iphase].njoins > 0)
181005b261ecSmrg		xfree(arcs[iphase].joins);
181105b261ecSmrg	    if (arcs[iphase].ncaps > 0)
181205b261ecSmrg		xfree(arcs[iphase].caps);
181305b261ecSmrg	}
181405b261ecSmrg	xfree(arcs);
181505b261ecSmrg}
181605b261ecSmrg
181705b261ecSmrg/*
181805b261ecSmrg * map angles to radial distance.  This only deals with the first quadrant
181905b261ecSmrg */
182005b261ecSmrg
182105b261ecSmrg/*
182205b261ecSmrg * a polygonal approximation to the arc for computing arc lengths
182305b261ecSmrg */
182405b261ecSmrg
182505b261ecSmrg# define DASH_MAP_SIZE	91
182605b261ecSmrg
182705b261ecSmrg# define dashIndexToAngle(di)	((((double) (di)) * 90.0) / ((double) DASH_MAP_SIZE - 1))
182805b261ecSmrg# define xAngleToDashIndex(xa)	((((long) (xa)) * (DASH_MAP_SIZE - 1)) / (90 * 64))
182905b261ecSmrg# define dashIndexToXAngle(di)	((((long) (di)) * (90 * 64)) / (DASH_MAP_SIZE - 1))
183005b261ecSmrg# define dashXAngleStep	(((double) (90 * 64)) / ((double) (DASH_MAP_SIZE - 1)))
183105b261ecSmrg
183205b261ecSmrgtypedef struct {
183305b261ecSmrg	double	map[DASH_MAP_SIZE];
183405b261ecSmrg} dashMap;
183505b261ecSmrg
183605b261ecSmrgstatic int computeAngleFromPath(int startAngle, int endAngle, dashMap *map,
183705b261ecSmrg				int *lenp, int backwards);
183805b261ecSmrg
183905b261ecSmrgstatic void
184005b261ecSmrgcomputeDashMap (
184105b261ecSmrg	xArc	*arcp,
184205b261ecSmrg	dashMap	*map)
184305b261ecSmrg{
184405b261ecSmrg	int	di;
184505b261ecSmrg	double	a, x, y, prevx = 0.0, prevy = 0.0, dist;
184605b261ecSmrg
184705b261ecSmrg	for (di = 0; di < DASH_MAP_SIZE; di++) {
184805b261ecSmrg		a = dashIndexToAngle (di);
184905b261ecSmrg		x = ((double) arcp->width / 2.0) * miDcos (a);
185005b261ecSmrg		y = ((double) arcp->height / 2.0) * miDsin (a);
185105b261ecSmrg		if (di == 0) {
185205b261ecSmrg			map->map[di] = 0.0;
185305b261ecSmrg		} else {
185405b261ecSmrg			dist = hypot (x - prevx, y - prevy);
185505b261ecSmrg			map->map[di] = map->map[di - 1] + dist;
185605b261ecSmrg		}
185705b261ecSmrg		prevx = x;
185805b261ecSmrg		prevy = y;
185905b261ecSmrg	}
186005b261ecSmrg}
186105b261ecSmrg
186205b261ecSmrgtypedef enum {HORIZONTAL, VERTICAL, OTHER} arcTypes;
186305b261ecSmrg
186405b261ecSmrg/* this routine is a bit gory */
186505b261ecSmrg
186605b261ecSmrgstatic miPolyArcPtr
186705b261ecSmrgmiComputeArcs (
186805b261ecSmrg	xArc	*parcs,
186905b261ecSmrg	int	narcs,
187005b261ecSmrg	GCPtr	pGC)
187105b261ecSmrg{
187205b261ecSmrg	int		isDashed, isDoubleDash;
187305b261ecSmrg	int		dashOffset;
187405b261ecSmrg	miPolyArcPtr	arcs;
187505b261ecSmrg	int		start, i, j, k = 0, nexti, nextk = 0;
187605b261ecSmrg	int		joinSize[2];
187705b261ecSmrg	int		capSize[2];
187805b261ecSmrg	int		arcSize[2];
187905b261ecSmrg	int		angle2;
188005b261ecSmrg	double		a0, a1;
188105b261ecSmrg	struct arcData	*data;
188205b261ecSmrg	miArcDataPtr	arc;
188305b261ecSmrg	xArc		xarc;
188405b261ecSmrg	int		iphase, prevphase = 0, joinphase;
188505b261ecSmrg	int		arcsJoin;
188605b261ecSmrg	int		selfJoin;
188705b261ecSmrg
188805b261ecSmrg	int		iDash = 0, dashRemaining;
188905b261ecSmrg	int		iDashStart = 0, dashRemainingStart = 0, iphaseStart;
189005b261ecSmrg	int		startAngle, spanAngle, endAngle, backwards = 0;
189105b261ecSmrg	int		prevDashAngle, dashAngle;
189205b261ecSmrg	dashMap		map;
189305b261ecSmrg
189405b261ecSmrg	isDashed = !(pGC->lineStyle == LineSolid);
189505b261ecSmrg	isDoubleDash = (pGC->lineStyle == LineDoubleDash);
189605b261ecSmrg	dashOffset = pGC->dashOffset;
189705b261ecSmrg
18984642e01fSmrg	data = (struct arcData *) xalloc (narcs * sizeof (struct arcData));
189905b261ecSmrg	if (!data)
190005b261ecSmrg	    return (miPolyArcPtr)NULL;
190105b261ecSmrg	arcs = (miPolyArcPtr) xalloc (sizeof (*arcs) * (isDoubleDash ? 2 : 1));
190205b261ecSmrg	if (!arcs)
190305b261ecSmrg	{
19044642e01fSmrg	    xfree(data);
190505b261ecSmrg	    return (miPolyArcPtr)NULL;
190605b261ecSmrg	}
190705b261ecSmrg	for (i = 0; i < narcs; i++) {
190805b261ecSmrg		a0 = todeg (parcs[i].angle1);
190905b261ecSmrg		angle2 = parcs[i].angle2;
191005b261ecSmrg		if (angle2 > FULLCIRCLE)
191105b261ecSmrg			angle2 = FULLCIRCLE;
191205b261ecSmrg		else if (angle2 < -FULLCIRCLE)
191305b261ecSmrg			angle2 = -FULLCIRCLE;
191405b261ecSmrg		data[i].selfJoin = angle2 == FULLCIRCLE || angle2 == -FULLCIRCLE;
191505b261ecSmrg		a1 = todeg (parcs[i].angle1 + angle2);
191605b261ecSmrg		data[i].x0 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a0));
191705b261ecSmrg		data[i].y0 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a0));
191805b261ecSmrg		data[i].x1 = parcs[i].x + (double) parcs[i].width / 2 * (1 + miDcos (a1));
191905b261ecSmrg		data[i].y1 = parcs[i].y + (double) parcs[i].height / 2 * (1 - miDsin (a1));
192005b261ecSmrg	}
192105b261ecSmrg
192205b261ecSmrg	for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++) {
192305b261ecSmrg		arcs[iphase].njoins = 0;
192405b261ecSmrg		arcs[iphase].joins = 0;
192505b261ecSmrg		joinSize[iphase] = 0;
192605b261ecSmrg
192705b261ecSmrg		arcs[iphase].ncaps = 0;
192805b261ecSmrg		arcs[iphase].caps = 0;
192905b261ecSmrg		capSize[iphase] = 0;
193005b261ecSmrg
193105b261ecSmrg		arcs[iphase].narcs = 0;
193205b261ecSmrg		arcs[iphase].arcs = 0;
193305b261ecSmrg		arcSize[iphase] = 0;
193405b261ecSmrg	}
193505b261ecSmrg
193605b261ecSmrg	iphase = 0;
193705b261ecSmrg	if (isDashed) {
193805b261ecSmrg		iDash = 0;
193905b261ecSmrg		dashRemaining = pGC->dash[0];
194005b261ecSmrg	 	while (dashOffset > 0) {
194105b261ecSmrg			if (dashOffset >= dashRemaining) {
194205b261ecSmrg				dashOffset -= dashRemaining;
194305b261ecSmrg				iphase = iphase ? 0 : 1;
194405b261ecSmrg				iDash++;
194505b261ecSmrg				if (iDash == pGC->numInDashList)
194605b261ecSmrg				    iDash = 0;
194705b261ecSmrg				dashRemaining = pGC->dash[iDash];
194805b261ecSmrg			} else {
194905b261ecSmrg				dashRemaining -= dashOffset;
195005b261ecSmrg				dashOffset = 0;
195105b261ecSmrg			}
195205b261ecSmrg		}
195305b261ecSmrg		iDashStart = iDash;
195405b261ecSmrg		dashRemainingStart = dashRemaining;
195505b261ecSmrg	}
195605b261ecSmrg	iphaseStart = iphase;
195705b261ecSmrg
195805b261ecSmrg	for (i = narcs - 1; i >= 0; i--) {
195905b261ecSmrg		j = i + 1;
196005b261ecSmrg		if (j == narcs)
196105b261ecSmrg			j = 0;
196205b261ecSmrg		if (data[i].selfJoin || i == j ||
196305b261ecSmrg		     (UNEQUAL (data[i].x1, data[j].x0) ||
196405b261ecSmrg		      UNEQUAL (data[i].y1, data[j].y0)))
196505b261ecSmrg 		{
196605b261ecSmrg			if (iphase == 0 || isDoubleDash)
196705b261ecSmrg				addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
196805b261ecSmrg	 				&capSize[iphase], RIGHT_END, 0);
196905b261ecSmrg			break;
197005b261ecSmrg		}
197105b261ecSmrg	}
197205b261ecSmrg	start = i + 1;
197305b261ecSmrg	if (start == narcs)
197405b261ecSmrg		start = 0;
197505b261ecSmrg	i = start;
197605b261ecSmrg	for (;;) {
197705b261ecSmrg		j = i + 1;
197805b261ecSmrg		if (j == narcs)
197905b261ecSmrg			j = 0;
198005b261ecSmrg		nexti = i+1;
198105b261ecSmrg		if (nexti == narcs)
198205b261ecSmrg			nexti = 0;
198305b261ecSmrg		if (isDashed) {
198405b261ecSmrg			/*
198505b261ecSmrg			** deal with dashed arcs.  Use special rules for certain 0 area arcs.
198605b261ecSmrg			** Presumably, the other 0 area arcs still aren't done right.
198705b261ecSmrg			*/
198805b261ecSmrg			arcTypes	arcType = OTHER;
198905b261ecSmrg			CARD16		thisLength;
199005b261ecSmrg
199105b261ecSmrg			if (parcs[i].height == 0
199205b261ecSmrg			    && (parcs[i].angle1 % FULLCIRCLE) == 0x2d00
199305b261ecSmrg			    && parcs[i].angle2 == 0x2d00)
199405b261ecSmrg				arcType = HORIZONTAL;
199505b261ecSmrg			else if (parcs[i].width == 0
199605b261ecSmrg			    && (parcs[i].angle1 % FULLCIRCLE) == 0x1680
199705b261ecSmrg			    && parcs[i].angle2 == 0x2d00)
199805b261ecSmrg				arcType = VERTICAL;
199905b261ecSmrg			if (arcType == OTHER) {
200005b261ecSmrg				/*
200105b261ecSmrg				 * precompute an approximation map
200205b261ecSmrg				 */
200305b261ecSmrg				computeDashMap (&parcs[i], &map);
200405b261ecSmrg				/*
200505b261ecSmrg				 * compute each individual dash segment using the path
200605b261ecSmrg				 * length function
200705b261ecSmrg				 */
200805b261ecSmrg				startAngle = parcs[i].angle1;
200905b261ecSmrg				spanAngle = parcs[i].angle2;
201005b261ecSmrg				if (spanAngle > FULLCIRCLE)
201105b261ecSmrg					spanAngle = FULLCIRCLE;
201205b261ecSmrg				else if (spanAngle < -FULLCIRCLE)
201305b261ecSmrg					spanAngle = -FULLCIRCLE;
201405b261ecSmrg				if (startAngle < 0)
201505b261ecSmrg					startAngle = FULLCIRCLE - (-startAngle) % FULLCIRCLE;
201605b261ecSmrg				if (startAngle >= FULLCIRCLE)
201705b261ecSmrg					startAngle = startAngle % FULLCIRCLE;
201805b261ecSmrg				endAngle = startAngle + spanAngle;
201905b261ecSmrg				backwards = spanAngle < 0;
202005b261ecSmrg			} else {
202105b261ecSmrg				xarc = parcs[i];
202205b261ecSmrg				if (arcType == VERTICAL) {
202305b261ecSmrg					xarc.angle1 = 0x1680;
202405b261ecSmrg					startAngle = parcs[i].y;
202505b261ecSmrg					endAngle = startAngle + parcs[i].height;
202605b261ecSmrg				} else {
202705b261ecSmrg					xarc.angle1 = 0x2d00;
202805b261ecSmrg					startAngle = parcs[i].x;
202905b261ecSmrg					endAngle = startAngle + parcs[i].width;
203005b261ecSmrg				}
203105b261ecSmrg			}
203205b261ecSmrg			dashAngle = startAngle;
203305b261ecSmrg			selfJoin = data[i].selfJoin &&
203405b261ecSmrg 				    (iphase == 0 || isDoubleDash);
203505b261ecSmrg			/*
203605b261ecSmrg			 * add dashed arcs to each bucket
203705b261ecSmrg			 */
203805b261ecSmrg			arc = 0;
203905b261ecSmrg			while (dashAngle != endAngle) {
204005b261ecSmrg				prevDashAngle = dashAngle;
204105b261ecSmrg				if (arcType == OTHER) {
204205b261ecSmrg					dashAngle = computeAngleFromPath (prevDashAngle, endAngle,
204305b261ecSmrg								&map, &dashRemaining, backwards);
204405b261ecSmrg					/* avoid troubles with huge arcs and small dashes */
204505b261ecSmrg					if (dashAngle == prevDashAngle) {
204605b261ecSmrg						if (backwards)
204705b261ecSmrg							dashAngle--;
204805b261ecSmrg						else
204905b261ecSmrg							dashAngle++;
205005b261ecSmrg					}
205105b261ecSmrg				} else {
205205b261ecSmrg					thisLength = (dashAngle + dashRemaining <= endAngle) ?
205305b261ecSmrg					    dashRemaining : endAngle - dashAngle;
205405b261ecSmrg					if (arcType == VERTICAL) {
205505b261ecSmrg						xarc.y = dashAngle;
205605b261ecSmrg						xarc.height = thisLength;
205705b261ecSmrg					} else {
205805b261ecSmrg						xarc.x = dashAngle;
205905b261ecSmrg						xarc.width = thisLength;
206005b261ecSmrg					}
206105b261ecSmrg					dashAngle += thisLength;
206205b261ecSmrg					dashRemaining -= thisLength;
206305b261ecSmrg				}
206405b261ecSmrg				if (iphase == 0 || isDoubleDash) {
206505b261ecSmrg					if (arcType == OTHER) {
206605b261ecSmrg						xarc = parcs[i];
206705b261ecSmrg						spanAngle = prevDashAngle;
206805b261ecSmrg						if (spanAngle < 0)
206905b261ecSmrg						    spanAngle = FULLCIRCLE - (-spanAngle) % FULLCIRCLE;
207005b261ecSmrg						if (spanAngle >= FULLCIRCLE)
207105b261ecSmrg						    spanAngle = spanAngle % FULLCIRCLE;
207205b261ecSmrg						xarc.angle1 = spanAngle;
207305b261ecSmrg						spanAngle = dashAngle - prevDashAngle;
207405b261ecSmrg						if (backwards) {
207505b261ecSmrg							if (dashAngle > prevDashAngle)
207605b261ecSmrg								spanAngle = - FULLCIRCLE + spanAngle;
207705b261ecSmrg						} else {
207805b261ecSmrg							if (dashAngle < prevDashAngle)
207905b261ecSmrg								spanAngle = FULLCIRCLE + spanAngle;
208005b261ecSmrg						}
208105b261ecSmrg						if (spanAngle > FULLCIRCLE)
208205b261ecSmrg						    spanAngle = FULLCIRCLE;
208305b261ecSmrg						if (spanAngle < -FULLCIRCLE)
208405b261ecSmrg						    spanAngle = -FULLCIRCLE;
208505b261ecSmrg						xarc.angle2 = spanAngle;
208605b261ecSmrg					}
208705b261ecSmrg					arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
208805b261ecSmrg 							&arcSize[iphase], &xarc);
208905b261ecSmrg					if (!arc)
209005b261ecSmrg					    goto arcfail;
209105b261ecSmrg					/*
209205b261ecSmrg					 * cap each end of an on/off dash
209305b261ecSmrg					 */
209405b261ecSmrg					if (!isDoubleDash) {
209505b261ecSmrg						if (prevDashAngle != startAngle) {
209605b261ecSmrg							addCap (&arcs[iphase].caps,
209705b261ecSmrg 								&arcs[iphase].ncaps,
209805b261ecSmrg 								&capSize[iphase], RIGHT_END,
209905b261ecSmrg 								arc - arcs[iphase].arcs);
210005b261ecSmrg
210105b261ecSmrg						}
210205b261ecSmrg						if (dashAngle != endAngle) {
210305b261ecSmrg							addCap (&arcs[iphase].caps,
210405b261ecSmrg 								&arcs[iphase].ncaps,
210505b261ecSmrg 								&capSize[iphase], LEFT_END,
210605b261ecSmrg 								arc - arcs[iphase].arcs);
210705b261ecSmrg						}
210805b261ecSmrg					}
210905b261ecSmrg					arc->cap = arcs[iphase].ncaps;
211005b261ecSmrg					arc->join = arcs[iphase].njoins;
211105b261ecSmrg					arc->render = 0;
211205b261ecSmrg					arc->selfJoin = 0;
211305b261ecSmrg					if (dashAngle == endAngle)
211405b261ecSmrg						arc->selfJoin = selfJoin;
211505b261ecSmrg				}
211605b261ecSmrg				prevphase = iphase;
211705b261ecSmrg				if (dashRemaining <= 0) {
211805b261ecSmrg					++iDash;
211905b261ecSmrg					if (iDash == pGC->numInDashList)
212005b261ecSmrg						iDash = 0;
212105b261ecSmrg					iphase = iphase ? 0:1;
212205b261ecSmrg					dashRemaining = pGC->dash[iDash];
212305b261ecSmrg				}
212405b261ecSmrg			}
212505b261ecSmrg			/*
212605b261ecSmrg			 * make sure a place exists for the position data when
212705b261ecSmrg			 * drawing a zero-length arc
212805b261ecSmrg			 */
212905b261ecSmrg			if (startAngle == endAngle) {
213005b261ecSmrg				prevphase = iphase;
213105b261ecSmrg				if (!isDoubleDash && iphase == 1)
213205b261ecSmrg					prevphase = 0;
213305b261ecSmrg				arc = addArc (&arcs[prevphase].arcs, &arcs[prevphase].narcs,
213405b261ecSmrg					      &arcSize[prevphase], &parcs[i]);
213505b261ecSmrg				if (!arc)
213605b261ecSmrg				    goto arcfail;
213705b261ecSmrg				arc->join = arcs[prevphase].njoins;
213805b261ecSmrg				arc->cap = arcs[prevphase].ncaps;
213905b261ecSmrg				arc->selfJoin = data[i].selfJoin;
214005b261ecSmrg			}
214105b261ecSmrg		} else {
214205b261ecSmrg			arc = addArc (&arcs[iphase].arcs, &arcs[iphase].narcs,
214305b261ecSmrg 				      &arcSize[iphase], &parcs[i]);
214405b261ecSmrg			if (!arc)
214505b261ecSmrg			    goto arcfail;
214605b261ecSmrg			arc->join = arcs[iphase].njoins;
214705b261ecSmrg			arc->cap = arcs[iphase].ncaps;
214805b261ecSmrg			arc->selfJoin = data[i].selfJoin;
214905b261ecSmrg			prevphase = iphase;
215005b261ecSmrg		}
215105b261ecSmrg		if (prevphase == 0 || isDoubleDash)
215205b261ecSmrg			k = arcs[prevphase].narcs - 1;
215305b261ecSmrg		if (iphase == 0 || isDoubleDash)
215405b261ecSmrg			nextk = arcs[iphase].narcs;
215505b261ecSmrg		if (nexti == start) {
215605b261ecSmrg			nextk = 0;
215705b261ecSmrg			if (isDashed) {
215805b261ecSmrg				iDash = iDashStart;
215905b261ecSmrg				iphase = iphaseStart;
216005b261ecSmrg				dashRemaining = dashRemainingStart;
216105b261ecSmrg			}
216205b261ecSmrg		}
216305b261ecSmrg		arcsJoin = narcs > 1 && i != j &&
216405b261ecSmrg	 		    ISEQUAL (data[i].x1, data[j].x0) &&
216505b261ecSmrg			    ISEQUAL (data[i].y1, data[j].y0) &&
216605b261ecSmrg			    !data[i].selfJoin && !data[j].selfJoin;
216705b261ecSmrg		if (arc)
216805b261ecSmrg		{
216905b261ecSmrg			if (arcsJoin)
217005b261ecSmrg				arc->render = 0;
217105b261ecSmrg			else
217205b261ecSmrg				arc->render = 1;
217305b261ecSmrg		}
217405b261ecSmrg		if (arcsJoin &&
217505b261ecSmrg		    (prevphase == 0 || isDoubleDash) &&
217605b261ecSmrg		    (iphase == 0 || isDoubleDash))
217705b261ecSmrg 		{
217805b261ecSmrg			joinphase = iphase;
217905b261ecSmrg			if (isDoubleDash) {
218005b261ecSmrg				if (nexti == start)
218105b261ecSmrg					joinphase = iphaseStart;
218205b261ecSmrg				/*
218305b261ecSmrg				 * if the join is right at the dash,
218405b261ecSmrg				 * draw the join in foreground
218505b261ecSmrg				 * This is because the foreground
218605b261ecSmrg				 * arcs are computed second, the results
218705b261ecSmrg				 * of which are needed to draw the join
218805b261ecSmrg				 */
218905b261ecSmrg				if (joinphase != prevphase)
219005b261ecSmrg					joinphase = 0;
219105b261ecSmrg			}
219205b261ecSmrg			if (joinphase == 0 || isDoubleDash) {
219305b261ecSmrg				addJoin (&arcs[joinphase].joins,
219405b261ecSmrg 					 &arcs[joinphase].njoins,
219505b261ecSmrg 					 &joinSize[joinphase],
219605b261ecSmrg	 				 LEFT_END, k, prevphase,
219705b261ecSmrg	 				 RIGHT_END, nextk, iphase);
219805b261ecSmrg				arc->join = arcs[prevphase].njoins;
219905b261ecSmrg			}
220005b261ecSmrg		} else {
220105b261ecSmrg			/*
220205b261ecSmrg			 * cap the left end of this arc
220305b261ecSmrg			 * unless it joins itself
220405b261ecSmrg			 */
220505b261ecSmrg			if ((prevphase == 0 || isDoubleDash) &&
220605b261ecSmrg			    !arc->selfJoin)
220705b261ecSmrg			{
220805b261ecSmrg				addCap (&arcs[prevphase].caps, &arcs[prevphase].ncaps,
220905b261ecSmrg 					&capSize[prevphase], LEFT_END, k);
221005b261ecSmrg				arc->cap = arcs[prevphase].ncaps;
221105b261ecSmrg			}
221205b261ecSmrg			if (isDashed && !arcsJoin) {
221305b261ecSmrg				iDash = iDashStart;
221405b261ecSmrg				iphase = iphaseStart;
221505b261ecSmrg				dashRemaining = dashRemainingStart;
221605b261ecSmrg			}
221705b261ecSmrg			nextk = arcs[iphase].narcs;
221805b261ecSmrg			if (nexti == start) {
221905b261ecSmrg				nextk = 0;
222005b261ecSmrg				iDash = iDashStart;
222105b261ecSmrg				iphase = iphaseStart;
222205b261ecSmrg				dashRemaining = dashRemainingStart;
222305b261ecSmrg			}
222405b261ecSmrg			/*
222505b261ecSmrg			 * cap the right end of the next arc.  If the
222605b261ecSmrg			 * next arc is actually the first arc, only
222705b261ecSmrg			 * cap it if it joins with this arc.  This
222805b261ecSmrg			 * case will occur when the final dash segment
222905b261ecSmrg			 * of an on/off dash is off.  Of course, this
223005b261ecSmrg			 * cap will be drawn at a strange time, but that
223105b261ecSmrg			 * hardly matters...
223205b261ecSmrg			 */
223305b261ecSmrg			if ((iphase == 0 || isDoubleDash) &&
223405b261ecSmrg			    (nexti != start || (arcsJoin && isDashed)))
223505b261ecSmrg				addCap (&arcs[iphase].caps, &arcs[iphase].ncaps,
223605b261ecSmrg 					&capSize[iphase], RIGHT_END, nextk);
223705b261ecSmrg		}
223805b261ecSmrg		i = nexti;
223905b261ecSmrg		if (i == start)
224005b261ecSmrg			break;
224105b261ecSmrg	}
224205b261ecSmrg	/*
224305b261ecSmrg	 * make sure the last section is rendered
224405b261ecSmrg	 */
224505b261ecSmrg	for (iphase = 0; iphase < (isDoubleDash ? 2 : 1); iphase++)
224605b261ecSmrg		if (arcs[iphase].narcs > 0) {
224705b261ecSmrg			arcs[iphase].arcs[arcs[iphase].narcs-1].render = 1;
224805b261ecSmrg			arcs[iphase].arcs[arcs[iphase].narcs-1].join =
224905b261ecSmrg			         arcs[iphase].njoins;
225005b261ecSmrg			arcs[iphase].arcs[arcs[iphase].narcs-1].cap =
225105b261ecSmrg			         arcs[iphase].ncaps;
225205b261ecSmrg		}
22534642e01fSmrg	xfree(data);
225405b261ecSmrg	return arcs;
225505b261ecSmrgarcfail:
225605b261ecSmrg	miFreeArcs(arcs, pGC);
22574642e01fSmrg	xfree(data);
225805b261ecSmrg	return (miPolyArcPtr)NULL;
225905b261ecSmrg}
226005b261ecSmrg
226105b261ecSmrgstatic double
226205b261ecSmrgangleToLength (
226305b261ecSmrg	int	angle,
226405b261ecSmrg	dashMap	*map)
226505b261ecSmrg{
226605b261ecSmrg	double	len, excesslen, sidelen = map->map[DASH_MAP_SIZE - 1], totallen;
226705b261ecSmrg	int	di;
226805b261ecSmrg	int	excess;
226905b261ecSmrg	Bool	oddSide = FALSE;
227005b261ecSmrg
227105b261ecSmrg	totallen = 0;
227205b261ecSmrg	if (angle >= 0) {
227305b261ecSmrg		while (angle >= 90 * 64) {
227405b261ecSmrg			angle -= 90 * 64;
227505b261ecSmrg			totallen += sidelen;
227605b261ecSmrg			oddSide = !oddSide;
227705b261ecSmrg		}
227805b261ecSmrg	} else {
227905b261ecSmrg		while (angle < 0) {
228005b261ecSmrg			angle += 90 * 64;
228105b261ecSmrg			totallen -= sidelen;
228205b261ecSmrg			oddSide = !oddSide;
228305b261ecSmrg		}
228405b261ecSmrg	}
228505b261ecSmrg	if (oddSide)
228605b261ecSmrg		angle = 90 * 64 - angle;
228705b261ecSmrg
228805b261ecSmrg	di = xAngleToDashIndex (angle);
228905b261ecSmrg	excess = angle - dashIndexToXAngle (di);
229005b261ecSmrg
229105b261ecSmrg	len = map->map[di];
229205b261ecSmrg	/*
229305b261ecSmrg	 * linearly interpolate between this point and the next
229405b261ecSmrg	 */
229505b261ecSmrg	if (excess > 0) {
229605b261ecSmrg		excesslen = (map->map[di + 1] - map->map[di]) *
229705b261ecSmrg				((double) excess) / dashXAngleStep;
229805b261ecSmrg		len += excesslen;
229905b261ecSmrg	}
230005b261ecSmrg	if (oddSide)
230105b261ecSmrg		totallen += (sidelen - len);
230205b261ecSmrg	else
230305b261ecSmrg		totallen += len;
230405b261ecSmrg	return totallen;
230505b261ecSmrg}
230605b261ecSmrg
230705b261ecSmrg/*
230805b261ecSmrg * len is along the arc, but may be more than one rotation
230905b261ecSmrg */
231005b261ecSmrg
231105b261ecSmrgstatic int
231205b261ecSmrglengthToAngle (
231305b261ecSmrg	double	len,
231405b261ecSmrg	dashMap	*map)
231505b261ecSmrg{
231605b261ecSmrg	double	sidelen = map->map[DASH_MAP_SIZE - 1];
231705b261ecSmrg	int	angle, angleexcess;
231805b261ecSmrg	Bool	oddSide = FALSE;
231905b261ecSmrg	int	a0, a1, a;
232005b261ecSmrg
232105b261ecSmrg	angle = 0;
232205b261ecSmrg	/*
232305b261ecSmrg	 * step around the ellipse, subtracting sidelens and
232405b261ecSmrg	 * adding 90 degrees.  oddSide will tell if the
232505b261ecSmrg	 * map should be interpolated in reverse
232605b261ecSmrg	 */
232705b261ecSmrg	if (len >= 0) {
232805b261ecSmrg		if (sidelen == 0)
232905b261ecSmrg			return 2 * FULLCIRCLE;	/* infinity */
233005b261ecSmrg		while (len >= sidelen) {
233105b261ecSmrg			angle += 90 * 64;
233205b261ecSmrg			len -= sidelen;
233305b261ecSmrg			oddSide = !oddSide;
233405b261ecSmrg		}
233505b261ecSmrg	} else {
233605b261ecSmrg		if (sidelen == 0)
233705b261ecSmrg			return -2 * FULLCIRCLE;	/* infinity */
233805b261ecSmrg		while (len < 0) {
233905b261ecSmrg			angle -= 90 * 64;
234005b261ecSmrg			len += sidelen;
234105b261ecSmrg			oddSide = !oddSide;
234205b261ecSmrg		}
234305b261ecSmrg	}
234405b261ecSmrg	if (oddSide)
234505b261ecSmrg		len = sidelen - len;
234605b261ecSmrg	a0 = 0;
234705b261ecSmrg	a1 = DASH_MAP_SIZE - 1;
234805b261ecSmrg	/*
234905b261ecSmrg	 * binary search for the closest pre-computed length
235005b261ecSmrg	 */
235105b261ecSmrg	while (a1 - a0 > 1) {
235205b261ecSmrg		a = (a0 + a1) / 2;
235305b261ecSmrg		if (len > map->map[a])
235405b261ecSmrg			a0 = a;
235505b261ecSmrg		else
235605b261ecSmrg			a1 = a;
235705b261ecSmrg	}
235805b261ecSmrg	angleexcess = dashIndexToXAngle (a0);
235905b261ecSmrg	/*
236005b261ecSmrg	 * linearly interpolate to the next point
236105b261ecSmrg	 */
236205b261ecSmrg	angleexcess += (len - map->map[a0]) /
236305b261ecSmrg			(map->map[a0+1] - map->map[a0]) * dashXAngleStep;
236405b261ecSmrg	if (oddSide)
236505b261ecSmrg		angle += (90 * 64) - angleexcess;
236605b261ecSmrg	else
236705b261ecSmrg		angle += angleexcess;
236805b261ecSmrg	return angle;
236905b261ecSmrg}
237005b261ecSmrg
237105b261ecSmrg/*
237205b261ecSmrg * compute the angle of an ellipse which cooresponds to
237305b261ecSmrg * the given path length.  Note that the correct solution
237405b261ecSmrg * to this problem is an eliptic integral, we'll punt and
237505b261ecSmrg * approximate (it's only for dashes anyway).  This
237605b261ecSmrg * approximation uses a polygon.
237705b261ecSmrg *
237805b261ecSmrg * The remaining portion of len is stored in *lenp -
237905b261ecSmrg * this will be negative if the arc extends beyond
238005b261ecSmrg * len and positive if len extends beyond the arc.
238105b261ecSmrg */
238205b261ecSmrg
238305b261ecSmrgstatic int
238405b261ecSmrgcomputeAngleFromPath (
238505b261ecSmrg	int	startAngle,
238605b261ecSmrg	int	endAngle,	/* normalized absolute angles in *64 degrees */
238705b261ecSmrg	dashMap	*map,
238805b261ecSmrg	int	*lenp,
238905b261ecSmrg	int	backwards)
239005b261ecSmrg{
239105b261ecSmrg	int	a0, a1, a;
239205b261ecSmrg	double	len0;
239305b261ecSmrg	int	len;
239405b261ecSmrg
239505b261ecSmrg	a0 = startAngle;
239605b261ecSmrg	a1 = endAngle;
239705b261ecSmrg	len = *lenp;
239805b261ecSmrg	if (backwards) {
239905b261ecSmrg		/*
240005b261ecSmrg		 * flip the problem around to always be
240105b261ecSmrg		 * forwards
240205b261ecSmrg		 */
240305b261ecSmrg		a0 = FULLCIRCLE - a0;
240405b261ecSmrg		a1 = FULLCIRCLE - a1;
240505b261ecSmrg	}
240605b261ecSmrg	if (a1 < a0)
240705b261ecSmrg		a1 += FULLCIRCLE;
240805b261ecSmrg	len0 = angleToLength (a0, map);
240905b261ecSmrg	a = lengthToAngle (len0 + len, map);
241005b261ecSmrg	if (a > a1) {
241105b261ecSmrg		a = a1;
241205b261ecSmrg		len -= angleToLength (a1, map) - len0;
241305b261ecSmrg	} else
241405b261ecSmrg		len = 0;
241505b261ecSmrg	if (backwards)
241605b261ecSmrg		a = FULLCIRCLE - a;
241705b261ecSmrg	*lenp = len;
241805b261ecSmrg	return a;
241905b261ecSmrg}
242005b261ecSmrg
242105b261ecSmrg/*
242205b261ecSmrg * scan convert wide arcs.
242305b261ecSmrg */
242405b261ecSmrg
242505b261ecSmrg/*
242605b261ecSmrg * draw zero width/height arcs
242705b261ecSmrg */
242805b261ecSmrg
242905b261ecSmrgstatic void
243005b261ecSmrgdrawZeroArc (
243105b261ecSmrg    DrawablePtr   pDraw,
243205b261ecSmrg    GCPtr         pGC,
243305b261ecSmrg    xArc          *tarc,
243405b261ecSmrg    int		  lw,
243505b261ecSmrg    miArcFacePtr	left,
243605b261ecSmrg    miArcFacePtr	right)
243705b261ecSmrg{
243805b261ecSmrg	double	x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0, w, h, x, y;
243905b261ecSmrg	double	xmax, ymax, xmin, ymin;
244005b261ecSmrg	int	a0, a1;
244105b261ecSmrg	double	a, startAngle, endAngle;
244205b261ecSmrg	double	l, lx, ly;
244305b261ecSmrg
244405b261ecSmrg	l = lw / 2.0;
244505b261ecSmrg	a0 = tarc->angle1;
244605b261ecSmrg	a1 = tarc->angle2;
244705b261ecSmrg	if (a1 > FULLCIRCLE)
244805b261ecSmrg		a1 = FULLCIRCLE;
244905b261ecSmrg	else if (a1 < -FULLCIRCLE)
245005b261ecSmrg		a1 = -FULLCIRCLE;
245105b261ecSmrg	w = (double)tarc->width / 2.0;
245205b261ecSmrg	h = (double)tarc->height / 2.0;
245305b261ecSmrg	/*
245405b261ecSmrg	 * play in X coordinates right away
245505b261ecSmrg	 */
245605b261ecSmrg	startAngle = - ((double) a0 / 64.0);
245705b261ecSmrg	endAngle = - ((double) (a0 + a1) / 64.0);
245805b261ecSmrg
245905b261ecSmrg	xmax = -w;
246005b261ecSmrg	xmin = w;
246105b261ecSmrg	ymax = -h;
246205b261ecSmrg	ymin = h;
246305b261ecSmrg	a = startAngle;
246405b261ecSmrg	for (;;)
246505b261ecSmrg	{
246605b261ecSmrg		x = w * miDcos(a);
246705b261ecSmrg		y = h * miDsin(a);
246805b261ecSmrg		if (a == startAngle)
246905b261ecSmrg		{
247005b261ecSmrg			x0 = x;
247105b261ecSmrg			y0 = y;
247205b261ecSmrg		}
247305b261ecSmrg		if (a == endAngle)
247405b261ecSmrg		{
247505b261ecSmrg			x1 = x;
247605b261ecSmrg			y1 = y;
247705b261ecSmrg		}
247805b261ecSmrg		if (x > xmax)
247905b261ecSmrg			xmax = x;
248005b261ecSmrg		if (x < xmin)
248105b261ecSmrg			xmin = x;
248205b261ecSmrg		if (y > ymax)
248305b261ecSmrg			ymax = y;
248405b261ecSmrg		if (y < ymin)
248505b261ecSmrg			ymin = y;
248605b261ecSmrg		if (a == endAngle)
248705b261ecSmrg			break;
248805b261ecSmrg		if (a1 < 0)	/* clockwise */
248905b261ecSmrg		{
249005b261ecSmrg			if (floor (a / 90.0) == floor (endAngle / 90.0))
249105b261ecSmrg				a = endAngle;
249205b261ecSmrg			else
249305b261ecSmrg				a = 90 * (floor (a/90.0) + 1);
249405b261ecSmrg		}
249505b261ecSmrg		else
249605b261ecSmrg		{
249705b261ecSmrg			if (ceil (a / 90.0) == ceil (endAngle / 90.0))
249805b261ecSmrg				a = endAngle;
249905b261ecSmrg			else
250005b261ecSmrg				a = 90 * (ceil (a/90.0) - 1);
250105b261ecSmrg		}
250205b261ecSmrg	}
250305b261ecSmrg	lx = ly = l;
250405b261ecSmrg	if ((x1 - x0) + (y1 - y0) < 0)
250505b261ecSmrg	    lx = ly = -l;
250605b261ecSmrg	if (h)
250705b261ecSmrg	{
250805b261ecSmrg	    ly = 0.0;
250905b261ecSmrg	    lx = -lx;
251005b261ecSmrg	}
251105b261ecSmrg	else
251205b261ecSmrg	    lx = 0.0;
251305b261ecSmrg	if (right)
251405b261ecSmrg	{
251505b261ecSmrg	    right->center.x = x0;
251605b261ecSmrg	    right->center.y = y0;
251705b261ecSmrg	    right->clock.x = x0 - lx;
251805b261ecSmrg	    right->clock.y = y0 - ly;
251905b261ecSmrg	    right->counterClock.x = x0 + lx;
252005b261ecSmrg	    right->counterClock.y = y0 + ly;
252105b261ecSmrg	}
252205b261ecSmrg	if (left)
252305b261ecSmrg 	{
252405b261ecSmrg	    left->center.x = x1;
252505b261ecSmrg	    left->center.y = y1;
252605b261ecSmrg	    left->clock.x = x1 + lx;
252705b261ecSmrg	    left->clock.y = y1 + ly;
252805b261ecSmrg	    left->counterClock.x = x1 - lx;
252905b261ecSmrg	    left->counterClock.y = y1 - ly;
253005b261ecSmrg	}
253105b261ecSmrg
253205b261ecSmrg	x0 = xmin;
253305b261ecSmrg	x1 = xmax;
253405b261ecSmrg	y0 = ymin;
253505b261ecSmrg	y1 = ymax;
253605b261ecSmrg	if (ymin != y1) {
253705b261ecSmrg		xmin = -l;
253805b261ecSmrg		xmax = l;
253905b261ecSmrg	} else {
254005b261ecSmrg		ymin = -l;
254105b261ecSmrg		ymax = l;
254205b261ecSmrg	}
254305b261ecSmrg	if (xmax != xmin && ymax != ymin) {
254405b261ecSmrg		int	minx, maxx, miny, maxy;
254505b261ecSmrg		xRectangle  rect;
254605b261ecSmrg
254705b261ecSmrg		minx = ICEIL (xmin + w) + tarc->x;
254805b261ecSmrg		maxx = ICEIL (xmax + w) + tarc->x;
254905b261ecSmrg		miny = ICEIL (ymin + h) + tarc->y;
255005b261ecSmrg		maxy = ICEIL (ymax + h) + tarc->y;
255105b261ecSmrg		rect.x = minx;
255205b261ecSmrg		rect.y = miny;
255305b261ecSmrg		rect.width = maxx - minx;
255405b261ecSmrg		rect.height = maxy - miny;
255505b261ecSmrg		(*pGC->ops->PolyFillRect) (pDraw, pGC, 1, &rect);
255605b261ecSmrg	}
255705b261ecSmrg}
255805b261ecSmrg
255905b261ecSmrg/*
256005b261ecSmrg * this computes the ellipse y value associated with the
256105b261ecSmrg * bottom of the tail.
256205b261ecSmrg */
256305b261ecSmrg
256405b261ecSmrgstatic void
256505b261ecSmrgtailEllipseY (
256605b261ecSmrg	struct arc_def		*def,
256705b261ecSmrg	struct accelerators	*acc)
256805b261ecSmrg{
256905b261ecSmrg	double		t;
257005b261ecSmrg
257105b261ecSmrg	acc->tail_y = 0.0;
257205b261ecSmrg	if (def->w == def->h)
257305b261ecSmrg	    return;
257405b261ecSmrg	t = def->l * def->w;
257505b261ecSmrg	if (def->w > def->h) {
257605b261ecSmrg	    if (t < acc->h2)
257705b261ecSmrg		return;
257805b261ecSmrg	} else {
257905b261ecSmrg	    if (t > acc->h2)
258005b261ecSmrg		return;
258105b261ecSmrg	}
258205b261ecSmrg	t = 2.0 * def->h * t;
258305b261ecSmrg	t = (CUBED_ROOT_4 * acc->h2 - cbrt(t * t)) / acc->h2mw2;
258405b261ecSmrg	if (t > 0.0)
258505b261ecSmrg	    acc->tail_y = def->h / CUBED_ROOT_2 * sqrt(t);
258605b261ecSmrg}
258705b261ecSmrg
258805b261ecSmrg/*
258905b261ecSmrg * inverse functions -- compute edge coordinates
259005b261ecSmrg * from the ellipse
259105b261ecSmrg */
259205b261ecSmrg
259305b261ecSmrgstatic double
259405b261ecSmrgouterXfromXY (
259505b261ecSmrg	double			x,
259605b261ecSmrg	double			y,
259705b261ecSmrg	struct arc_def		*def,
259805b261ecSmrg	struct accelerators	*acc)
259905b261ecSmrg{
260005b261ecSmrg	return x + (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
260105b261ecSmrg}
260205b261ecSmrg
260305b261ecSmrgstatic double
260405b261ecSmrgouterYfromXY (
260505b261ecSmrg	double		x,
260605b261ecSmrg	double		y,
260705b261ecSmrg	struct arc_def		*def,
260805b261ecSmrg	struct accelerators	*acc)
260905b261ecSmrg{
261005b261ecSmrg	return y + (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
261105b261ecSmrg}
261205b261ecSmrg
261305b261ecSmrgstatic double
261405b261ecSmrginnerXfromXY (
261505b261ecSmrg	double			x,
261605b261ecSmrg	double			y,
261705b261ecSmrg	struct arc_def		*def,
261805b261ecSmrg	struct accelerators	*acc)
261905b261ecSmrg{
262005b261ecSmrg	return x - (x * acc->h2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
262105b261ecSmrg}
262205b261ecSmrg
262305b261ecSmrgstatic double
262405b261ecSmrginnerYfromXY (
262505b261ecSmrg	double			x,
262605b261ecSmrg	double			y,
262705b261ecSmrg	struct arc_def		*def,
262805b261ecSmrg	struct accelerators	*acc)
262905b261ecSmrg{
263005b261ecSmrg	return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
263105b261ecSmrg}
263205b261ecSmrg
263305b261ecSmrgstatic double
263405b261ecSmrginnerYfromY (
263505b261ecSmrg	double	y,
263605b261ecSmrg	struct arc_def		*def,
263705b261ecSmrg	struct accelerators	*acc)
263805b261ecSmrg{
263905b261ecSmrg	double	x;
264005b261ecSmrg
264105b261ecSmrg	x = (def->w / def->h) * sqrt (acc->h2 - y*y);
264205b261ecSmrg
264305b261ecSmrg	return y - (y * acc->w2l) / sqrt (x*x * acc->h4 + y*y * acc->w4);
264405b261ecSmrg}
264505b261ecSmrg
264605b261ecSmrgstatic void
264705b261ecSmrgcomputeLine (
264805b261ecSmrg	double		x1,
264905b261ecSmrg	double		y1,
265005b261ecSmrg	double		x2,
265105b261ecSmrg	double		y2,
265205b261ecSmrg	struct line	*line)
265305b261ecSmrg{
265405b261ecSmrg	if (y1 == y2)
265505b261ecSmrg		line->valid = 0;
265605b261ecSmrg	else {
265705b261ecSmrg		line->m = (x1 - x2) / (y1 - y2);
265805b261ecSmrg		line->b = x1  - y1 * line->m;
265905b261ecSmrg		line->valid = 1;
266005b261ecSmrg	}
266105b261ecSmrg}
266205b261ecSmrg
266305b261ecSmrg/*
266405b261ecSmrg * compute various accelerators for an ellipse.  These
266505b261ecSmrg * are simply values that are used repeatedly in
266605b261ecSmrg * the computations
266705b261ecSmrg */
266805b261ecSmrg
266905b261ecSmrgstatic void
267005b261ecSmrgcomputeAcc (
267105b261ecSmrg	xArc			*tarc,
267205b261ecSmrg	int			lw,
267305b261ecSmrg	struct arc_def		*def,
267405b261ecSmrg	struct accelerators	*acc)
267505b261ecSmrg{
267605b261ecSmrg	def->w = ((double) tarc->width) / 2.0;
267705b261ecSmrg	def->h = ((double) tarc->height) / 2.0;
267805b261ecSmrg	def->l = ((double) lw) / 2.0;
267905b261ecSmrg	acc->h2 = def->h * def->h;
268005b261ecSmrg	acc->w2 = def->w * def->w;
268105b261ecSmrg	acc->h4 = acc->h2 * acc->h2;
268205b261ecSmrg	acc->w4 = acc->w2 * acc->w2;
268305b261ecSmrg	acc->h2l = acc->h2 * def->l;
268405b261ecSmrg	acc->w2l = acc->w2 * def->l;
268505b261ecSmrg	acc->h2mw2 = acc->h2 - acc->w2;
268605b261ecSmrg	acc->fromIntX = (tarc->width & 1) ? 0.5 : 0.0;
268705b261ecSmrg	acc->fromIntY = (tarc->height & 1) ? 0.5 : 0.0;
268805b261ecSmrg	acc->xorg = tarc->x + (tarc->width >> 1);
268905b261ecSmrg	acc->yorgu = tarc->y + (tarc->height >> 1);
269005b261ecSmrg	acc->yorgl = acc->yorgu + (tarc->height & 1);
269105b261ecSmrg	tailEllipseY (def, acc);
269205b261ecSmrg}
269305b261ecSmrg
269405b261ecSmrg/*
269505b261ecSmrg * compute y value bounds of various portions of the arc,
269605b261ecSmrg * the outer edge, the ellipse and the inner edge.
269705b261ecSmrg */
269805b261ecSmrg
269905b261ecSmrgstatic void
270005b261ecSmrgcomputeBound (
270105b261ecSmrg	struct arc_def		*def,
270205b261ecSmrg	struct arc_bound	*bound,
270305b261ecSmrg	struct accelerators	*acc,
270405b261ecSmrg	miArcFacePtr		right,
270505b261ecSmrg	miArcFacePtr		left)
270605b261ecSmrg{
270705b261ecSmrg	double		t;
270805b261ecSmrg	double		innerTaily;
270905b261ecSmrg	double		tail_y;
271005b261ecSmrg	struct bound	innerx, outerx;
271105b261ecSmrg	struct bound	ellipsex;
271205b261ecSmrg
271305b261ecSmrg	bound->ellipse.min = Dsin (def->a0) * def->h;
271405b261ecSmrg	bound->ellipse.max = Dsin (def->a1) * def->h;
271505b261ecSmrg	if (def->a0 == 45 && def->w == def->h)
271605b261ecSmrg		ellipsex.min = bound->ellipse.min;
271705b261ecSmrg	else
271805b261ecSmrg		ellipsex.min = Dcos (def->a0) * def->w;
271905b261ecSmrg	if (def->a1 == 45 && def->w == def->h)
272005b261ecSmrg		ellipsex.max = bound->ellipse.max;
272105b261ecSmrg	else
272205b261ecSmrg		ellipsex.max = Dcos (def->a1) * def->w;
272305b261ecSmrg	bound->outer.min = outerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
272405b261ecSmrg	bound->outer.max = outerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
272505b261ecSmrg	bound->inner.min = innerYfromXY (ellipsex.min, bound->ellipse.min, def, acc);
272605b261ecSmrg	bound->inner.max = innerYfromXY (ellipsex.max, bound->ellipse.max, def, acc);
272705b261ecSmrg
272805b261ecSmrg	outerx.min = outerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
272905b261ecSmrg	outerx.max = outerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
273005b261ecSmrg	innerx.min = innerXfromXY (ellipsex.min, bound->ellipse.min, def, acc);
273105b261ecSmrg	innerx.max = innerXfromXY (ellipsex.max, bound->ellipse.max, def, acc);
273205b261ecSmrg
273305b261ecSmrg	/*
273405b261ecSmrg	 * save the line end points for the
273505b261ecSmrg	 * cap code to use.  Careful here, these are
273605b261ecSmrg	 * in cartesean coordinates (y increasing upwards)
273705b261ecSmrg	 * while the cap code uses inverted coordinates
273805b261ecSmrg	 * (y increasing downwards)
273905b261ecSmrg	 */
274005b261ecSmrg
274105b261ecSmrg	if (right) {
274205b261ecSmrg		right->counterClock.y = bound->outer.min;
274305b261ecSmrg		right->counterClock.x = outerx.min;
274405b261ecSmrg		right->center.y = bound->ellipse.min;
274505b261ecSmrg		right->center.x = ellipsex.min;
274605b261ecSmrg		right->clock.y = bound->inner.min;
274705b261ecSmrg		right->clock.x = innerx.min;
274805b261ecSmrg	}
274905b261ecSmrg
275005b261ecSmrg	if (left) {
275105b261ecSmrg		left->clock.y = bound->outer.max;
275205b261ecSmrg		left->clock.x = outerx.max;
275305b261ecSmrg		left->center.y = bound->ellipse.max;
275405b261ecSmrg		left->center.x = ellipsex.max;
275505b261ecSmrg		left->counterClock.y = bound->inner.max;
275605b261ecSmrg		left->counterClock.x = innerx.max;
275705b261ecSmrg	}
275805b261ecSmrg
275905b261ecSmrg	bound->left.min = bound->inner.max;
276005b261ecSmrg	bound->left.max = bound->outer.max;
276105b261ecSmrg	bound->right.min = bound->inner.min;
276205b261ecSmrg	bound->right.max = bound->outer.min;
276305b261ecSmrg
276405b261ecSmrg	computeLine (innerx.min, bound->inner.min, outerx.min, bound->outer.min,
276505b261ecSmrg		      &acc->right);
276605b261ecSmrg	computeLine (innerx.max, bound->inner.max, outerx.max, bound->outer.max,
276705b261ecSmrg		     &acc->left);
276805b261ecSmrg
276905b261ecSmrg	if (bound->inner.min > bound->inner.max) {
277005b261ecSmrg		t = bound->inner.min;
277105b261ecSmrg		bound->inner.min = bound->inner.max;
277205b261ecSmrg		bound->inner.max = t;
277305b261ecSmrg	}
277405b261ecSmrg	tail_y = acc->tail_y;
277505b261ecSmrg	if (tail_y > bound->ellipse.max)
277605b261ecSmrg		tail_y = bound->ellipse.max;
277705b261ecSmrg	else if (tail_y < bound->ellipse.min)
277805b261ecSmrg		tail_y = bound->ellipse.min;
277905b261ecSmrg	innerTaily = innerYfromY (tail_y, def, acc);
278005b261ecSmrg	if (bound->inner.min > innerTaily)
278105b261ecSmrg		bound->inner.min = innerTaily;
278205b261ecSmrg	if (bound->inner.max < innerTaily)
278305b261ecSmrg		bound->inner.max = innerTaily;
278405b261ecSmrg	bound->inneri.min = ICEIL(bound->inner.min - acc->fromIntY);
278505b261ecSmrg	bound->inneri.max = floor(bound->inner.max - acc->fromIntY);
278605b261ecSmrg	bound->outeri.min = ICEIL(bound->outer.min - acc->fromIntY);
278705b261ecSmrg	bound->outeri.max = floor(bound->outer.max - acc->fromIntY);
278805b261ecSmrg}
278905b261ecSmrg
279005b261ecSmrg/*
279105b261ecSmrg * this section computes the x value of the span at y
279205b261ecSmrg * intersected with the specified face of the ellipse.
279305b261ecSmrg *
279405b261ecSmrg * this is the min/max X value over the set of normal
279505b261ecSmrg * lines to the entire ellipse,  the equation of the
279605b261ecSmrg * normal lines is:
279705b261ecSmrg *
279805b261ecSmrg *     ellipse_x h^2                   h^2
279905b261ecSmrg * x = ------------ y + ellipse_x (1 - --- )
280005b261ecSmrg *     ellipse_y w^2                   w^2
280105b261ecSmrg *
280205b261ecSmrg * compute the derivative with-respect-to ellipse_y and solve
280305b261ecSmrg * for zero:
280405b261ecSmrg *
280505b261ecSmrg *       (w^2 - h^2) ellipse_y^3 + h^4 y
280605b261ecSmrg * 0 = - ----------------------------------
280705b261ecSmrg *       h w ellipse_y^2 sqrt (h^2 - ellipse_y^2)
280805b261ecSmrg *
280905b261ecSmrg *             (   h^4 y     )
281005b261ecSmrg * ellipse_y = ( ----------  ) ^ (1/3)
281105b261ecSmrg *             ( (h^2 - w^2) )
281205b261ecSmrg *
281305b261ecSmrg * The other two solutions to the equation are imaginary.
281405b261ecSmrg *
281505b261ecSmrg * This gives the position on the ellipse which generates
281605b261ecSmrg * the normal with the largest/smallest x intersection point.
281705b261ecSmrg *
281805b261ecSmrg * Now compute the second derivative to check whether
281905b261ecSmrg * the intersection is a minimum or maximum:
282005b261ecSmrg *
282105b261ecSmrg *    h (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
282205b261ecSmrg * -  -------------------------------------------
282305b261ecSmrg *          w y0^3 (sqrt (h^2 - y^2)) ^ 3
282405b261ecSmrg *
282505b261ecSmrg * as we only care about the sign,
282605b261ecSmrg *
282705b261ecSmrg * - (y0^3 (w^2 - h^2) + h^2 y (3y0^2 - 2h^2))
282805b261ecSmrg *
282905b261ecSmrg * or (to use accelerators),
283005b261ecSmrg *
283105b261ecSmrg * y0^3 (h^2 - w^2) - h^2 y (3y0^2 - 2h^2)
283205b261ecSmrg *
283305b261ecSmrg */
283405b261ecSmrg
283505b261ecSmrg/*
283605b261ecSmrg * computes the position on the ellipse whose normal line
283705b261ecSmrg * intersects the given scan line maximally
283805b261ecSmrg */
283905b261ecSmrg
284005b261ecSmrgstatic double
284105b261ecSmrghookEllipseY (
284205b261ecSmrg	double			scan_y,
284305b261ecSmrg	struct arc_bound	*bound,
284405b261ecSmrg	struct accelerators	*acc,
284505b261ecSmrg	int			left)
284605b261ecSmrg{
284705b261ecSmrg	double	ret;
284805b261ecSmrg
284905b261ecSmrg	if (acc->h2mw2 == 0) {
285005b261ecSmrg		if ( (scan_y > 0 && !left) || (scan_y < 0 && left) )
285105b261ecSmrg			return bound->ellipse.min;
285205b261ecSmrg		return bound->ellipse.max;
285305b261ecSmrg	}
285405b261ecSmrg	ret = (acc->h4 * scan_y) / (acc->h2mw2);
285505b261ecSmrg	if (ret >= 0)
285605b261ecSmrg		return cbrt (ret);
285705b261ecSmrg	else
285805b261ecSmrg		return -cbrt (-ret);
285905b261ecSmrg}
286005b261ecSmrg
286105b261ecSmrg/*
286205b261ecSmrg * computes the X value of the intersection of the
286305b261ecSmrg * given scan line with the right side of the lower hook
286405b261ecSmrg */
286505b261ecSmrg
286605b261ecSmrgstatic double
286705b261ecSmrghookX (
286805b261ecSmrg	double			scan_y,
286905b261ecSmrg	struct arc_def		*def,
287005b261ecSmrg	struct arc_bound	*bound,
287105b261ecSmrg	struct accelerators	*acc,
287205b261ecSmrg	int			left)
287305b261ecSmrg{
287405b261ecSmrg	double	ellipse_y, x;
287505b261ecSmrg	double	maxMin;
287605b261ecSmrg
287705b261ecSmrg	if (def->w != def->h) {
287805b261ecSmrg		ellipse_y = hookEllipseY (scan_y, bound, acc, left);
287905b261ecSmrg		if (boundedLe (ellipse_y, bound->ellipse)) {
288005b261ecSmrg			/*
288105b261ecSmrg		 	 * compute the value of the second
288205b261ecSmrg		 	 * derivative
288305b261ecSmrg		 	 */
288405b261ecSmrg			maxMin = ellipse_y*ellipse_y*ellipse_y * acc->h2mw2 -
288505b261ecSmrg		 	 acc->h2 * scan_y * (3 * ellipse_y*ellipse_y - 2*acc->h2);
288605b261ecSmrg			if ((left && maxMin > 0) || (!left && maxMin < 0)) {
288705b261ecSmrg				if (ellipse_y == 0)
288805b261ecSmrg					return def->w + left ? -def->l : def->l;
288905b261ecSmrg				x = (acc->h2 * scan_y - ellipse_y * acc->h2mw2) *
289005b261ecSmrg					sqrt (acc->h2 - ellipse_y * ellipse_y) /
289105b261ecSmrg			 		(def->h * def->w * ellipse_y);
289205b261ecSmrg				return x;
289305b261ecSmrg			}
289405b261ecSmrg		}
289505b261ecSmrg	}
289605b261ecSmrg	if (left) {
289705b261ecSmrg		if (acc->left.valid && boundedLe (scan_y, bound->left)) {
289805b261ecSmrg			x = intersectLine (scan_y, acc->left);
289905b261ecSmrg		} else {
290005b261ecSmrg			if (acc->right.valid)
290105b261ecSmrg				x = intersectLine (scan_y, acc->right);
290205b261ecSmrg			else
290305b261ecSmrg				x = def->w - def->l;
290405b261ecSmrg		}
290505b261ecSmrg	} else {
290605b261ecSmrg		if (acc->right.valid && boundedLe (scan_y, bound->right)) {
290705b261ecSmrg			x = intersectLine (scan_y, acc->right);
290805b261ecSmrg		} else {
290905b261ecSmrg			if (acc->left.valid)
291005b261ecSmrg				x = intersectLine (scan_y, acc->left);
291105b261ecSmrg			else
291205b261ecSmrg				x = def->w - def->l;
291305b261ecSmrg		}
291405b261ecSmrg	}
291505b261ecSmrg	return x;
291605b261ecSmrg}
291705b261ecSmrg
291805b261ecSmrg/*
291905b261ecSmrg * generate the set of spans with
292005b261ecSmrg * the given y coordinate
292105b261ecSmrg */
292205b261ecSmrg
292305b261ecSmrgstatic void
292405b261ecSmrgarcSpan (
292505b261ecSmrg	int			y,
292605b261ecSmrg	int			lx,
292705b261ecSmrg	int			lw,
292805b261ecSmrg	int			rx,
292905b261ecSmrg	int			rw,
293005b261ecSmrg	struct arc_def		*def,
293105b261ecSmrg	struct arc_bound	*bounds,
293205b261ecSmrg	struct accelerators	*acc,
293305b261ecSmrg	int			mask)
293405b261ecSmrg{
293505b261ecSmrg	int linx, loutx, rinx, routx;
293605b261ecSmrg	double x, altx;
293705b261ecSmrg
293805b261ecSmrg	if (boundedLe (y, bounds->inneri)) {
293905b261ecSmrg	    linx = -(lx + lw);
294005b261ecSmrg	    rinx = rx;
294105b261ecSmrg	} else {
294205b261ecSmrg	    /*
294305b261ecSmrg	     * intersection with left face
294405b261ecSmrg	     */
294505b261ecSmrg	    x = hookX (y + acc->fromIntY, def, bounds, acc, 1);
294605b261ecSmrg	    if (acc->right.valid &&
294705b261ecSmrg		boundedLe (y + acc->fromIntY, bounds->right))
294805b261ecSmrg	    {
294905b261ecSmrg		altx = intersectLine (y + acc->fromIntY, acc->right);
295005b261ecSmrg		if (altx < x)
295105b261ecSmrg		    x = altx;
295205b261ecSmrg	    }
295305b261ecSmrg	    linx = -ICEIL(acc->fromIntX - x);
295405b261ecSmrg	    rinx = ICEIL(acc->fromIntX + x);
295505b261ecSmrg	}
295605b261ecSmrg	if (boundedLe (y, bounds->outeri)) {
295705b261ecSmrg	    loutx = -lx;
295805b261ecSmrg	    routx = rx + rw;
295905b261ecSmrg	} else {
296005b261ecSmrg	    /*
296105b261ecSmrg	     * intersection with right face
296205b261ecSmrg	     */
296305b261ecSmrg	    x = hookX (y + acc->fromIntY, def, bounds, acc, 0);
296405b261ecSmrg	    if (acc->left.valid &&
296505b261ecSmrg		boundedLe (y + acc->fromIntY, bounds->left))
296605b261ecSmrg	    {
296705b261ecSmrg		altx = x;
296805b261ecSmrg		x = intersectLine (y + acc->fromIntY, acc->left);
296905b261ecSmrg		if (x < altx)
297005b261ecSmrg		    x = altx;
297105b261ecSmrg	    }
297205b261ecSmrg	    loutx = -ICEIL(acc->fromIntX - x);
297305b261ecSmrg	    routx = ICEIL(acc->fromIntX + x);
297405b261ecSmrg	}
297505b261ecSmrg	if (routx > rinx) {
297605b261ecSmrg	    if (mask & 1)
297705b261ecSmrg		newFinalSpan (acc->yorgu - y,
297805b261ecSmrg			      acc->xorg + rinx, acc->xorg + routx);
297905b261ecSmrg	    if (mask & 8)
298005b261ecSmrg		newFinalSpan (acc->yorgl + y,
298105b261ecSmrg			      acc->xorg + rinx, acc->xorg + routx);
298205b261ecSmrg	}
298305b261ecSmrg	if (loutx > linx) {
298405b261ecSmrg	    if (mask & 2)
298505b261ecSmrg		newFinalSpan (acc->yorgu - y,
298605b261ecSmrg			      acc->xorg - loutx, acc->xorg - linx);
298705b261ecSmrg	    if (mask & 4)
298805b261ecSmrg		newFinalSpan (acc->yorgl + y,
298905b261ecSmrg			      acc->xorg - loutx, acc->xorg - linx);
299005b261ecSmrg	}
299105b261ecSmrg}
299205b261ecSmrg
299305b261ecSmrgstatic void
299405b261ecSmrgarcSpan0 (
299505b261ecSmrg	int			lx,
299605b261ecSmrg	int			lw,
299705b261ecSmrg	int			rx,
299805b261ecSmrg	int			rw,
299905b261ecSmrg	struct arc_def		*def,
300005b261ecSmrg	struct arc_bound	*bounds,
300105b261ecSmrg	struct accelerators	*acc,
300205b261ecSmrg	int			mask)
300305b261ecSmrg{
300405b261ecSmrg    double x;
300505b261ecSmrg
300605b261ecSmrg    if (boundedLe (0, bounds->inneri) &&
300705b261ecSmrg	acc->left.valid && boundedLe (0, bounds->left) &&
300805b261ecSmrg	acc->left.b > 0)
300905b261ecSmrg    {
301005b261ecSmrg	x = def->w - def->l;
301105b261ecSmrg	if (acc->left.b < x)
301205b261ecSmrg	    x = acc->left.b;
301305b261ecSmrg	lw = ICEIL(acc->fromIntX - x) - lx;
301405b261ecSmrg	rw += rx;
301505b261ecSmrg	rx = ICEIL(acc->fromIntX + x);
301605b261ecSmrg	rw -= rx;
301705b261ecSmrg    }
301805b261ecSmrg    arcSpan (0, lx, lw, rx, rw, def, bounds, acc, mask);
301905b261ecSmrg}
302005b261ecSmrg
302105b261ecSmrgstatic void
302205b261ecSmrgtailSpan (
302305b261ecSmrg	int			y,
302405b261ecSmrg	int			lw,
302505b261ecSmrg	int			rw,
302605b261ecSmrg	struct arc_def		*def,
302705b261ecSmrg	struct arc_bound	*bounds,
302805b261ecSmrg	struct accelerators	*acc,
302905b261ecSmrg	int			mask)
303005b261ecSmrg{
303105b261ecSmrg    double yy, xalt, x, lx, rx;
303205b261ecSmrg    int n;
303305b261ecSmrg
303405b261ecSmrg    if (boundedLe(y, bounds->outeri))
303505b261ecSmrg	arcSpan (y, 0, lw, -rw, rw, def, bounds, acc, mask);
303605b261ecSmrg    else if (def->w != def->h) {
303705b261ecSmrg	yy = y + acc->fromIntY;
303805b261ecSmrg	x = tailX(yy, def, bounds, acc);
303905b261ecSmrg	if (yy == 0.0 && x == -rw - acc->fromIntX)
304005b261ecSmrg	    return;
304105b261ecSmrg	if (acc->right.valid && boundedLe (yy, bounds->right)) {
304205b261ecSmrg	    rx = x;
304305b261ecSmrg	    lx = -x;
304405b261ecSmrg	    xalt = intersectLine (yy, acc->right);
304505b261ecSmrg	    if (xalt >= -rw - acc->fromIntX && xalt <= rx)
304605b261ecSmrg		rx = xalt;
304705b261ecSmrg	    n = ICEIL(acc->fromIntX + lx);
304805b261ecSmrg	    if (lw > n) {
304905b261ecSmrg		if (mask & 2)
305005b261ecSmrg		    newFinalSpan (acc->yorgu - y,
305105b261ecSmrg				  acc->xorg + n, acc->xorg + lw);
305205b261ecSmrg		if (mask & 4)
305305b261ecSmrg		    newFinalSpan (acc->yorgl + y,
305405b261ecSmrg				  acc->xorg + n, acc->xorg + lw);
305505b261ecSmrg	    }
305605b261ecSmrg	    n = ICEIL(acc->fromIntX + rx);
305705b261ecSmrg	    if (n > -rw) {
305805b261ecSmrg		if (mask & 1)
305905b261ecSmrg		    newFinalSpan (acc->yorgu - y,
306005b261ecSmrg				  acc->xorg - rw, acc->xorg + n);
306105b261ecSmrg		if (mask & 8)
306205b261ecSmrg		    newFinalSpan (acc->yorgl + y,
306305b261ecSmrg				  acc->xorg - rw, acc->xorg + n);
306405b261ecSmrg	    }
306505b261ecSmrg	}
306605b261ecSmrg	arcSpan (y,
306705b261ecSmrg		 ICEIL(acc->fromIntX - x), 0,
306805b261ecSmrg		 ICEIL(acc->fromIntX + x), 0,
306905b261ecSmrg		 def, bounds, acc, mask);
307005b261ecSmrg    }
307105b261ecSmrg}
307205b261ecSmrg
307305b261ecSmrg/*
307405b261ecSmrg * create whole arcs out of pieces.  This code is
307505b261ecSmrg * very bad.
307605b261ecSmrg */
307705b261ecSmrg
307805b261ecSmrgstatic struct finalSpan	**finalSpans = NULL;
307905b261ecSmrgstatic int		finalMiny = 0, finalMaxy = -1;
308005b261ecSmrgstatic int		finalSize = 0;
308105b261ecSmrg
308205b261ecSmrgstatic int		nspans = 0;	/* total spans, not just y coords */
308305b261ecSmrg
308405b261ecSmrgstruct finalSpan {
308505b261ecSmrg	struct finalSpan	*next;
308605b261ecSmrg	int			min, max;	/* x values */
308705b261ecSmrg};
308805b261ecSmrg
308905b261ecSmrgstatic struct finalSpan    *freeFinalSpans, *tmpFinalSpan;
309005b261ecSmrg
309105b261ecSmrg# define allocFinalSpan()   (freeFinalSpans ?\
309205b261ecSmrg				((tmpFinalSpan = freeFinalSpans), \
309305b261ecSmrg				 (freeFinalSpans = freeFinalSpans->next), \
309405b261ecSmrg				 (tmpFinalSpan->next = 0), \
309505b261ecSmrg				 tmpFinalSpan) : \
309605b261ecSmrg			     realAllocSpan ())
309705b261ecSmrg
309805b261ecSmrg# define SPAN_CHUNK_SIZE    128
309905b261ecSmrg
310005b261ecSmrgstruct finalSpanChunk {
310105b261ecSmrg	struct finalSpan	data[SPAN_CHUNK_SIZE];
310205b261ecSmrg	struct finalSpanChunk	*next;
310305b261ecSmrg};
310405b261ecSmrg
310505b261ecSmrgstatic struct finalSpanChunk	*chunks;
310605b261ecSmrg
310705b261ecSmrgstatic struct finalSpan *
310805b261ecSmrgrealAllocSpan (void)
310905b261ecSmrg{
311005b261ecSmrg	struct finalSpanChunk	*newChunk;
311105b261ecSmrg	struct finalSpan	*span;
311205b261ecSmrg	int			i;
311305b261ecSmrg
311405b261ecSmrg	newChunk = (struct finalSpanChunk *) xalloc (sizeof (struct finalSpanChunk));
311505b261ecSmrg	if (!newChunk)
311605b261ecSmrg		return (struct finalSpan *) NULL;
311705b261ecSmrg	newChunk->next = chunks;
311805b261ecSmrg	chunks = newChunk;
311905b261ecSmrg	freeFinalSpans = span = newChunk->data + 1;
312005b261ecSmrg	for (i = 1; i < SPAN_CHUNK_SIZE-1; i++) {
312105b261ecSmrg		span->next = span+1;
312205b261ecSmrg		span++;
312305b261ecSmrg	}
312405b261ecSmrg	span->next = 0;
312505b261ecSmrg	span = newChunk->data;
312605b261ecSmrg	span->next = 0;
312705b261ecSmrg	return span;
312805b261ecSmrg}
312905b261ecSmrg
313005b261ecSmrgstatic void
313105b261ecSmrgdisposeFinalSpans (void)
313205b261ecSmrg{
313305b261ecSmrg	struct finalSpanChunk	*chunk, *next;
313405b261ecSmrg
313505b261ecSmrg	for (chunk = chunks; chunk; chunk = next) {
313605b261ecSmrg		next = chunk->next;
313705b261ecSmrg		xfree (chunk);
313805b261ecSmrg	}
313905b261ecSmrg	chunks = 0;
314005b261ecSmrg	freeFinalSpans = 0;
314105b261ecSmrg	xfree(finalSpans);
314205b261ecSmrg	finalSpans = 0;
314305b261ecSmrg}
314405b261ecSmrg
314505b261ecSmrgstatic void
314605b261ecSmrgfillSpans (
314705b261ecSmrg    DrawablePtr	pDrawable,
314805b261ecSmrg    GCPtr	pGC)
314905b261ecSmrg{
315005b261ecSmrg	struct finalSpan	*span;
315105b261ecSmrg	DDXPointPtr		xSpan;
315205b261ecSmrg	int			*xWidth;
315305b261ecSmrg	int			i;
315405b261ecSmrg	struct finalSpan	**f;
315505b261ecSmrg	int			spany;
315605b261ecSmrg	DDXPointPtr		xSpans;
315705b261ecSmrg	int			*xWidths;
315805b261ecSmrg
315905b261ecSmrg	if (nspans == 0)
316005b261ecSmrg		return;
31614642e01fSmrg	xSpan = xSpans = (DDXPointPtr) xalloc (nspans * sizeof (DDXPointRec));
31624642e01fSmrg	xWidth = xWidths = (int *) xalloc (nspans * sizeof (int));
316305b261ecSmrg	if (xSpans && xWidths)
316405b261ecSmrg	{
316505b261ecSmrg	    i = 0;
316605b261ecSmrg	    f = finalSpans;
316705b261ecSmrg	    for (spany = finalMiny; spany <= finalMaxy; spany++, f++) {
316805b261ecSmrg		    for (span = *f; span; span=span->next) {
316905b261ecSmrg			    if (span->max <= span->min)
317005b261ecSmrg				    continue;
317105b261ecSmrg			    xSpan->x = span->min;
317205b261ecSmrg			    xSpan->y = spany;
317305b261ecSmrg			    ++xSpan;
317405b261ecSmrg			    *xWidth++ = span->max - span->min;
317505b261ecSmrg			    ++i;
317605b261ecSmrg		    }
317705b261ecSmrg	    }
317805b261ecSmrg	    (*pGC->ops->FillSpans) (pDrawable, pGC, i, xSpans, xWidths, TRUE);
317905b261ecSmrg	}
318005b261ecSmrg	disposeFinalSpans ();
318105b261ecSmrg	if (xSpans)
31824642e01fSmrg	    xfree (xSpans);
318305b261ecSmrg	if (xWidths)
31844642e01fSmrg	    xfree (xWidths);
318505b261ecSmrg	finalMiny = 0;
318605b261ecSmrg	finalMaxy = -1;
318705b261ecSmrg	finalSize = 0;
318805b261ecSmrg	nspans = 0;
318905b261ecSmrg}
319005b261ecSmrg
319105b261ecSmrg# define SPAN_REALLOC	100
319205b261ecSmrg
319305b261ecSmrg# define findSpan(y) ((finalMiny <= (y) && (y) <= finalMaxy) ? \
319405b261ecSmrg			  &finalSpans[(y) - finalMiny] : \
319505b261ecSmrg			  realFindSpan (y))
319605b261ecSmrg
319705b261ecSmrgstatic struct finalSpan **
319805b261ecSmrgrealFindSpan (int y)
319905b261ecSmrg{
320005b261ecSmrg	struct finalSpan	**newSpans;
320105b261ecSmrg	int			newSize, newMiny, newMaxy;
320205b261ecSmrg	int			change;
320305b261ecSmrg	int			i;
320405b261ecSmrg
320505b261ecSmrg	if (y < finalMiny || y > finalMaxy) {
320605b261ecSmrg		if (!finalSize) {
320705b261ecSmrg			finalMiny = y;
320805b261ecSmrg			finalMaxy = y - 1;
320905b261ecSmrg		}
321005b261ecSmrg		if (y < finalMiny)
321105b261ecSmrg			change = finalMiny - y;
321205b261ecSmrg		else
321305b261ecSmrg			change = y - finalMaxy;
321405b261ecSmrg		if (change >= SPAN_REALLOC)
321505b261ecSmrg			change += SPAN_REALLOC;
321605b261ecSmrg		else
321705b261ecSmrg			change = SPAN_REALLOC;
321805b261ecSmrg		newSize = finalSize + change;
321905b261ecSmrg		newSpans = (struct finalSpan **) xalloc
322005b261ecSmrg 					(newSize * sizeof (struct finalSpan *));
322105b261ecSmrg		if (!newSpans)
322205b261ecSmrg		    return (struct finalSpan **)NULL;
322305b261ecSmrg		newMiny = finalMiny;
322405b261ecSmrg		newMaxy = finalMaxy;
322505b261ecSmrg		if (y < finalMiny)
322605b261ecSmrg			newMiny = finalMiny - change;
322705b261ecSmrg		else
322805b261ecSmrg			newMaxy = finalMaxy + change;
322905b261ecSmrg		if (finalSpans) {
323005b261ecSmrg			memmove(((char *) newSpans) + (finalMiny-newMiny) * sizeof (struct finalSpan *),
323105b261ecSmrg				(char *) finalSpans,
323205b261ecSmrg			       finalSize * sizeof (struct finalSpan *));
323305b261ecSmrg			xfree (finalSpans);
323405b261ecSmrg		}
323505b261ecSmrg		if ((i = finalMiny - newMiny) > 0)
323605b261ecSmrg			bzero ((char *)newSpans, i * sizeof (struct finalSpan *));
323705b261ecSmrg		if ((i = newMaxy - finalMaxy) > 0)
323805b261ecSmrg			bzero ((char *)(newSpans + newSize - i),
323905b261ecSmrg			       i * sizeof (struct finalSpan *));
324005b261ecSmrg		finalSpans = newSpans;
324105b261ecSmrg		finalMaxy = newMaxy;
324205b261ecSmrg		finalMiny = newMiny;
324305b261ecSmrg		finalSize = newSize;
324405b261ecSmrg	}
324505b261ecSmrg	return &finalSpans[y - finalMiny];
324605b261ecSmrg}
324705b261ecSmrg
324805b261ecSmrgstatic void
324905b261ecSmrgnewFinalSpan (
325005b261ecSmrg    int		y,
325105b261ecSmrg    int	xmin,
325205b261ecSmrg    int	xmax)
325305b261ecSmrg{
325405b261ecSmrg	struct finalSpan	*x;
325505b261ecSmrg	struct finalSpan	**f;
325605b261ecSmrg	struct finalSpan	*oldx;
325705b261ecSmrg	struct finalSpan	*prev;
325805b261ecSmrg
325905b261ecSmrg	f = findSpan (y);
326005b261ecSmrg	if (!f)
326105b261ecSmrg	    return;
326205b261ecSmrg	oldx = 0;
326305b261ecSmrg	for (;;) {
326405b261ecSmrg		prev = 0;
326505b261ecSmrg		for (x = *f; x; x=x->next) {
326605b261ecSmrg			if (x == oldx) {
326705b261ecSmrg				prev = x;
326805b261ecSmrg				continue;
326905b261ecSmrg			}
327005b261ecSmrg			if (x->min <= xmax && xmin <= x->max) {
327105b261ecSmrg				if (oldx) {
327205b261ecSmrg					oldx->min = min (x->min, xmin);
327305b261ecSmrg					oldx->max = max (x->max, xmax);
327405b261ecSmrg					if (prev)
327505b261ecSmrg						prev->next = x->next;
327605b261ecSmrg					else
327705b261ecSmrg						*f = x->next;
327805b261ecSmrg					--nspans;
327905b261ecSmrg				} else {
328005b261ecSmrg					x->min = min (x->min, xmin);
328105b261ecSmrg					x->max = max (x->max, xmax);
328205b261ecSmrg					oldx = x;
328305b261ecSmrg				}
328405b261ecSmrg				xmin = oldx->min;
328505b261ecSmrg				xmax = oldx->max;
328605b261ecSmrg				break;
328705b261ecSmrg			}
328805b261ecSmrg			prev = x;
328905b261ecSmrg		}
329005b261ecSmrg		if (!x)
329105b261ecSmrg			break;
329205b261ecSmrg	}
329305b261ecSmrg	if (!oldx) {
329405b261ecSmrg		x = allocFinalSpan ();
329505b261ecSmrg		if (x)
329605b261ecSmrg		{
329705b261ecSmrg		    x->min = xmin;
329805b261ecSmrg		    x->max = xmax;
329905b261ecSmrg		    x->next = *f;
330005b261ecSmrg		    *f = x;
330105b261ecSmrg		    ++nspans;
330205b261ecSmrg		}
330305b261ecSmrg	}
330405b261ecSmrg}
330505b261ecSmrg
330605b261ecSmrgstatic void
330705b261ecSmrgmirrorSppPoint (
330805b261ecSmrg	int		quadrant,
330905b261ecSmrg	SppPointPtr	sppPoint)
331005b261ecSmrg{
331105b261ecSmrg	switch (quadrant) {
331205b261ecSmrg	case 0:
331305b261ecSmrg		break;
331405b261ecSmrg	case 1:
331505b261ecSmrg		sppPoint->x = -sppPoint->x;
331605b261ecSmrg		break;
331705b261ecSmrg	case 2:
331805b261ecSmrg		sppPoint->x = -sppPoint->x;
331905b261ecSmrg		sppPoint->y = -sppPoint->y;
332005b261ecSmrg		break;
332105b261ecSmrg	case 3:
332205b261ecSmrg		sppPoint->y = -sppPoint->y;
332305b261ecSmrg		break;
332405b261ecSmrg	}
332505b261ecSmrg	/*
332605b261ecSmrg	 * and translate to X coordinate system
332705b261ecSmrg	 */
332805b261ecSmrg	sppPoint->y = -sppPoint->y;
332905b261ecSmrg}
333005b261ecSmrg
333105b261ecSmrg/*
333205b261ecSmrg * split an arc into pieces which are scan-converted
333305b261ecSmrg * in the first-quadrant and mirrored into position.
333405b261ecSmrg * This is necessary as the scan-conversion code can
333505b261ecSmrg * only deal with arcs completely contained in the
333605b261ecSmrg * first quadrant.
333705b261ecSmrg */
333805b261ecSmrg
333905b261ecSmrgstatic void
334005b261ecSmrgdrawArc (
334105b261ecSmrg	xArc *tarc,
334205b261ecSmrg	int	l,
334305b261ecSmrg	int	a0,
334405b261ecSmrg	int	a1,
334505b261ecSmrg	miArcFacePtr	right,
334605b261ecSmrg	miArcFacePtr	left)	/* save end line points */
334705b261ecSmrg{
334805b261ecSmrg	struct arc_def		def;
334905b261ecSmrg	struct accelerators	acc;
335005b261ecSmrg	int			startq, endq, curq;
335105b261ecSmrg	int			rightq, leftq = 0, righta = 0, lefta = 0;
335205b261ecSmrg	miArcFacePtr		passRight, passLeft;
335305b261ecSmrg	int			q0 = 0, q1 = 0, mask;
335405b261ecSmrg	struct band {
335505b261ecSmrg		int	a0, a1;
335605b261ecSmrg		int	mask;
335705b261ecSmrg	}	band[5], sweep[20];
335805b261ecSmrg	int			bandno, sweepno;
335905b261ecSmrg	int			i, j;
336005b261ecSmrg	int			flipRight = 0, flipLeft = 0;
336105b261ecSmrg	int			copyEnd = 0;
336205b261ecSmrg	miArcSpanData		*spdata;
336305b261ecSmrg	Bool			mustFree;
336405b261ecSmrg
336505b261ecSmrg	spdata = miComputeWideEllipse(l, tarc, &mustFree);
336605b261ecSmrg	if (!spdata)
336705b261ecSmrg	    return;
336805b261ecSmrg
336905b261ecSmrg	if (a1 < a0)
337005b261ecSmrg		a1 += 360 * 64;
337105b261ecSmrg	startq = a0 / (90 * 64);
337205b261ecSmrg	if (a0 == a1)
337305b261ecSmrg	    endq = startq;
337405b261ecSmrg	else
337505b261ecSmrg	    endq = (a1-1) / (90 * 64);
337605b261ecSmrg	bandno = 0;
337705b261ecSmrg	curq = startq;
337805b261ecSmrg	rightq = -1;
337905b261ecSmrg	for (;;) {
338005b261ecSmrg		switch (curq) {
338105b261ecSmrg		case 0:
338205b261ecSmrg			if (a0 > 90 * 64)
338305b261ecSmrg				q0 = 0;
338405b261ecSmrg			else
338505b261ecSmrg				q0 = a0;
338605b261ecSmrg			if (a1 < 360 * 64)
338705b261ecSmrg				q1 = min (a1, 90 * 64);
338805b261ecSmrg			else
338905b261ecSmrg				q1 = 90 * 64;
339005b261ecSmrg			if (curq == startq && a0 == q0 && rightq < 0) {
339105b261ecSmrg				righta = q0;
339205b261ecSmrg				rightq = curq;
339305b261ecSmrg			}
339405b261ecSmrg			if (curq == endq && a1 == q1) {
339505b261ecSmrg				lefta = q1;
339605b261ecSmrg				leftq = curq;
339705b261ecSmrg			}
339805b261ecSmrg			break;
339905b261ecSmrg		case 1:
340005b261ecSmrg			if (a1 < 90 * 64)
340105b261ecSmrg				q0 = 0;
340205b261ecSmrg			else
340305b261ecSmrg				q0 = 180 * 64 - min (a1, 180 * 64);
340405b261ecSmrg			if (a0 > 180 * 64)
340505b261ecSmrg				q1 = 90 * 64;
340605b261ecSmrg			else
340705b261ecSmrg				q1 = 180 * 64 - max (a0, 90 * 64);
340805b261ecSmrg			if (curq == startq && 180 * 64 - a0 == q1) {
340905b261ecSmrg				righta = q1;
341005b261ecSmrg				rightq = curq;
341105b261ecSmrg			}
341205b261ecSmrg			if (curq == endq && 180 * 64 - a1 == q0) {
341305b261ecSmrg				lefta = q0;
341405b261ecSmrg				leftq = curq;
341505b261ecSmrg			}
341605b261ecSmrg			break;
341705b261ecSmrg		case 2:
341805b261ecSmrg			if (a0 > 270 * 64)
341905b261ecSmrg				q0 = 0;
342005b261ecSmrg			else
342105b261ecSmrg				q0 = max (a0, 180 * 64) - 180 * 64;
342205b261ecSmrg			if (a1 < 180 * 64)
342305b261ecSmrg				q1 = 90 * 64;
342405b261ecSmrg			else
342505b261ecSmrg				q1 = min (a1, 270 * 64) - 180 * 64;
342605b261ecSmrg			if (curq == startq && a0 - 180*64 == q0) {
342705b261ecSmrg				righta = q0;
342805b261ecSmrg				rightq = curq;
342905b261ecSmrg			}
343005b261ecSmrg			if (curq == endq && a1 - 180 * 64 == q1) {
343105b261ecSmrg				lefta = q1;
343205b261ecSmrg				leftq = curq;
343305b261ecSmrg			}
343405b261ecSmrg			break;
343505b261ecSmrg		case 3:
343605b261ecSmrg			if (a1 < 270 * 64)
343705b261ecSmrg				q0 = 0;
343805b261ecSmrg			else
343905b261ecSmrg				q0 = 360 * 64 - min (a1, 360 * 64);
344005b261ecSmrg			q1 = 360 * 64 - max (a0, 270 * 64);
344105b261ecSmrg			if (curq == startq && 360 * 64 - a0 == q1) {
344205b261ecSmrg				righta = q1;
344305b261ecSmrg				rightq = curq;
344405b261ecSmrg			}
344505b261ecSmrg			if (curq == endq && 360 * 64 - a1 == q0) {
344605b261ecSmrg				lefta = q0;
344705b261ecSmrg				leftq = curq;
344805b261ecSmrg			}
344905b261ecSmrg			break;
345005b261ecSmrg		}
345105b261ecSmrg		band[bandno].a0 = q0;
345205b261ecSmrg		band[bandno].a1 = q1;
345305b261ecSmrg		band[bandno].mask = 1 << curq;
345405b261ecSmrg		bandno++;
345505b261ecSmrg		if (curq == endq)
345605b261ecSmrg			break;
345705b261ecSmrg		curq++;
345805b261ecSmrg		if (curq == 4) {
345905b261ecSmrg			a0 = 0;
346005b261ecSmrg			a1 -= 360 * 64;
346105b261ecSmrg			curq = 0;
346205b261ecSmrg			endq -= 4;
346305b261ecSmrg		}
346405b261ecSmrg	}
346505b261ecSmrg	sweepno = 0;
346605b261ecSmrg	for (;;) {
346705b261ecSmrg		q0 = 90 * 64;
346805b261ecSmrg		mask = 0;
346905b261ecSmrg		/*
347005b261ecSmrg		 * find left-most point
347105b261ecSmrg		 */
347205b261ecSmrg		for (i = 0; i < bandno; i++)
347305b261ecSmrg			if (band[i].a0 <= q0) {
347405b261ecSmrg				q0 = band[i].a0;
347505b261ecSmrg				q1 = band[i].a1;
347605b261ecSmrg				mask = band[i].mask;
347705b261ecSmrg			}
347805b261ecSmrg		if (!mask)
347905b261ecSmrg			break;
348005b261ecSmrg		/*
348105b261ecSmrg		 * locate next point of change
348205b261ecSmrg		 */
348305b261ecSmrg		for (i = 0; i < bandno; i++)
348405b261ecSmrg			if (!(mask & band[i].mask)) {
348505b261ecSmrg				if (band[i].a0 == q0) {
348605b261ecSmrg					if (band[i].a1 < q1)
348705b261ecSmrg						q1 = band[i].a1;
348805b261ecSmrg					mask |= band[i].mask;
348905b261ecSmrg 				} else if (band[i].a0 < q1)
349005b261ecSmrg					q1 = band[i].a0;
349105b261ecSmrg			}
349205b261ecSmrg		/*
349305b261ecSmrg		 * create a new sweep
349405b261ecSmrg		 */
349505b261ecSmrg		sweep[sweepno].a0 = q0;
349605b261ecSmrg		sweep[sweepno].a1 = q1;
349705b261ecSmrg		sweep[sweepno].mask = mask;
349805b261ecSmrg		sweepno++;
349905b261ecSmrg		/*
350005b261ecSmrg		 * subtract the sweep from the affected bands
350105b261ecSmrg		 */
350205b261ecSmrg		for (i = 0; i < bandno; i++)
350305b261ecSmrg			if (band[i].a0 == q0) {
350405b261ecSmrg				band[i].a0 = q1;
350505b261ecSmrg				/*
350605b261ecSmrg				 * check if this band is empty
350705b261ecSmrg				 */
350805b261ecSmrg				if (band[i].a0 == band[i].a1)
350905b261ecSmrg					band[i].a1 = band[i].a0 = 90 * 64 + 1;
351005b261ecSmrg			}
351105b261ecSmrg	}
351205b261ecSmrg	computeAcc (tarc, l, &def, &acc);
351305b261ecSmrg	for (j = 0; j < sweepno; j++) {
351405b261ecSmrg		mask = sweep[j].mask;
351505b261ecSmrg		passRight = passLeft = 0;
351605b261ecSmrg 		if (mask & (1 << rightq)) {
351705b261ecSmrg			if (sweep[j].a0 == righta)
351805b261ecSmrg				passRight = right;
351905b261ecSmrg			else if (sweep[j].a1 == righta) {
352005b261ecSmrg				passLeft = right;
352105b261ecSmrg				flipRight = 1;
352205b261ecSmrg			}
352305b261ecSmrg		}
352405b261ecSmrg		if (mask & (1 << leftq)) {
352505b261ecSmrg			if (sweep[j].a1 == lefta)
352605b261ecSmrg			{
352705b261ecSmrg				if (passLeft)
352805b261ecSmrg					copyEnd = 1;
352905b261ecSmrg				passLeft = left;
353005b261ecSmrg			}
353105b261ecSmrg			else if (sweep[j].a0 == lefta) {
353205b261ecSmrg				if (passRight)
353305b261ecSmrg					copyEnd = 1;
353405b261ecSmrg				passRight = left;
353505b261ecSmrg				flipLeft = 1;
353605b261ecSmrg			}
353705b261ecSmrg		}
353805b261ecSmrg		drawQuadrant (&def, &acc, sweep[j].a0, sweep[j].a1, mask,
353905b261ecSmrg 			      passRight, passLeft, spdata);
354005b261ecSmrg	}
354105b261ecSmrg	/*
354205b261ecSmrg	 * when copyEnd is set, both ends of the arc were computed
354305b261ecSmrg	 * at the same time; drawQuadrant only takes one end though,
354405b261ecSmrg	 * so the left end will be the only one holding the data.  Copy
354505b261ecSmrg	 * it from there.
354605b261ecSmrg	 */
354705b261ecSmrg	if (copyEnd)
354805b261ecSmrg		*right = *left;
354905b261ecSmrg	/*
355005b261ecSmrg	 * mirror the coordinates generated for the
355105b261ecSmrg	 * faces of the arc
355205b261ecSmrg	 */
355305b261ecSmrg	if (right) {
355405b261ecSmrg		mirrorSppPoint (rightq, &right->clock);
355505b261ecSmrg		mirrorSppPoint (rightq, &right->center);
355605b261ecSmrg		mirrorSppPoint (rightq, &right->counterClock);
355705b261ecSmrg		if (flipRight) {
355805b261ecSmrg			SppPointRec	temp;
355905b261ecSmrg
356005b261ecSmrg			temp = right->clock;
356105b261ecSmrg			right->clock = right->counterClock;
356205b261ecSmrg			right->counterClock = temp;
356305b261ecSmrg		}
356405b261ecSmrg	}
356505b261ecSmrg	if (left) {
356605b261ecSmrg		mirrorSppPoint (leftq,  &left->counterClock);
356705b261ecSmrg		mirrorSppPoint (leftq,  &left->center);
356805b261ecSmrg		mirrorSppPoint (leftq,  &left->clock);
356905b261ecSmrg		if (flipLeft) {
357005b261ecSmrg			SppPointRec	temp;
357105b261ecSmrg
357205b261ecSmrg			temp = left->clock;
357305b261ecSmrg			left->clock = left->counterClock;
357405b261ecSmrg			left->counterClock = temp;
357505b261ecSmrg		}
357605b261ecSmrg	}
357705b261ecSmrg	if (mustFree)
357805b261ecSmrg	    xfree(spdata);
357905b261ecSmrg}
358005b261ecSmrg
358105b261ecSmrgstatic void
358205b261ecSmrgdrawQuadrant (
358305b261ecSmrg	struct arc_def		*def,
358405b261ecSmrg	struct accelerators	*acc,
358505b261ecSmrg	int			a0,
358605b261ecSmrg	int			a1,
358705b261ecSmrg	int			mask,
358805b261ecSmrg	miArcFacePtr		right,
358905b261ecSmrg	miArcFacePtr		left,
359005b261ecSmrg	miArcSpanData		*spdata)
359105b261ecSmrg{
359205b261ecSmrg	struct arc_bound	bound;
359305b261ecSmrg	double			yy, x, xalt;
359405b261ecSmrg	int			y, miny, maxy;
359505b261ecSmrg	int			n;
359605b261ecSmrg	miArcSpan		*span;
359705b261ecSmrg
359805b261ecSmrg	def->a0 = ((double) a0) / 64.0;
359905b261ecSmrg	def->a1 = ((double) a1) / 64.0;
360005b261ecSmrg	computeBound (def, &bound, acc, right, left);
360105b261ecSmrg	yy = bound.inner.min;
360205b261ecSmrg	if (bound.outer.min < yy)
360305b261ecSmrg	    yy = bound.outer.min;
360405b261ecSmrg	miny = ICEIL(yy - acc->fromIntY);
360505b261ecSmrg	yy = bound.inner.max;
360605b261ecSmrg	if (bound.outer.max > yy)
360705b261ecSmrg	    yy = bound.outer.max;
360805b261ecSmrg	maxy = floor(yy - acc->fromIntY);
360905b261ecSmrg	y = spdata->k;
361005b261ecSmrg	span = spdata->spans;
361105b261ecSmrg	if (spdata->top)
361205b261ecSmrg	{
361305b261ecSmrg	    if (a1 == 90 * 64 && (mask & 1))
361405b261ecSmrg		newFinalSpan (acc->yorgu - y - 1, acc->xorg, acc->xorg + 1);
361505b261ecSmrg	    span++;
361605b261ecSmrg	}
361705b261ecSmrg	for (n = spdata->count1; --n >= 0; )
361805b261ecSmrg	{
361905b261ecSmrg	    if (y < miny)
362005b261ecSmrg		return;
362105b261ecSmrg	    if (y <= maxy) {
362205b261ecSmrg		arcSpan (y,
362305b261ecSmrg			 span->lx, -span->lx, 0, span->lx + span->lw,
362405b261ecSmrg			 def, &bound, acc, mask);
362505b261ecSmrg		if (span->rw + span->rx)
362605b261ecSmrg		    tailSpan (y, -span->rw, -span->rx, def, &bound, acc, mask);
362705b261ecSmrg	    }
362805b261ecSmrg	    y--;
362905b261ecSmrg	    span++;
363005b261ecSmrg	}
363105b261ecSmrg	if (y < miny)
363205b261ecSmrg	    return;
363305b261ecSmrg	if (spdata->hole)
363405b261ecSmrg	{
363505b261ecSmrg	    if (y <= maxy)
363605b261ecSmrg		arcSpan (y, 0, 0, 0, 1, def, &bound, acc, mask & 0xc);
363705b261ecSmrg	}
363805b261ecSmrg	for (n = spdata->count2; --n >= 0; )
363905b261ecSmrg	{
364005b261ecSmrg	    if (y < miny)
364105b261ecSmrg		return;
364205b261ecSmrg	    if (y <= maxy)
364305b261ecSmrg		arcSpan (y, span->lx, span->lw, span->rx, span->rw,
364405b261ecSmrg			 def, &bound, acc, mask);
364505b261ecSmrg	    y--;
364605b261ecSmrg	    span++;
364705b261ecSmrg	}
364805b261ecSmrg	if (spdata->bot && miny <= y && y <= maxy)
364905b261ecSmrg	{
365005b261ecSmrg	    n = mask;
365105b261ecSmrg	    if (y == miny)
365205b261ecSmrg		n &= 0xc;
365305b261ecSmrg	    if (span->rw <= 0) {
365405b261ecSmrg		arcSpan0 (span->lx, -span->lx, 0, span->lx + span->lw,
365505b261ecSmrg			  def, &bound, acc, n);
365605b261ecSmrg		if (span->rw + span->rx)
365705b261ecSmrg		    tailSpan (y, -span->rw, -span->rx, def, &bound, acc, n);
365805b261ecSmrg	    }
365905b261ecSmrg	    else
366005b261ecSmrg		arcSpan0 (span->lx, span->lw, span->rx, span->rw,
366105b261ecSmrg			  def, &bound, acc, n);
366205b261ecSmrg	    y--;
366305b261ecSmrg	}
366405b261ecSmrg	while (y >= miny) {
366505b261ecSmrg	    yy = y + acc->fromIntY;
366605b261ecSmrg	    if (def->w == def->h) {
366705b261ecSmrg		xalt = def->w - def->l;
366805b261ecSmrg		x = -sqrt(xalt * xalt - yy * yy);
366905b261ecSmrg	    } else {
367005b261ecSmrg		x = tailX(yy, def, &bound, acc);
367105b261ecSmrg		if (acc->left.valid && boundedLe (yy, bound.left)) {
367205b261ecSmrg		    xalt = intersectLine (yy, acc->left);
367305b261ecSmrg		    if (xalt < x)
367405b261ecSmrg			x = xalt;
367505b261ecSmrg		}
367605b261ecSmrg		if (acc->right.valid && boundedLe (yy, bound.right)) {
367705b261ecSmrg		    xalt = intersectLine (yy, acc->right);
367805b261ecSmrg		    if (xalt < x)
367905b261ecSmrg			x = xalt;
368005b261ecSmrg		}
368105b261ecSmrg	    }
368205b261ecSmrg	    arcSpan (y,
368305b261ecSmrg		     ICEIL(acc->fromIntX - x), 0,
368405b261ecSmrg		     ICEIL(acc->fromIntX + x), 0,
368505b261ecSmrg		     def, &bound, acc, mask);
368605b261ecSmrg	    y--;
368705b261ecSmrg	}
368805b261ecSmrg}
3689